Prechádzať zdrojové kódy

Merge pull request #1 from yangjie11/main

[add] move from rt-thread to pkgs
guo 5 rokov pred
rodič
commit
01f3417035
82 zmenil súbory, kde vykonal 22238 pridanie a 2 odobranie
  1. 1 0
      AUTHORS
  2. 27 0
      CMakeLists.txt
  3. 339 0
      COPYING
  4. 275 0
      Doxyfile
  5. 277 2
      README.md
  6. 38 0
      SConscript
  7. 5 0
      TODO
  8. 660 0
      dfs_uffs.c
  9. 89 0
      dfs_uffs.h
  10. BIN
      doc/Understanding-UFFS.odp
  11. BIN
      doc/Understanding-UFFS.pdf
  12. BIN
      doc/uffs-serial-num-relationship.JPG
  13. 7 0
      src/CMakeLists.txt
  14. 24 0
      src/emu/CMakeLists.txt
  15. 643 0
      src/emu/cmdline.c
  16. 83 0
      src/emu/cmdline.h
  17. 694 0
      src/emu/helper_cmds.c
  18. 1186 0
      src/emu/test_cmds.c
  19. 113 0
      src/emu/uffs_fileem.c
  20. 88 0
      src/emu/uffs_fileem.h
  21. 236 0
      src/emu/uffs_fileem_ecc_hw.c
  22. 356 0
      src/emu/uffs_fileem_ecc_hw_auto.c
  23. 212 0
      src/emu/uffs_fileem_ecc_soft.c
  24. 232 0
      src/emu/uffs_fileem_share.c
  25. 311 0
      src/emu/uffs_fileem_wrap.c
  26. 22 0
      src/example/CMakeLists.txt
  27. 434 0
      src/example/flash-interface-example.c
  28. 163 0
      src/example/static-mem-allocate.c
  29. 101 0
      src/inc/uffs/uffs.h
  30. 75 0
      src/inc/uffs/uffs_badblock.h
  31. 107 0
      src/inc/uffs/uffs_blockinfo.h
  32. 178 0
      src/inc/uffs/uffs_buf.h
  33. 59 0
      src/inc/uffs/uffs_core.h
  34. 46 0
      src/inc/uffs/uffs_crc.h
  35. 211 0
      src/inc/uffs/uffs_device.h
  36. 89 0
      src/inc/uffs/uffs_ecc.h
  37. 151 0
      src/inc/uffs/uffs_fd.h
  38. 76 0
      src/inc/uffs/uffs_find.h
  39. 311 0
      src/inc/uffs/uffs_flash.h
  40. 138 0
      src/inc/uffs/uffs_fs.h
  41. 83 0
      src/inc/uffs/uffs_mem.h
  42. 96 0
      src/inc/uffs/uffs_mtb.h
  43. 75 0
      src/inc/uffs/uffs_os.h
  44. 94 0
      src/inc/uffs/uffs_pool.h
  45. 336 0
      src/inc/uffs/uffs_public.h
  46. 237 0
      src/inc/uffs/uffs_tree.h
  47. 160 0
      src/inc/uffs/uffs_types.h
  48. 64 0
      src/inc/uffs/uffs_utils.h
  49. 55 0
      src/inc/uffs/uffs_version.h
  50. 13 0
      src/platform/CMakeLists.txt
  51. 322 0
      src/platform/posix/uffs_config.h
  52. 148 0
      src/platform/posix/uffs_os.c
  53. 317 0
      src/platform/win32/uffs_config.h
  54. 148 0
      src/platform/win32/uffs_os.c
  55. 59 0
      src/uffs/CMakeLists.txt
  56. 249 0
      src/uffs/uffs_badblock.c
  57. 413 0
      src/uffs/uffs_blockinfo.c
  58. 1799 0
      src/uffs/uffs_buf.c
  59. 93 0
      src/uffs/uffs_crc.c
  60. 188 0
      src/uffs/uffs_debug.c
  61. 93 0
      src/uffs/uffs_device.c
  62. 384 0
      src/uffs/uffs_ecc.c
  63. 746 0
      src/uffs/uffs_fd.c
  64. 378 0
      src/uffs/uffs_find.c
  65. 1034 0
      src/uffs/uffs_flash.c
  66. 1944 0
      src/uffs/uffs_fs.c
  67. 204 0
      src/uffs/uffs_init.c
  68. 92 0
      src/uffs/uffs_mem.c
  69. 366 0
      src/uffs/uffs_mtb.c
  70. 386 0
      src/uffs/uffs_pool.c
  71. 448 0
      src/uffs/uffs_public.c
  72. 1278 0
      src/uffs/uffs_tree.c
  73. 402 0
      src/uffs/uffs_utils.c
  74. 73 0
      src/uffs/uffs_version.c
  75. 20 0
      src/utils/CMakeLists.txt
  76. 493 0
      src/utils/mkuffs.c
  77. 25 0
      tools/chomp_uffs_perror.rb
  78. 24 0
      tools/format_code.rb
  79. 3 0
      tools/make_package.sh
  80. 322 0
      uffs_config.h
  81. 359 0
      uffs_nandif.c
  82. 158 0
      uffs_rtthread.c

+ 1 - 0
AUTHORS

@@ -0,0 +1 @@
+Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>

+ 27 - 0
CMakeLists.txt

@@ -0,0 +1,27 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6 )
+PROJECT( uffs )
+
+SET(CMAKE_CXX_FLAGS_VGL "-O0 -g")
+SET(CMAKE_C_FLAGS_VGL "-O0 -g")
+
+SET(CMAKE_C_FLAGS_GCOV "-g -O0 -Wall -fprofile-arcs -ftest-coverage")
+SET(CMAKE_CXX_FLAGS_GCOV "-g -O0 -Wall -fprofile-arcs -ftest-coverage")
+SET(CMAKE_EXE_LINKER_FLAGS_GCOV "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov")
+
+IF (CMAKE_COMPILER_IS_GNUCC)
+IF (APPLE)
+        SET(CMAKE_CXX_FLAGS "-fPIC -Wall -Werror -g -rdynamic")
+        SET(CMAKE_C_FLAGS "-fPIC -Wall -Werror -g -rdynamic")
+ELSE ()
+	SET(CMAKE_CXX_FLAGS "-fPIC -Wall -Werror -Wclobbered -Wempty-body -Wignored-qualifiers -Wmissing-parameter-type -Woverride-init -Wtype-limits -Wuninitialized -g -rdynamic")
+	SET(CMAKE_C_FLAGS "-fPIC -Wall -Werror -Wclobbered -Wempty-body -Wignored-qualifiers -Wmissing-parameter-type -Woverride-init -Wtype-limits -Wuninitialized -g -rdynamic")
+ENDIF()
+ENDIF()
+
+IF (UNIX)
+	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNIX")
+	SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUNIX")
+ENDIF()
+
+ADD_SUBDIRECTORY( src )
+

+ 339 - 0
COPYING

@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 275 - 0
Doxyfile

@@ -0,0 +1,275 @@
+# Doxyfile 1.4.1-KDevelop
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = uffs-doc
+PROJECT_NUMBER         = 0.1
+OUTPUT_DIRECTORY       = doc/doxygen-doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = ./
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+DISTRIBUTE_GROUP_DOC   = NO
+TAB_SIZE               = 4
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = NO
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = YES
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = YES
+CASE_SENSE_NAMES       = NO
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = YES
+SORT_BY_SCOPE_NAME     = YES
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = ./src
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.C \
+                         *.CC \
+                         *.C++ \
+                         *.II \
+                         *.I++ \
+                         *.H \
+                         *.HH \
+                         *.H++ \
+                         *.CS \
+                         *.PHP \
+                         *.PHP3 \
+                         *.M \
+                         *.MM \
+                         *.C \
+                         *.H \
+                         *.tlh \
+                         *.diff \
+                         *.patch \
+                         *.moc \
+                         *.xpm \
+                         *.dox
+RECURSIVE              = YES
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = YES
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = uffs.tag
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO

+ 277 - 2
README.md

@@ -1,2 +1,277 @@
-# uffs
-UFFS is a file system that uses NAND flash in a small memory environment
+# UFFS
+
+UFFS: Ultra-low-cost Flash File System
+
+Project: http://uffs.sf.net/
+Blog:    http://all-about-uffs.blogspot.com/
+Q/A:     http://groups.google.com/group/uffs/
+
+Author: Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+INTRODUCTION
+------------
+
+UFFS is a nand flash file system designed for embedded system.
+
+UFFS have some unique and advanced features:
+  * Low cost: e.g. it needs only 41K bytes RAM for 64MB NAND flash (page size 2048).
+    
+  * Fast booting: it reads only a few spares from each block, typically
+    mounting a fully filled file system (Gbits) within one second.
+    
+  * Superb Reliability: 
+    - The file system is designed for the embedded system which may 
+        frequently lost power/reset without care.
+    - Journal file system, the file system will automatically rollback
+        to the last state when lost power on the middle of flash programing.
+    - When 'write' return without error, the data is guarenteed been
+        saved on flash.
+
+  * Fast file create/read/write/seek.  
+  * Bad-block tolerant, ECC enable and good ware-leveling.
+  * There is no garbage collection needed for UFFS.
+  * Support multiple NAND flash class in one system.
+  * Support bare flash hardware, no operating system needed. 
+  * Support static memory allocation (works without 'malloc').
+  * Fully simulated on PC (Windows/Linux) platform.
+
+Disadvantage:
+  * space inefficency for small files: UFFS use at least one
+   'block'(the minial erase unit for NAND flash, e.g. 16K ) for a file.
+  * maximum supported blocks: 2^16 = 65535
+
+Memory consuming example:
+    For page size = 512:
+        [VARY]Tree nodes: 16 * total_blocks 
+        [CONST]Page Bufs: MAX_CACHED_BUFFERS(10) * (40 + pageSize(512)) = 5.4K 
+        [CONST]Block Info caches: (24 + 14 * pages_per_block (32)) * MAX_CACHED_BLOCK_INFO (10) = 4.6K
+
+        Example 1: 128M bytes NAND, 8192 blocks, total memory cost:
+            (16 * 8192)128K + 5.4K + 4.6K = 138K bytes.
+		
+        Example 2: 32M Bytes NAND, 2048 blocks, total memory cost:
+            (16 * 2048)32K + 5.4K + 4.6K = 42K bytes.
+		
+        Example 3: 16M bytes NAND, 1024 blocks, total memory cost:
+            (16 * 1024)16K + 5.4K + 4.6K = 26K bytes.
+
+    For page size = 2048:
+        [VARY]Tree nodes: 16 * total_blocks 
+        [CONST]Page Bufs: MAX_CACHED_BUFFERS(10) * (40 + pageSize(2048)) = 20.4K 
+        [CONST]Block Info caches: (24 + 14 * pages_per_block (32)) * MAX_CACHED_BLOCK_INFO (10) = 4.6K
+
+        Example 1: 512M bytes NAND, 8192 blocks, total memory cost:
+            (16 * 8192)128K + 20.4K + 4.6K = 153K bytes.
+		
+        Example 2: 128M Bytes NAND, 2048 blocks, total memory cost:
+            (16 * 2048)32K + 20.4K + 4.6K = 57K bytes.
+		
+        Example 3: 64M bytes NAND, 1024 blocks, total memory cost:
+            (16 * 1024)16K + 20.4K + 4.6K = 41K bytes.
+            
+
+BUILD SIMULATOR REQUIREMENT
+---------------------------
+From V1.2.0, build uffs simulator requires 'cmake'.
+'cmake' can be downloaded from: http://www.cmake.org/
+
+or, under Debian/Ubuntu:
+  sudo apt-get install cmake
+
+BUILD SIMULATOR ON LINUX
+------------------------
+1) create a 'build' dir:
+
+mkdir -p ~/build/uffs
+
+2) create Makefiles and build:
+  cd ~/build/uffs
+  cmake <path_to_uffs>
+  make
+
+5) run simulator (interactive mode):
+  src/utils/mkuffs
+ 
+ 
+BUILD SIMULATOR ON WINDOWS
+--------------------------
+
+1) create a 'build' dir along with uffs source dir,
+ d:\build\uffs
+
+2) Create VC project files:
+  cd build\uffs
+  cmake <path_to_uffs>
+
+3) Open uffs.dsw (or uffs.sln for VC > 6 ), compile & run.
+
+ 
+LATEST SOURCE CODE
+------------------
+You can get the latest source code from git repository:
+  git clone git://uffs.git.sourceforge.net/gitroot/uffs/uffs
+
+
+CURRENT STATUS
+--------------
+UFFS 0.1.x is a working version on PC simulator, also has been ported to 
+uBase embedded OS as a 'real world' product for thousands of copies,
+it works fine so far.
+
+UFFS 0.2.0 implementes full directory.
+
+UFFS 1.0.0 is the first stable release at sf.net.
+
+UFFS 1.1.0: support NAND flash with large page size (up to 2K).
+
+UFFS 1.1.1: bug fixes. a tool for making uffs disk image.
+
+UFFS 1.1.2: bug fixes. add more Flash Class. change Licence from GNU GPLv2 to GNU LGPLv2
+
+UFFS 1.2.0: 
+  - eliminate 'current path' and relatives. Now you should use absolute path in all
+    uffs APIs. For dir, the fullname should end with '/'.
+  - allow using static memory allocation, 'malloc' is no longer needed.
+  - using cmake for building simulator.
+  - bug fixes & minor changes.
+
+UFFS 1.2.1:
+  - improve bad block management
+  - bug fixes
+  - change Licence to modified GNU GPLv2.
+
+UFFS 1.3.0:
+  - improved flash interface
+  - support hardware ECC
+  - support user defined spare layout (for customized NAND flash controller)
+  - support 4K page size
+  - no partial page program required, support MLC NAND flash
+  - reduced buffer flushes by grouping buffers
+  - structual improvments and bug fixes
+  
+UFFS v1.3.1:
+  - Tidy up three memory allocators: static, native and system.
+  - Fix bugs in flash interface example.
+  - Fix memory allocation bugs when using static memory allocator.
+  - Add flash driver interface 'WriteFullPage()'.
+  - Fix compilation errors for BlackFin DSP compiler.
+
+UFFS v1.3.2:
+  - Add POSIX like file system APIs.
+  - Bug fixes.
+  
+UFFS v1.3.3:
+  - Change Flash Interface, simplify interface.
+  - Improved bad block handling.
+  - Better support for MLC NAND flash.
+  - Added hardware ECC and RS-ECC controller emulator.
+  - Bug fixes.
+
+UFFS v1.3.4
+  - New UO_NOECC option for skipping ECC (fast reading).
+  - POSIX compliance uffs_seek().
+  - Improved unclean page detection (add new 'seal' byte in spare area).
+  - Optional page data CRC.
+  - Bug fixes.
+  - Other improvements.
+  
+LICENCE
+-------
+  From v1.2.1, UFFS is released under a modified GNU GPLv2. (the same as eCos Licence)
+  The full licence text can be found in the header of source files:
+  
+	  UFFS is free software; you can redistribute it and/or modify it under
+	  the GNU Library General Public License as published by the Free Software 
+	  Foundation; either version 2 of the License, or (at your option) any
+	  later version.
+
+	  UFFS 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
+	  or GNU Library General Public License, as applicable, for more details.
+	 
+	  You should have received a copy of the GNU General Public License
+	  and GNU Library General Public License along with UFFS; if not, write
+	  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+	  Boston, MA  02110-1301, USA.
+
+	  As a special exception, if other files instantiate templates or use
+	  macros or inline functions from this file, or you compile this file
+	  and link it with other works to produce a work based on this file,
+	  this file does not by itself cause the resulting work to be covered
+	  by the GNU General Public License. However the source code for this
+	  file must still be made available in accordance with section (3) of
+	  the GNU General Public License v2.
+	 
+	  This exception does not invalidate any other reasons why a work based
+	  on this file might be covered by the GNU General Public License.
+
+
+TESTING UFFS WITH SQLITE3 REGRESSION TEST CASES
+-----------------------------------------------
+UFFS can be tested with sqlite3 regression test cases (on Linux).
+
+1) install tcl8.5-dev on host PC:
+	apt-get install tcl8.5 tcl8.5-dev
+
+    # make sure your Linux is using tcl8.5 as the default tclsh:
+		sudo update-alternatives --config tclsh
+		(select "tclsh8.5" from the list.)
+
+2) build uffs:
+	mkdir -p ~/build/uffs
+	cd ~/build/uffs
+	cmake <path_to_uffs>
+
+3) build sqlite3:
+	cd <path_to_uffs>/src/test/sqlite3/sqlite-src-3070900
+	./configure        					 # create some build support files
+	git checkout -f Makefile config.h    # restore modified Makefile and config.h
+	make test                            # start build 'testfixture' program.
+	
+	# now you'll see something like:
+	Connect: Connection refused
+	Assert (uffs_ret == ret && ret == bak_ret) fail at /home/.../src/test/api_test/os_uffs.c:os_unlink:329: unlink("/home/.../src/test/sqlite3/sqlite-src-3070900/./test.db-journal"), unix return 0, uffs return -1, bak return -1
+	make: *** [test] Error 1
+
+4) run test cases:
+	Open two terminals.
+	on termional A:
+		cd ~/build/uffs
+		src/utils/mkuffs -t 1024
+
+		#on uffs simulator command line, enter:
+		format /
+		apisrv
+
+	on terminal B:
+		cd <path_to_uffs>/src/test/sqlite3/sqlite-src-3070900
+		./test-uffs.sh
+
+	Note: if you want to run mkuffs on another PC, for example, a Windows PC, you need to specify the IP address in test-uffs.sh.
+	
+	The test will take 1~4 hours, depends on how fast your Linux box is.
+
+
+ACKNOWLEDGMENT
+---------------
+Special thanks for your contributions to:
+(list in no particular order)
+
+* Chen Jun <chj@nlscan.com>
+* Michail <digiolog@mail.ru>
+* Sjpu <sjpu@163.com>
+* RobertGray <xennex@hotmail.com>
+* Dongbo <dongbo@ftsafe.com>
+* Cag <seucag@hotmail.com>
+* Sergey <s_sazonov@m2m-t.ru>
+* Chris Conrad <chris.conrad@halliburton.com>
+* Vladimir <decoder@rambler.ru>
+* Thien Pham <thienpham2008@yahoo.com>
+* Emmanuel Blot <eblot.ml@gmail.com>
+* Michael <yowong2@gmail.com>
+* Mick D <mick-eng@sourceforge.net>
+* Paul <paulr227@gmail.com>
+* Rogerz <rogerz.zhang@gmail.com>
+
+

+ 38 - 0
SConscript

@@ -0,0 +1,38 @@
+# RT-Thread building script for component
+
+from building import *
+
+cwd = GetCurrentDir()
+
+src = Glob('*.c')
+
+uffs = Split('''
+src/uffs/uffs_badblock.c
+src/uffs/uffs_blockinfo.c
+src/uffs/uffs_buf.c
+src/uffs/uffs_debug.c
+src/uffs/uffs_device.c
+src/uffs/uffs_ecc.c
+src/uffs/uffs_crc.c
+src/uffs/uffs_fd.c
+src/uffs/uffs_find.c
+src/uffs/uffs_flash.c
+src/uffs/uffs_fs.c
+src/uffs/uffs_init.c
+src/uffs/uffs_mem.c
+src/uffs/uffs_mtb.c
+src/uffs/uffs_pool.c
+src/uffs/uffs_public.c
+src/uffs/uffs_tree.c
+src/uffs/uffs_utils.c
+src/uffs/uffs_version.c
+
+''')
+
+src = src + uffs
+ 
+CPPPATH = [cwd, cwd + '/src/inc']
+
+group = DefineGroup('uffs', src , depend = ['RT_USING_DFS', 'PKG_USING_DFS_UFFS'], CPPPATH = CPPPATH)
+
+Return('group')

+ 5 - 0
TODO

@@ -0,0 +1,5 @@
+TODO list for v1.3:
+
+* New API: int uffs_SkipObject(uffs_Object *obj, int size);
+* Introduce buffer group
+* Interface to Linux MTD

+ 660 - 0
dfs_uffs.c

@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2011-10-22     prife        the first version
+ * 2012-03-28     prife        use mtd device interface
+ * 2012-04-05     prife        update uffs with official repo and use uffs_UnMount/Mount
+ * 2017-04-12     lizhen9880   fix the f_bsize and f_blocks issue in function dfs_uffs_statfs
+ */
+
+#include <rtthread.h>
+
+#include <dfs_fs.h>
+#include <dfs_file.h>
+#include <rtdevice.h>
+
+#include "dfs_uffs.h"
+
+#include "uffs/uffs_fd.h" /* posix file api is here */
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_mem.h"
+#include "uffs/uffs_utils.h"
+
+/*
+ * RT-Thread DFS Interface for uffs
+ */
+#define UFFS_DEVICE_MAX         2    /* the max partions on a nand deivce*/
+#define UFFS_MOUNT_PATH_MAX     128  /* the mount point max length */
+#define FILE_PATH_MAX           256  /* the longest file path */
+
+struct _nand_dev
+{
+    struct rt_mtd_nand_device *dev;
+    struct uffs_StorageAttrSt storage;
+    uffs_Device uffs_dev;
+    uffs_MountTable mount_table;
+    char mount_path[UFFS_MOUNT_PATH_MAX];
+    void *data;   /* when uffs use static buf, it will save ptr here */
+};
+/* make sure the following struct var had been initilased to 0! */
+static struct _nand_dev nand_part[UFFS_DEVICE_MAX] = {0};
+
+static int uffs_result_to_dfs(int result)
+{
+    int status = -1;
+
+    result = result < 0 ? -result : result;
+    switch (result)
+    {
+    case UENOERR:/** no error */
+        break;
+    case UEACCES:/** Tried to open read-only file for writing, or files sharing mode
+                   does not allow specified operations, or given path is directory */
+        status = -EINVAL;
+        break;/* no suitable */
+    case UEEXIST:   /** _O_CREAT and _O_EXCL flags specified, but filename already exists */
+        status = -EEXIST;
+        break;
+    case UEINVAL:  /** Invalid oflag or pmode argument */
+        status = -EINVAL;
+        break;
+    case UEMFILE: /** No more file handles available(too many open files)  */
+        status = -1;
+        break;
+    case UENOENT: /** file or path not found */
+        status = -ENOENT;
+        break;
+    case UETIME: /** can't set file time */
+        status = -1;
+        break;
+    case UEBADF: /** invalid file handle */
+        status = -EBADF;
+        break;
+    case UENOMEM:/** no enough memory */
+        status = -ENOSPC;
+        break;
+    case UEIOERR: /** I/O error from lower level flash operation */
+        status = -EIO;
+        break;
+    case UENOTDIR: /** Not a directory */
+        status = -ENOTDIR;
+        break;
+    case UEISDIR: /** Is a directory */
+        status = -EISDIR;
+        break;
+    case UEUNKNOWN_ERR:
+    default:
+        status = -1;
+        break; /* unknown error! */
+    }
+
+    return status;
+}
+
+static URET _device_init(uffs_Device *dev)
+{
+    dev->attr->_private = NULL; // hook nand_chip data structure to attr->_private
+    dev->ops = (struct uffs_FlashOpsSt *)&nand_ops;
+
+    return U_SUCC;
+}
+
+static URET _device_release(uffs_Device *dev)
+{
+    return U_SUCC;
+}
+
+static int init_uffs_fs(
+    struct _nand_dev *nand_part)
+{
+    uffs_MountTable *mtb;
+    struct rt_mtd_nand_device *nand;
+    struct uffs_StorageAttrSt *flash_storage;
+
+    mtb = &nand_part->mount_table;
+    nand = nand_part->dev;
+    flash_storage = &nand_part->storage;
+
+    /* setup nand storage attributes */
+    uffs_setup_storage(flash_storage, nand);
+
+    /* register mount table */
+    if (mtb->dev)
+    {
+        /* set memory allocator for uffs */
+#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
+        uffs_MemSetupSystemAllocator(&mtb->dev->mem);
+#endif
+        /* setup device init/release entry */
+        mtb->dev->Init = _device_init;
+        mtb->dev->Release = _device_release;
+        mtb->dev->attr = flash_storage;
+
+        uffs_RegisterMountTable(mtb);
+    }
+    /* mount uffs partion on nand device */
+    return uffs_Mount(nand_part->mount_path) == U_SUCC ? 0 : -1;
+}
+
+static int dfs_uffs_mount(
+    struct dfs_filesystem *fs,
+    unsigned long rwflag,
+    const void *data)
+{
+    rt_base_t index;
+    uffs_MountTable *mount_part;
+    struct rt_mtd_nand_device *dev;
+
+    RT_ASSERT(rt_strlen(fs->path) < (UFFS_MOUNT_PATH_MAX - 1));
+    dev = RT_MTD_NAND_DEVICE(fs->dev_id);
+
+    /*1. find a empty entry in partition table */
+    for (index = 0; index < UFFS_DEVICE_MAX ; index ++)
+    {
+        if (nand_part[index].dev == RT_NULL)
+            break;
+    }
+    if (index == UFFS_DEVICE_MAX)
+        return -ENOENT;
+
+    /*2. fill partition structure */
+    nand_part[index].dev = dev;
+
+    /* make a right mount path for uffs, end with '/' */
+    rt_snprintf(nand_part[index].mount_path, UFFS_MOUNT_PATH_MAX, "%s/", fs->path);
+    if (nand_part[index].mount_path[1] == '/')
+        nand_part[index].mount_path[1] = 0;
+
+    mount_part = &(nand_part[index].mount_table);
+    mount_part->mount   = nand_part[index].mount_path;
+    mount_part->dev = &(nand_part[index].uffs_dev);
+    rt_memset(mount_part->dev, 0, sizeof(uffs_Device));//in order to make uffs happy.
+    mount_part->dev->_private = dev;   /* save dev_id into uffs */
+    mount_part->start_block = dev->block_start;
+    mount_part->end_block = dev->block_end;
+    /*3. mount uffs */
+    if (init_uffs_fs(&nand_part[index]) < 0)
+    {
+        return uffs_result_to_dfs(uffs_get_error());
+    }
+    return 0;
+}
+
+static int dfs_uffs_unmount(struct dfs_filesystem *fs)
+{
+    rt_base_t index;
+    int result;
+
+    /* find the device index and then unmount it */
+    for (index = 0; index < UFFS_DEVICE_MAX; index++)
+    {
+        if (nand_part[index].dev == RT_MTD_NAND_DEVICE(fs->dev_id))
+        {
+            nand_part[index].dev = RT_NULL;
+            result = uffs_UnMount(nand_part[index].mount_path);
+            if (result != U_SUCC)
+                break;
+
+            result = uffs_UnRegisterMountTable(& nand_part[index].mount_table);
+            return (result == U_SUCC) ? RT_EOK : -1;
+        }
+    }
+    return -ENOENT;
+}
+
+static int dfs_uffs_mkfs(rt_device_t dev_id)
+{
+    rt_base_t index;
+    rt_uint32_t block;
+    struct rt_mtd_nand_device *mtd;
+
+    /*1. find the device index */
+    for (index = 0; index < UFFS_DEVICE_MAX; index++)
+    {
+        if (nand_part[index].dev == (struct rt_mtd_nand_device *)dev_id)
+            break;
+    }
+
+    if (index == UFFS_DEVICE_MAX)
+    {
+        /* can't find device driver */
+        return -ENOENT;
+    }
+
+    /*2. then unmount the partition */
+    uffs_UnMount(nand_part[index].mount_path);
+    mtd = nand_part[index].dev;
+
+    /*3. erase all blocks on the partition */
+    block = mtd->block_start;
+    for (; block <= mtd->block_end; block++)
+    {
+        rt_mtd_nand_erase_block(mtd, block);
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+        if (rt_mtd_nand_check_block(mtd, block) != RT_EOK)
+        {
+            rt_kprintf("found bad block %d\n", block);
+            rt_mtd_nand_mark_badblock(mtd, block);
+        }
+#endif
+    }
+
+    /*4. remount it */
+    if (init_uffs_fs(&nand_part[index]) < 0)
+    {
+        return uffs_result_to_dfs(uffs_get_error());
+    }
+    return RT_EOK;
+}
+
+static int dfs_uffs_statfs(struct dfs_filesystem *fs,
+                           struct statfs *buf)
+{
+    rt_base_t index;
+    struct rt_mtd_nand_device *mtd = RT_MTD_NAND_DEVICE(fs->dev_id);
+
+    RT_ASSERT(mtd != RT_NULL);
+
+    /* find the device index */
+    for (index = 0; index < UFFS_DEVICE_MAX; index++)
+    {
+        if (nand_part[index].dev == (void *)mtd)
+            break;
+    }
+    if (index == UFFS_DEVICE_MAX)
+        return -ENOENT;
+
+    buf->f_bsize = mtd->page_size * mtd->pages_per_block;
+    buf->f_blocks = (mtd->block_end - mtd->block_start + 1);
+    buf->f_bfree = uffs_GetDeviceFree(&nand_part[index].uffs_dev) / buf->f_bsize ;
+
+    return 0;
+}
+
+static int dfs_uffs_open(struct dfs_fd *file)
+{
+    int fd;
+    int oflag, mode;
+    char *file_path;
+
+    oflag = file->flags;
+    if (oflag & O_DIRECTORY)   /* operations about dir */
+    {
+        uffs_DIR *dir;
+
+        if (oflag & O_CREAT)   /* create a dir*/
+        {
+            if (uffs_mkdir(file->path) < 0)
+                return uffs_result_to_dfs(uffs_get_error());
+        }
+        /* open dir */
+        file_path = rt_malloc(FILE_PATH_MAX);
+        if (file_path == RT_NULL)
+            return -ENOMEM;
+
+        if (file->path[0] == '/' && !(file->path[1] == 0))
+            rt_snprintf(file_path, FILE_PATH_MAX, "%s/", file->path);
+        else
+        {
+            file_path[0] = '/';
+            file_path[1] = 0;
+        }
+
+        dir = uffs_opendir(file_path);
+
+        if (dir == RT_NULL)
+        {
+            rt_free(file_path);
+            return uffs_result_to_dfs(uffs_get_error());
+        }
+        /* save this pointer,will used by  dfs_uffs_getdents*/
+        file->data = dir;
+        rt_free(file_path);
+        return RT_EOK;
+    }
+    /* regular file operations */
+    /* int uffs_open(const char *name, int oflag, ...); what is this?
+     * uffs_open can open dir!!  **/
+    mode = 0;
+    if (oflag & O_RDONLY) mode |= UO_RDONLY;
+    if (oflag & O_WRONLY) mode |= UO_WRONLY;
+    if (oflag & O_RDWR)   mode |= UO_RDWR;
+    /* Opens the file, if it is existing. If not, a new file is created. */
+    if (oflag & O_CREAT) mode |= UO_CREATE;
+    /* Creates a new file. If the file is existing, it is truncated and overwritten. */
+    if (oflag & O_TRUNC) mode |= UO_TRUNC;
+    /* Creates a new file. The function fails if the file is already existing. */
+    if (oflag & O_EXCL) mode |= UO_EXCL;
+
+    fd = uffs_open(file->path, mode);
+    if (fd < 0)
+    {
+        return uffs_result_to_dfs(uffs_get_error());
+    }
+
+    /* save this pointer, it will be used when calling read(), write(),
+     * flush(), seek(), and will be free when calling close()*/
+
+    file->data = (void *)fd;
+    file->pos  = uffs_seek(fd, 0, USEEK_CUR);
+    file->size = uffs_seek(fd, 0, USEEK_END);
+    uffs_seek(fd, file->pos, USEEK_SET);
+
+    if (oflag & O_APPEND)
+    {
+        file->pos = uffs_seek(fd, 0, USEEK_END);
+    }
+    return 0;
+}
+
+static int dfs_uffs_close(struct dfs_fd *file)
+{
+    int oflag;
+    int fd;
+
+    oflag = file->flags;
+    if (oflag & O_DIRECTORY)
+    {
+        /* operations about dir */
+        if (uffs_closedir((uffs_DIR *)(file->data)) < 0)
+            return uffs_result_to_dfs(uffs_get_error());
+
+        return 0;
+    }
+    /* regular file operations */
+    fd = (int)(file->data);
+
+    if (uffs_close(fd) == 0)
+        return 0;
+
+    return uffs_result_to_dfs(uffs_get_error());
+}
+
+static int dfs_uffs_ioctl(struct dfs_fd *file, int cmd, void *args)
+{
+    return -ENOSYS;
+}
+
+static int dfs_uffs_read(struct dfs_fd *file, void *buf, size_t len)
+{
+    int fd;
+    int char_read;
+
+    fd = (int)(file->data);
+    char_read = uffs_read(fd, buf, len);
+    if (char_read < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    /* update position */
+    file->pos = uffs_seek(fd, 0, USEEK_CUR);
+    return char_read;
+}
+
+static int dfs_uffs_write(struct dfs_fd *file,
+                          const void *buf,
+                          size_t len)
+{
+    int fd;
+    int char_write;
+
+    fd = (int)(file->data);
+
+    char_write = uffs_write(fd, buf, len);
+    if (char_write < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    /* update position */
+    file->pos = uffs_seek(fd, 0, USEEK_CUR);
+    return char_write;
+}
+
+static int dfs_uffs_flush(struct dfs_fd *file)
+{
+    int fd;
+    int result;
+
+    fd = (int)(file->data);
+
+    result = uffs_flush(fd);
+    if (result < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+    return 0;
+}
+
+int uffs_seekdir(uffs_DIR *dir, long offset)
+{
+    int i = 0;
+
+    while (i < offset)
+    {
+        if (uffs_readdir(dir) == RT_NULL)
+            return -1;
+        i++;
+    }
+    return 0;
+}
+
+
+static int dfs_uffs_seek(struct dfs_fd *file,
+                         rt_off_t offset)
+{
+    int result;
+
+    /* set offset as current offset */
+    if (file->type == FT_DIRECTORY)
+    {
+        uffs_rewinddir((uffs_DIR *)(file->data));
+        result = uffs_seekdir((uffs_DIR *)(file->data), offset / sizeof(struct dirent));
+        if (result >= 0)
+        {
+            file->pos = offset;
+            return offset;
+        }
+    }
+    else if (file->type == FT_REGULAR)
+    {
+        result = uffs_seek((int)(file->data), offset, USEEK_SET);
+        if (result >= 0)
+            return offset;
+    }
+
+    return uffs_result_to_dfs(uffs_get_error());
+}
+
+/* return the size of struct dirent*/
+static int dfs_uffs_getdents(
+    struct dfs_fd *file,
+    struct dirent *dirp,
+    uint32_t count)
+{
+    rt_uint32_t index;
+    char *file_path;
+    struct dirent *d;
+    uffs_DIR *dir;
+    struct uffs_dirent *uffs_d;
+
+    dir = (uffs_DIR *)(file->data);
+    RT_ASSERT(dir != RT_NULL);
+
+    /* round count, count is always 1 */
+    count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
+    if (count == 0) return -EINVAL;
+
+    /* allocate file name */
+    file_path = rt_malloc(FILE_PATH_MAX);
+    if (file_path == RT_NULL)
+        return -ENOMEM;
+
+    index = 0;
+    /* usually, the while loop should only be looped only once! */
+    while (1)
+    {
+        struct uffs_stat s;
+
+        d = dirp + index;
+
+        uffs_d = uffs_readdir(dir);
+        if (uffs_d == RT_NULL)
+        {
+            rt_free(file_path);
+            return (uffs_result_to_dfs(uffs_get_error()));
+        }
+
+        if (file->path[0] == '/' && !(file->path[1] == 0))
+            rt_snprintf(file_path, FILE_PATH_MAX, "%s/%s", file->path, uffs_d->d_name);
+        else
+            rt_strncpy(file_path, uffs_d->d_name, FILE_PATH_MAX);
+
+        uffs_stat(file_path, &s);
+        switch (s.st_mode & US_IFMT)  /* file type mark */
+        {
+        case US_IFREG: /* directory */
+            d->d_type = DT_REG;
+            break;
+        case US_IFDIR: /* regular file */
+            d->d_type = DT_DIR;
+            break;
+        case US_IFLNK: /* symbolic link */
+        case US_IREAD: /* read permission */
+        case US_IWRITE:/* write permission */
+        default:
+            d->d_type = DT_UNKNOWN;
+            break;
+        }
+
+        /* write the rest args of struct dirent* dirp  */
+        d->d_namlen = rt_strlen(uffs_d->d_name);
+        d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
+        rt_strncpy(d->d_name, uffs_d->d_name, rt_strlen(uffs_d->d_name) + 1);
+
+        index ++;
+        if (index * sizeof(struct dirent) >= count)
+            break;
+    }
+
+    /* free file name buf */
+    rt_free(file_path);
+
+    if (index == 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    file->pos += index * sizeof(struct dirent);
+
+    return index * sizeof(struct dirent);
+}
+
+static int dfs_uffs_unlink(struct dfs_filesystem *fs, const char *path)
+{
+    int result;
+    struct uffs_stat s;
+
+    /* judge file type, dir is to be delete by uffs_rmdir, others by uffs_remove */
+    if (uffs_lstat(path, &s) < 0)
+    {
+        return uffs_result_to_dfs(uffs_get_error());
+    }
+
+    switch (s.st_mode & US_IFMT)
+    {
+    case US_IFREG:
+        result = uffs_remove(path);
+        break;
+    case US_IFDIR:
+        result = uffs_rmdir(path);
+        break;
+    default:
+        /* unknown file type */
+        return -1;
+    }
+    if (result < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    return 0;
+}
+
+static int dfs_uffs_rename(
+    struct dfs_filesystem *fs,
+    const char *oldpath,
+    const char *newpath)
+{
+    int result;
+
+    result = uffs_rename(oldpath, newpath);
+    if (result < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    return 0;
+}
+
+static int dfs_uffs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st)
+{
+    int result;
+    struct uffs_stat s;
+
+    result = uffs_stat(path, &s);
+    if (result < 0)
+        return uffs_result_to_dfs(uffs_get_error());
+
+    /* convert uffs stat to dfs stat structure */
+    /* FIXME, these field may not be the same */
+    st->st_dev  = 0;
+    st->st_mode = s.st_mode;
+    st->st_size = s.st_size;
+    st->st_mtime = s.st_mtime;
+
+    return 0;
+}
+
+static const struct dfs_file_ops dfs_uffs_fops =
+{
+    dfs_uffs_open,
+    dfs_uffs_close,
+    dfs_uffs_ioctl,
+    dfs_uffs_read,
+    dfs_uffs_write,
+    dfs_uffs_flush,
+    dfs_uffs_seek,
+    dfs_uffs_getdents,
+};
+
+static const struct dfs_filesystem_ops dfs_uffs_ops =
+{
+    "uffs", /* file system type: uffs */
+#if RTTHREAD_VERSION >= 10100
+    DFS_FS_FLAG_FULLPATH,
+#else
+#error "uffs can only work with rtthread whose version should >= 1.01\n"
+#endif
+    &dfs_uffs_fops,
+
+    dfs_uffs_mount,
+    dfs_uffs_unmount,
+    dfs_uffs_mkfs,
+    dfs_uffs_statfs,
+
+    dfs_uffs_unlink,
+    dfs_uffs_stat,
+    dfs_uffs_rename,
+};
+
+int dfs_uffs_init(void)
+{
+    /* register uffs file system */
+    dfs_register(&dfs_uffs_ops);
+
+    if (uffs_InitObjectBuf() == U_SUCC)
+    {
+        if (uffs_DirEntryBufInit() == U_SUCC)
+        {
+            uffs_InitGlobalFsLock();
+            return RT_EOK;
+        }
+    }
+    return -RT_ERROR;
+}
+INIT_COMPONENT_EXPORT(dfs_uffs_init);
+

+ 89 - 0
dfs_uffs.h

@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2012-03-30     prife        the first version
+ */
+
+#ifndef DFS_UFFS_H_
+#define DFS_UFFS_H_
+
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+
+/* the UFFS ECC mode opitons  */
+#ifndef RT_UFFS_ECC_MODE
+#define RT_UFFS_ECC_MODE      1
+#endif
+
+/*
+ * RT_UFFS_ECC_MODE:
+ * 0, Do not use ECC
+ * 1, UFFS calculate the ECC
+ * 2, Flash driver(or by hardware) calculate the ECC
+ * 3, Hardware calculate the ECC and automatically write to spare.
+ */
+#if RT_UFFS_ECC_MODE == 0
+#define RT_CONFIG_UFFS_ECC_MODE UFFS_ECC_NONE
+#elif RT_UFFS_ECC_MODE == 1
+#define RT_CONFIG_UFFS_ECC_MODE UFFS_ECC_SOFT
+#elif RT_UFFS_ECC_MODE == 2
+#define RT_CONFIG_UFFS_ECC_MODE UFFS_ECC_HW
+#elif RT_UFFS_ECC_MODE == 3
+#define RT_CONFIG_UFFS_ECC_MODE UFFS_ECC_HW_AUTO
+#endif
+
+/* #define RT_CONFIG_UFFS_ECC_MODE  UFFS_ECC_HW_AUTO */
+/* #define RT_CONFIG_UFFS_ECC_MODE  UFFS_ECC_SOFT */
+/* #define RT_CONFIG_UFFS_ECC_MODE  UFFS_ECC_NONE */
+
+/* enable this ,you need provide a mark_badblock/check_block funciton */
+/* #define RT_UFFS_USE_CHECK_MARK_FUNCITON */
+
+#if RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_SOFT      /* let uffs do soft ecc */
+#define RT_CONFIG_UFFS_LAYOUT    UFFS_LAYOUT_UFFS /* UFFS_LAYOUT_FLASH */
+
+#elif RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_HW_AUTO /* nand driver make ecc and do ecc correct */
+#define RT_CONFIG_UFFS_LAYOUT    UFFS_LAYOUT_FLASH
+
+#elif RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_NONE
+#define RT_CONFIG_UFFS_LAYOUT    UFFS_LAYOUT_UFFS /* UFFS_LAYOUT_FLASH */
+
+#else
+#error "uffs under rt-thread do not support this ECC mode"
+#endif /* RT_CONFIG_UFFS_ECC_MODE */
+
+#if (!CONFIG_USE_STATIC_MEMORY_ALLOCATOR)  && (CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR)
+#define RT_UFFS_MEMORY_ALLOCATOR  1  /* use system memory allocator */
+#elif (CONFIG_USE_STATIC_MEMORY_ALLOCATOR) && (!CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR)
+#define RT_UFFS_MEMORY_ALLOCATOR  0  /* use static memory allocator */
+#else
+#error "UFFS: CONFIG_USE_STATIC_MEMORY_ALLOCATOR ,CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR are invalid!"
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0
+#error "dfs_uffs only support CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR"
+#endif
+
+#if defined(CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME)
+#error "dfs_uffs not support CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME"
+#endif
+
+#if (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_HW_AUTO) && (RT_CONFIG_UFFS_LAYOUT != UFFS_LAYOUT_FLASH)
+#error "when use UFFS_ECC_HW_AUTO, you must use UFFS_LAYOUT_FLASH"
+
+#elif (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_SOFT) && (RT_CONFIG_UFFS_LAYOUT != UFFS_LAYOUT_UFFS)
+#warning "when use UFFS_ECC_SOFT, it is recommended to use UFFS_LAYOUT_UFFS"
+#endif
+
+extern const uffs_FlashOps nand_ops;
+
+extern void uffs_setup_storage(
+    struct uffs_StorageAttrSt *attr,
+    struct rt_mtd_nand_device *nand);
+
+extern int dfs_uffs_init(void);
+#endif /* DFS_UFFS_H_ */

BIN
doc/Understanding-UFFS.odp


BIN
doc/Understanding-UFFS.pdf


BIN
doc/uffs-serial-num-relationship.JPG


+ 7 - 0
src/CMakeLists.txt

@@ -0,0 +1,7 @@
+ADD_SUBDIRECTORY(emu)
+ADD_SUBDIRECTORY(uffs)
+ADD_SUBDIRECTORY(utils)
+ADD_SUBDIRECTORY(example)
+ADD_SUBDIRECTORY(platform)
+ADD_SUBDIRECTORY(test/api_test)
+ADD_SUBDIRECTORY(test/clients)

+ 24 - 0
src/emu/CMakeLists.txt

@@ -0,0 +1,24 @@
+SET (libemu_SRCS 
+		cmdline.c
+		cmdline.h
+		helper_cmds.c
+		uffs_fileem.c
+		uffs_fileem_share.c
+		uffs_fileem_wrap.c
+		uffs_fileem_ecc_soft.c
+		uffs_fileem_ecc_hw.c
+		uffs_fileem_ecc_hw_auto.c
+		uffs_fileem.h
+		test_cmds.c
+	)
+
+IF (UNIX)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/posix)
+ENDIF()
+IF (WIN32)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/win32)
+ENDIF()
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc)
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/test/api_test)
+ADD_LIBRARY(emu STATIC ${libemu_SRCS} )
+

+ 643 - 0
src/emu/cmdline.c

@@ -0,0 +1,643 @@
+/*
+This file is part of UFFS, the Ultra-low-cost Flash File System.
+
+Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+UFFS is free software; you can redistribute it and/or modify it under
+the GNU Library General Public License as published by the Free Software 
+Foundation; either version 2 of the License, or (at your option) any
+later version.
+
+UFFS 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
+or GNU Library General Public License, as applicable, for more details.
+
+You should have received a copy of the GNU General Public License
+and GNU Library General Public License along with UFFS; if not, write
+to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA  02110-1301, USA.
+
+As a special exception, if other files instantiate templates or use
+macros or inline functions from this file, or you compile this file
+and link it with other works to produce a work based on this file,
+this file does not by itself cause the resulting work to be covered
+by the GNU General Public License. However the source code for this
+file must still be made available in accordance with section (3) of
+the GNU General Public License v2.
+
+This exception does not invalidate any other reasons why a work based
+on this file might be covered by the GNU General Public License.
+*/
+
+/**
+* \file cmdline.c
+* \brief command line test interface
+* \author Ricky Zheng, created in 22th Feb, 2007
+*/
+
+#include <string.h>
+#include <stdio.h>
+//#include <conio.h>
+#include "uffs_config.h"
+#include "cmdline.h"
+#include "uffs/uffs_fs.h"
+
+#define PROMPT "UFFS>"
+
+#define PFX "cli : "
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+#define MAX_CLI_ARGS_BUF_LEN	120
+#define MAX_CLI_ARGS_NUM		20
+#define MAX_CLI_ENV_NUM			11		// '?', '0' - '9'
+
+
+struct cli_arg {
+	int argc;
+	char *argv[MAX_CLI_ARGS_NUM];
+	char _buf[MAX_CLI_ARGS_BUF_LEN];
+};
+
+static BOOL m_exit = FALSE;
+static BOOL m_abort = FALSE;
+static struct cli_commandset *m_cmdset_head = NULL;
+
+// Note: last command return code stored in env 0.
+static int m_cli_envs[MAX_CLI_ENV_NUM] = {0};	// cli environment variables
+
+static const struct cli_command * cli_find(const char *cmd);
+static int cmd_help(int argc, char *argv[]);
+
+
+#define FOR_EACH_CLI_CMD(set, cmd) \
+	for (set = m_cmdset_head; set && set->cmds; set = set->next) \
+		for (cmd = set->cmds; cmd && cmd->handler; cmd++)
+
+
+/*** filter out leading and tailing spaces, discard comments
+ * return pointer to start of new command line
+ */
+static char * cli_process_line(char *p)
+{
+	char *s;
+	char *x;
+
+	if (!p)
+		return NULL;
+
+	// skip leading spaces
+	while (p && (*p == ' ' || *p == '\t'))
+		p++;
+
+	for (s = x = p; *p; x++, p++) {
+		switch(*p) {
+		case '\\':
+			p++;
+			if (*p) {
+				switch(*p) {
+				case 'n':
+					*x = '\n';
+					break;
+				case 'r':
+					*x = '\r';
+					break;
+				case 't':
+					*x = '\t';
+					break;
+				case 'b':
+					*x = '\b';
+					break;
+				default:
+					if (*p >= '0' && *p <= '9')
+						*x = *p - '0';
+					else
+						*x = *p;
+					break;
+				}
+			}
+			break;
+		default:
+			if (*p == '\r' || *p == '\n' || *p == '#') *p = '\0';
+			*x = *p;
+			break;
+		}
+
+		if (*p == 0)
+			break;
+	}
+
+	// trim tailing spaces
+	p--;
+	while (p > s && (*p == ' ' || *p == '\t'))
+		*p-- = '\0';
+
+	return s;
+}
+
+static int cli_env_to_idx(char env)
+{
+	int idx = -1;
+
+	if (env >= '0' && env <= '9') {
+		idx = env - '0' + 1;
+	}
+	else if (env == '?') {
+		idx = 0;
+	}
+
+	return idx;
+}
+
+int cli_env_set(char env, int val)
+{
+	int idx = cli_env_to_idx(env);
+
+	if (idx >= 0) {
+		m_cli_envs[idx] = val;
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int cli_env_get(char env)
+{
+	int idx = cli_env_to_idx(env);
+
+	return idx >= 0 ? m_cli_envs[idx] : 0;
+}
+
+/** exec command <n> times:
+ *		exec <n> <cmd> [...]
+ */
+static int cmd_exec(int argc, char *argv[])
+{
+	int n = 0;
+	const struct cli_command *cmd;
+
+	CHK_ARGC(3, 0);
+
+	if (sscanf(argv[1], "%d", &n) != 1)
+		return CLI_INVALID_ARG;
+	if (n <= 0)
+		return CLI_INVALID_ARG;
+
+	cmd = cli_find(argv[2]);
+	if (cmd == NULL) {
+		MSG("Unknown command '%s'\n", argv[2]);
+		return -1;
+	}
+	else {
+		argv += 2;
+		while (n-- >= 0) {
+			if (cmd->handler(argc - 2, argv) != 0)
+				return -1;
+		}
+	}
+
+	return 0;
+}
+
+/** 
+ * test expression
+ *	test <a> <op> <b>
+ * for example:
+ *	test 1 > 0    ==> return 0
+ *	test 1 <= 0   ==> return -1
+ */
+static int cmd_test(int argc, char *argv[])
+{
+	int a, b;
+	char *op;
+	BOOL tst = FALSE;
+
+	CHK_ARGC(4, 4);
+
+	if (sscanf(argv[1], "%d", &a) != 1 ||
+		sscanf(argv[3], "%d", &b) != 1)
+	{
+		return CLI_INVALID_ARG;
+	}
+
+	op = argv[2];
+	if (!strcmp(op, ">")) {
+		tst = (a > b);
+	}
+	else if (!strcmp(op, "<")) {
+		tst = (a < b);
+	}
+	else if (!strcmp(op, "==")) {
+		tst = (a == b);
+	}
+	else if (!strcmp(op, ">=")) {
+		tst = (a >= b);
+	}
+	else if (!strcmp(op, "<=")) {
+		tst = (a <= b);
+	}
+	else if (!strcmp(op, "!=")) {
+		tst = (a != b);
+	}
+	else {
+		return CLI_INVALID_ARG;
+	}
+	
+	return tst ? 0 : -1;
+}
+
+/** if last command failed (return != 0), run <cmd>
+ *    ! <cmd>
+ */
+static int cmd_failed(int argc, char *argv[])
+{
+	const struct cli_command *cmd;
+
+	CHK_ARGC(2, 0);
+
+	cmd = cli_find(argv[1]);
+	if (cmd == NULL) {
+		MSG("Unknown command '%s'\n", argv[1]);
+		return -1;
+	}
+	else {
+		argv++;
+		return (cli_env_get('?') == 0 ? 0 : cmd->handler(argc - 1, argv));
+	}
+}
+
+/** print messages
+ *		echo [<arg> ...]
+ */
+static int cmd_echo(int argc, char *argv[])
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		MSG("%s%s", i > 1 ? " " : "", argv[i]);
+	}
+	MSG("\n");
+
+	return 0;
+}
+
+/** set cli environment variable
+ *		set <env> <value>
+ */
+static int cmd_set(int argc, char *argv[])
+{
+	int val;
+	int ret = -1;
+
+	CHK_ARGC(3, 0);
+
+	if (sscanf(argv[2], "%d", &val) == 1) {
+		ret = cli_env_set(argv[1][0], val);
+	}
+
+	return ret;
+}
+
+/** evaluation the expresstion, result to $1
+ *		evl <value> <op> <value>
+ */
+static int cmd_evl(int argc, char *argv[])
+{
+	int val1, val2, result = 0;
+	int ret = -1;
+
+	CHK_ARGC(4, 4);
+
+	if (sscanf(argv[1], "%d", &val1) == 1 &&
+		sscanf(argv[3], "%d", &val2) == 1) {
+		ret = 0;
+		switch(argv[2][0]) {
+			case '+':
+				result = val1 + val2;
+				break;
+			case '-':
+				result = val1 - val2;
+				break;
+			case '*':
+				result = val1 * val2;
+				break;
+			case '/':
+				if (val2 == 0)
+					ret = -1;
+				else
+					result = val1 / val2;
+				break;
+			case '%':
+				if (val2 == 0)
+					ret = -1;
+				else
+					result = val1 % val2;
+				break;
+			default:
+				ret = CLI_INVALID_ARG;
+				break;
+		}
+	}
+
+	if (ret == 0)
+		ret = cli_env_set('1', result);
+
+	return ret;
+}
+
+static int cmd_exit(int argc, char *argv[])
+{
+	m_exit = TRUE;
+	return 0;
+}
+
+/** Abort current script
+ *		abort [...]
+ */
+static int cmd_abort(int argc, char *argv[])
+{
+	if (argc > 1) {
+		cmd_echo(argc, argv);
+	}
+
+	m_abort = TRUE;
+
+	return 0;
+}
+
+/** run local file system's script
+ *		script <filename>
+ */
+static int cmd_script(int argc, char *argv[])
+{
+	char line_buf[256];
+	char *p;
+	FILE *fp;
+	const char *name;
+	int ret = 0;
+	static int stack = 0;
+
+	CHK_ARGC(2, 0);
+
+	if (stack++ == 0)
+		m_abort = FALSE;
+
+	name = argv[1];
+	fp = fopen(name, "r");
+
+	if (fp) {
+		memset(line_buf, 0, sizeof(line_buf));
+		while (!m_abort && fgets(line_buf, sizeof(line_buf) - 1, fp)) {
+			p = line_buf + sizeof(line_buf) - 1;
+			while (*p == 0 && p > line_buf)
+				p--;
+			while ((*p == '\r' || *p == '\n') && p > line_buf) {
+				*p-- = 0;
+			}
+			p = cli_process_line(line_buf);
+			if (*p)
+				ret = cli_interpret(p);
+			memset(line_buf, 0, sizeof(line_buf));
+		}
+		fclose(fp);
+	}
+	else {
+		MSG("Can't open host script file '%s' for read\n", name);
+		ret = -1;
+	}
+
+	stack--;
+
+	return ret;
+}
+
+
+
+static const struct cli_command default_cmds[] = 
+{
+	{ cmd_help,		"help|?",	"[<command>]",		"show commands or help on one command" },
+	{ cmd_exit,		"exit",		NULL,				"exit command line" },
+	{ cmd_exec,		"*",		"<n> <cmd> [...]>",	"run <cmd> <n> times" },
+	{ cmd_failed,	"!",		"<cmd> [...]",		"run <cmd> if last command failed" },
+	{ cmd_echo,		"echo",		"[...]",			"print messages" },
+	{ cmd_set,		"set",		"<env> <val>",		"set env variable" },
+	{ cmd_evl,		"evl",		"<val> <op> <val>",	"evaluation expresstion" },
+	{ cmd_test,		"test",		"<a> <op> <b>",		"test expression: <a> <op> <b>" },
+	{ cmd_script,   "script",   "<file>",           "run host script <file>" },
+	{ cmd_abort,	"abort",	NULL,				"abort from the running script" },
+	{ NULL, NULL, NULL, NULL }
+};
+
+static struct cli_commandset default_cmdset = {
+	default_cmds,
+};
+
+static BOOL match_cmd(const char *src, int start, int end, const char *des)
+{
+	while (src[start] == ' ' && start < end) 
+		start++;
+
+	while (src[end] == ' ' && start < end) 
+		end--;
+
+	if ((int)strlen(des) == (end - start + 1)) {
+		if (memcmp(src + start, des, end - start + 1) == 0) {
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static BOOL check_cmd(const char *cmds, const char *cmd)
+{
+	int start, end;
+
+	for (start = end = 0; cmds[end] != 0 && cmds[end] != '|'; end++);
+
+	while (end > start) {
+		if (match_cmd(cmds, start, end - 1, cmd) == TRUE) 
+			return TRUE;
+		if (cmds[end] == 0) 
+			break;
+		if (cmds[end] == '|') {
+			end++;
+			for (start = end; cmds[end] != 0 && cmds[end] != '|'; end++);
+		}
+	} 
+
+	return FALSE;
+}
+
+static const struct cli_command * cli_find(const char *cmd)
+{
+	struct cli_commandset *work;
+	const struct cli_command *s;
+
+	FOR_EACH_CLI_CMD(work, s) {
+		if (check_cmd(s->cmd, cmd) == TRUE)
+			return s;
+	}
+
+	return NULL;
+}
+
+static void show_cmd_usage(const struct cli_command *cmd)
+{
+	MSG("%s: %s\n", cmd->cmd, cmd->descr);
+	MSG("Usage: %s %s\n", cmd->cmd, cmd->args ? cmd->args : "");
+}
+
+static int cmd_help(int argc, char *argv[])
+{
+	const struct cli_command *cmd;
+	struct cli_commandset *cmdset;
+	int i, n;
+
+	if (argc < 2)  {
+		MSG("Available commands:\n");
+		n = 0;
+		FOR_EACH_CLI_CMD(cmdset, cmd) {
+			MSG("%s", cmd->cmd);
+			for (i = strlen(cmd->cmd); i%10; i++, MSG(" "));
+			if ((++n % 5) == 0) MSG("\n");
+		}
+		MSG("\n");
+	}
+	else {
+		cmd = cli_find(argv[1]);
+		if (cmd == NULL) {
+			MSG("No such command\n");
+			return -1;
+		}
+		else {
+			show_cmd_usage(cmd);
+		}
+	}
+
+	return 0;
+}
+
+static void cli_parse_args(const char *cmd, struct cli_arg *arg)
+{
+	char *p;
+	int val;
+
+	if (arg) {
+		arg->argc = 0;
+		if (cmd) {
+			p = arg->_buf;
+			while (*cmd && arg->argc < MAX_CLI_ARGS_NUM && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {
+				while(*cmd && (*cmd == ' ' || *cmd == '\t'))
+					cmd++;
+
+				arg->argv[arg->argc] = p;
+				while (*cmd && (*cmd != ' ' && *cmd != '\t') && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {
+					if (*cmd == '$') {
+						// command env replacement
+						cmd++;
+						val = cli_env_get(*cmd++);
+						if (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN - 12) {  // 12 is long enough for 32bit 'int'
+							p += sprintf(p, "%d", val & 0xFFFFFFFF);
+						}
+					}
+					else
+						*p++ = *cmd++;
+				}
+				*p++ = '\0';
+
+				if (*(arg->argv[arg->argc]) == '\0')
+					break;
+				arg->argc++;
+			}
+		}
+	}
+}
+
+int cli_interpret(const char *line)
+{
+	struct cli_arg arg = {0};
+	const struct cli_command *cmd;
+	int ret = -1;
+
+	cli_parse_args(line, &arg);
+
+	if (arg.argc > 0) {
+		cmd = cli_find(arg.argv[0]);
+		if (cmd == NULL) {
+			MSG("Unknown command '%s'\n", arg.argv[0]);
+		}
+		else {
+			ret = cmd->handler(arg.argc, arg.argv);
+			if (ret == CLI_INVALID_ARG) {
+				MSG("\n");
+				show_cmd_usage(cmd);
+			}
+		}
+	}
+
+	cli_env_set('?', ret);	// $? = last command return code
+
+	return ret;
+}
+
+void cli_add_commandset(struct cli_commandset *set)
+{
+	if (set) {
+		set->next = m_cmdset_head;
+		m_cmdset_head = set;
+	}
+}
+
+void cli_main_entry()
+{
+	char line[80];
+	int linelen = 0;
+	char *p;
+
+	MSG("$ ");
+	cli_add_commandset(&default_cmdset);
+
+	while (!m_exit) {
+		char ch;
+		if (linelen >= sizeof(line))
+			continue;
+		ch = getc(stdin);
+		switch (ch) {
+		case 8:
+		case 127:
+			if (linelen > 0) {
+				--linelen;
+				MSG("\x08 \x08");
+			}
+			break;
+
+		case '\r':
+		case '\n':
+			//MSG("\r\n");
+			if (linelen > 0) {
+				line[linelen] = 0;
+				p = cli_process_line(line);
+				if (*p)
+					cli_interpret(p);
+				linelen = 0;
+			}
+			MSG("$ ");
+			break;
+
+		case 21:
+			while (linelen > 0) {
+				--linelen;
+				MSG("\x08 \x08");
+			}
+			break;
+
+		default:
+			if (ch >= ' ' && ch < 127 && linelen < sizeof(line) - 1) {
+				line[linelen++] = ch;
+				//MSG("%c", ch);
+			}
+		}
+	}
+}

+ 83 - 0
src/emu/cmdline.h

@@ -0,0 +1,83 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+
+#ifndef _UFFS_CLI_H_
+#define _UFFS_CLI_H_
+
+#ifndef BOOL
+#define BOOL int
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define CLI_INVALID_ARG			-100
+
+typedef int command_t(int argc, char *argv[]);
+
+struct cli_command {
+    command_t *handler;
+    const char *cmd;
+    const char *args;
+    const char *descr;
+};
+
+struct cli_commandset {
+	const struct cli_command *cmds;
+	struct cli_commandset *next;
+};
+
+void cli_add_commandset(struct cli_commandset *set);
+int cli_interpret(const char *line);
+int cli_env_get(char env);
+int cli_env_set(char env, int val);
+void cli_main_entry();
+
+#define u_assert(x) \
+	((x) ? TRUE : \
+			(uffs_PerrorRaw(UFFS_MSG_NORMAL, \
+				"Assert failed at %s:%s:%d: '%s' is not true.\n", \
+				__FILE__, __FUNCTION__, __LINE__, #x), FALSE))
+
+
+#define CHK_ARGC(min, max) \
+	if (argc < min || (max > 0 && argc > max)) \
+		return CLI_INVALID_ARG
+
+#endif
+
+

+ 694 - 0
src/emu/helper_cmds.c

@@ -0,0 +1,694 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file helper_cmds.c
+ * \brief helper commands for test uffs
+ * \author Ricky Zheng
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_find.h"
+#include "cmdline.h"
+#include "uffs/uffs_fd.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs_fileem.h"
+
+#define PFX "cmd : "
+
+#define MAX_PATH_LENGTH 128
+
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+/** format [<mount>] */
+static int cmd_format(int argc, char *argv[])
+{
+	URET ret;
+	const char *mount = "/";
+	uffs_Device *dev;
+	UBOOL force = U_FALSE;
+
+	if (argc > 1) {
+		mount = argv[1];
+		if (argc > 2 && strcmp(argv[2], "-f") == 0)
+			force = U_TRUE;
+	}
+	MSGLN("Formating %s ... ", mount);
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point.");
+		return -1;
+	}
+	else {
+		ret = uffs_FormatDevice(dev, force);
+		if (ret != U_SUCC) {
+			MSGLN("Format fail.");
+			return -1;
+		}
+		else {
+			MSGLN("Format succ.");
+		}
+		uffs_PutDevice(dev);
+	}
+
+	return 0;
+}
+
+/** mkf <file> */
+static int cmd_mkf(int argc, char *argv[])
+{
+	int fd;
+	const char *name;
+	int oflags = UO_RDWR | UO_CREATE;
+
+	CHK_ARGC(2, 2);
+
+	name = argv[1];
+	fd = uffs_open(name, oflags);
+	if (fd < 0) {
+		MSGLN("Create %s fail, err: %d", name, uffs_get_error());
+		return -1;
+	}
+	else {
+		MSGLN("Create %s succ.", name);
+		uffs_close(fd);
+	}
+	
+	return 0;
+}
+
+/** mkdir <dir> */
+static int cmd_mkdir(int argc, char *argv[])
+{
+	const char *name;
+
+	CHK_ARGC(2, 0);
+
+	name = argv[1];
+	
+	if (uffs_mkdir(name) < 0) {
+		MSGLN("Create %s fail, err: %d", name, uffs_get_error());
+		return -1;
+	}
+	else {
+		MSGLN("Create %s succ.", name);
+	}
+
+	return 0;
+}
+
+
+static int CountObjectUnder(const char *dir)
+{
+	int count = 0;
+	uffs_DIR *dirp;
+
+	dirp = uffs_opendir(dir);
+	if (dirp) {
+		while (uffs_readdir(dirp) != NULL)
+			count++;
+		uffs_closedir(dirp);
+	}
+	return count;
+}
+
+static int cmd_pwd(int argc, char *argv[])
+{
+	MSGLN("not supported.");
+	return 0;
+}
+
+static int cmd_cd(int argc, char *argv[])
+{
+	MSGLN("Not supported");
+	return 0;
+}
+
+/** ls [<dir>] */
+static int cmd_ls(int argc, char *argv[])
+{
+	uffs_DIR *dirp;
+	struct uffs_dirent *ent;
+	struct uffs_stat stat_buf;
+	int count = 0;
+	char buf[MAX_PATH_LENGTH+2];
+	const char *name = "/";
+	char *sub;
+	int ret = 0;
+
+	CHK_ARGC(1, 2);
+
+	if (argc > 1)
+		name = argv[1];
+
+	dirp = uffs_opendir(name);
+	if (dirp == NULL) {
+		MSGLN("Can't open '%s' for list", name);
+		ret = -1;
+	}
+	else {
+		MSG("------name-----------size---------serial-----" TENDSTR);
+		ent = uffs_readdir(dirp);
+		while (ent) {
+			MSG("%9s", ent->d_name);
+			strcpy(buf, name);
+			sub = buf;
+			if (name[strlen(name)-1] != '/')
+				sub = strcat(buf, "/");
+			sub = strcat(sub, ent->d_name);
+			if (ent->d_type & FILE_ATTR_DIR) {
+				sub = strcat(sub, "/");
+				MSG("/  \t<%8d>", CountObjectUnder(sub));
+			}
+			else {
+				uffs_stat(sub, &stat_buf);
+				MSG("   \t %8d ", stat_buf.st_size);
+			}
+			MSG("\t%6d" TENDSTR, ent->d_ino);
+			count++;
+			ent = uffs_readdir(dirp);
+		}
+		
+		uffs_closedir(dirp);
+
+		MSG("Total: %d objects." TENDSTR, count);
+	}
+
+	return ret;
+}
+
+/** rm <obj> */
+static int cmd_rm(int argc, char *argv[])
+{
+	const char *name = NULL;
+	int ret = 0;
+	struct uffs_stat st;
+
+	CHK_ARGC(2, 2);
+
+	name = argv[1];
+
+	ret = uffs_stat(name, &st);
+	if (ret < 0) {
+		MSGLN("Can't stat '%s'", name);
+		return ret;
+	}
+
+	if (st.st_mode & US_IFDIR) {
+		ret = uffs_rmdir(name);
+	}
+	else {
+		ret = uffs_remove(name);
+	}
+
+	if (ret == 0)
+		MSGLN("Delete '%s' succ.", name);
+	else
+		MSGLN("Delete '%s' fail!", name);
+
+	return ret;
+}
+
+/** ren|mv <old> <new> */
+static int cmd_ren(int argc, char *argv[])
+{
+	const char *oldname;
+	const char *newname;
+	int ret;
+
+	CHK_ARGC(3, 3);
+
+	oldname = argv[1];
+	newname = argv[2];
+
+	if ((ret = uffs_rename(oldname, newname)) == 0) {
+		MSGLN("Rename from '%s' to '%s' succ.", oldname, newname);
+	}
+	else {
+		MSGLN("Rename from '%s' to '%s' fail!", oldname, newname);
+	}
+
+	return ret;
+}
+
+static void dump_msg_to_stdout(struct uffs_DeviceSt *dev, const char *fmt, ...)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	va_list args;
+
+	va_start(args, fmt);
+	//vprintf(fmt, args);
+	if (emu && emu->dump_fp)
+		vfprintf(emu->dump_fp, fmt, args);
+	va_end(args);
+}
+
+/** dump [<mount>] */
+static int cmd_dump(int argc, char *argv[])
+{
+	uffs_Device *dev;
+	uffs_FileEmu *emu;
+	const char *mount = "/";
+	const char *dump_file = "dump.txt";
+
+	if (argc > 1) {
+		mount = argv[1];
+		if (argc > 2)
+			dump_file = argv[2];
+	}
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point %s", mount);
+		return -1;
+	}
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+	emu->dump_fp = fopen(dump_file, "w");
+
+	uffs_DumpDevice(dev, dump_msg_to_stdout);
+
+	if (emu->dump_fp)
+		fclose(emu->dump_fp);
+
+	uffs_PutDevice(dev);
+
+	return 0;
+}
+
+/** st [<mount>] */
+static int cmd_st(int argc, char *argv[])
+{
+	uffs_Device *dev;
+	const char *mount = "/";
+	uffs_FlashStat *s;
+	TreeNode *node;
+
+	if (argc > 1) {
+		mount = argv[1];
+	}
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point %s", mount);
+		return -1;
+	}
+
+	s = &(dev->st);
+
+	MSG("----------- basic info -----------" TENDSTR);
+	MSG("TreeNode size:         %d" TENDSTR, sizeof(TreeNode));
+	MSG("TagStore size:         %d" TENDSTR, sizeof(struct uffs_TagStoreSt));
+	MSG("MaxCachedBlockInfo:    %d" TENDSTR, dev->cfg.bc_caches);
+	MSG("MaxPageBuffers:        %d" TENDSTR, dev->cfg.page_buffers);
+	MSG("MaxDirtyPagesPerBlock: %d" TENDSTR, dev->cfg.dirty_pages);
+	MSG("MaxPathLength:         %d" TENDSTR, MAX_PATH_LENGTH);
+	MSG("MaxObjectHandles:      %d" TENDSTR, MAX_OBJECT_HANDLE);
+	MSG("FreeObjectHandles:     %d" TENDSTR, uffs_GetFreeObjectHandlers());
+	MSG("MaxDirHandles:         %d" TENDSTR, MAX_DIR_HANDLE);
+	MSG("FreeDirHandles:        %d" TENDSTR, uffs_PoolGetFreeCount(uffs_DirEntryBufGetPool()));
+
+	MSG("----------- statistics for '%s' -----------" TENDSTR, mount);
+	MSG("Device Ref:            %d" TENDSTR, dev->ref_count);
+	MSG("Block Erased:          %d" TENDSTR, s->block_erase_count);
+	MSG("Write Page:            %d" TENDSTR, s->page_write_count);
+	MSG("Write Spare:           %d" TENDSTR, s->spare_write_count);
+	MSG("Read Page:             %d" TENDSTR, s->page_read_count - s->page_header_read_count);
+	MSG("Read Header:           %d" TENDSTR, s->page_header_read_count);
+	MSG("Read Spare:            %d" TENDSTR, s->spare_read_count);
+	MSG("I/O Read:              %lu" TENDSTR, s->io_read);
+	MSG("I/O Write:             %lu" TENDSTR, s->io_write);
+
+	MSG("--------- partition info for '%s' ---------" TENDSTR, mount);
+	MSG("Space total:           %d" TENDSTR, uffs_GetDeviceTotal(dev));
+	MSG("Space used:            %d" TENDSTR, uffs_GetDeviceUsed(dev));
+	MSG("Space free:            %d" TENDSTR, uffs_GetDeviceFree(dev));
+	MSG("Page Size:             %d" TENDSTR, dev->attr->page_data_size);
+	MSG("Spare Size:            %d" TENDSTR, dev->attr->spare_size);
+	MSG("Pages Per Block:       %d" TENDSTR, dev->attr->pages_per_block);
+	MSG("Block size:            %d" TENDSTR, dev->attr->page_data_size * dev->attr->pages_per_block);
+	MSG("Total blocks:          %d of %d" TENDSTR, (dev->par.end - dev->par.start + 1), dev->attr->total_blocks);
+	if (dev->tree.bad) {
+		MSG("Bad blocks: ");
+		node = dev->tree.bad;
+		while(node) {
+			MSG("%d, ", node->u.list.block);
+			node = node->u.list.next;
+		}
+		MSG(TENDSTR);
+	}
+
+	uffs_PutDevice(dev);
+
+	return 0;
+
+}
+
+/** cp <src> <des> */
+static int cmd_cp(int argc, char *argv[])
+{
+	const char *src;
+	const char *des;
+	char buf[100];
+	int fd1 = -1, fd2 = -1;
+	int len;
+	BOOL src_local = FALSE, des_local = FALSE;
+	FILE *fp1 = NULL, *fp2 = NULL;
+	int ret = -1;
+
+	CHK_ARGC(3, 3);
+
+	src = argv[1];
+	des = argv[2];
+
+	if (memcmp(src, "::", 2) == 0) {
+		src += 2;
+		src_local = TRUE;
+	}
+	if (memcmp(des, "::", 2) == 0) {
+		des += 2;
+		des_local = TRUE;
+	}
+	
+	if (src_local) {
+		if ((fp1 = fopen(src, "rb")) == NULL) {
+			MSGLN("Can't open %s for copy.", src);
+			goto fail_ext;
+		}
+	}
+	else {
+		if ((fd1 = uffs_open(src, UO_RDONLY)) < 0) {
+			MSGLN("Can't open %s for copy.", src);
+			goto fail_ext;
+		}
+	}
+
+	if (des_local) {
+		if ((fp2 = fopen(des, "wb")) == NULL) {
+			MSGLN("Can't open %s for copy.", des);
+			goto fail_ext;
+		}
+	}
+	else {
+		if ((fd2 = uffs_open(des, UO_RDWR|UO_CREATE|UO_TRUNC)) < 0) {
+			MSGLN("Can't open %s for copy.", des);
+			goto fail_ext;
+		}
+	}
+
+	ret = 0;
+	while (	(src_local ? (feof(fp1) == 0) : (uffs_eof(fd1) == 0)) ) {
+		ret = -1;
+		if (src_local) {
+			len = fread(buf, 1, sizeof(buf), fp1);
+		}
+		else {
+			len = uffs_read(fd1, buf, sizeof(buf));
+		}
+		if (len == 0) {
+			ret = -1;
+			break;
+		}
+		if (len < 0) {
+			MSGLN("read file %s fail ?", src);
+			break;
+		}
+		if (des_local) {
+			if ((int)fwrite(buf, 1, len, fp2) != len) {
+				MSGLN("write file %s fail ? ", des);
+				break;
+			}
+		}
+		else {
+			if (uffs_write(fd2, buf, len) != len) {
+				MSGLN("write file %s fail ? ", des);
+				break;
+			}
+		}
+		ret = 0;
+	}
+
+fail_ext:
+	if (fd1 > 0)
+		uffs_close(fd1);
+	if (fd2 > 0)
+		uffs_close(fd2);
+	if (fp1) 
+		fclose(fp1);
+	if (fp2)
+		fclose(fp2);
+
+	return ret;
+}
+
+/** cat <file> [<offset>] [<size>] */
+static int cmd_cat(int argc, char *argv[])
+{
+	int fd;
+	const char *name = NULL;
+	char buf[100];
+	int start = 0, size = 0, printed = 0, n, len;
+	int ret = -1;
+
+	CHK_ARGC(2, 4);
+
+	name = argv[1];
+
+	if ((fd = uffs_open(name, UO_RDONLY)) < 0) {
+		MSGLN("Can't open %s", name);
+		goto fail;
+	}
+
+	if (argc > 2) {
+		start = strtol(argv[2], NULL, 10);
+		if (argc > 3) size = strtol(argv[3], NULL, 10);
+	}
+
+	if (start >= 0)
+		uffs_seek(fd, start, USEEK_SET);
+	else
+		uffs_seek(fd, -start, USEEK_END);
+
+	while (uffs_eof(fd) == 0) {
+		len = uffs_read(fd, buf, sizeof(buf) - 1);
+		if (len == 0) 
+			break;
+		if (len > 0) {
+			if (size == 0 || printed < size) {
+				n = (size == 0 ? len : (size - printed > len ? len : size - printed));
+				buf[n] = 0;
+				MSG("%s", buf);
+				printed += n;
+			}
+			else {
+				break;
+			}
+		}
+	}
+	MSG(TENDSTR);
+	uffs_close(fd);
+
+	ret = 0;
+fail:
+
+	return ret;
+}
+
+/** mount partition or show mounted partitions
+ *		mount [<mount>]
+ */
+static int cmd_mount(int argc, char *argv[])
+{
+	uffs_MountTable *tab;
+	const char *mount = NULL;
+
+	if (argc == 1) {
+		tab = uffs_MtbGetMounted();
+		while (tab) {
+			MSG(" %s : (%d) ~ (%d)\n", tab->mount, tab->start_block, tab->end_block);
+			tab = tab->next;
+		}
+	}
+	else {
+		mount = argv[1];
+		if (uffs_Mount(mount) < 0) {
+			MSGLN("Can't mount %s", mount);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/** unmount parition or show unmounted partitions
+ *		umount [<mount>]
+ */
+static int cmd_unmount(int argc, char *argv[])
+{
+	uffs_MountTable *tab;
+	const char *mount = NULL;
+
+	if (argc == 1) {
+		tab = uffs_MtbGetUnMounted();
+		while (tab) {
+			MSG(" %s : (%d) ~ (%d)\n", tab->mount, tab->start_block, tab->end_block);
+			tab = tab->next;
+		}
+	}
+	else {
+		mount = argv[1];
+		if (uffs_UnMount(mount) < 0) {
+			MSGLN("Can't unmount %s", mount);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/** inspect buffers
+ *		inspb [<mount>]
+ */
+static int cmd_inspb(int argc, char *argv[])
+{
+	uffs_Device *dev;
+	const char *mount = "/";
+
+	CHK_ARGC(1, 2);
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point %s", mount);
+		return -1;
+	}
+	uffs_BufInspect(dev);
+	uffs_PutDevice(dev);
+
+	return 0;
+
+}
+
+/** print block wear-leveling information
+ *		wl [<mount>]
+ */
+static int cmd_wl(int argc, char *argv[])
+{
+	const char *mount = "/";
+	uffs_Device *dev;
+	struct uffs_PartitionSt *par;
+	uffs_FileEmu *emu;
+	int i, max;
+	u32 n;
+
+#define NUM_PER_LINE	10
+
+	CHK_ARGC(1, 2);
+
+	if (argc > 1) {
+		mount = argv[1];
+	}
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point %s", mount);
+		return -1;
+	}
+
+	par = &dev->par;
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+	max = -1;
+
+	for (i = 0; i < par->end - par->start; i++) {
+		if ((i % NUM_PER_LINE) == 0) {
+			MSG("%04d:", i + par->start);
+		}
+		n = i + par->start;
+		max = (max == -1 ? n :
+				(emu->em_monitor_block[n] > emu->em_monitor_block[max] ? n : max)
+			   );
+		MSG(" %4d", emu->em_monitor_block[n]);
+		if (uffs_TreeFindBadNodeByBlock(dev, n))
+			MSG("%c", 'x');
+		else if (uffs_TreeFindErasedNodeByBlock(dev, n))
+			MSG("%c", ' ');
+		else
+			MSG("%c", '.');
+		if (((i + 1) % NUM_PER_LINE) == 0)
+			MSG("\n");
+	}
+	MSG("\n");
+	MSG("Total blocks %d, peak erase count %d at block %d\n",
+		par->end - par->start, max == -1 ? 0 : emu->em_monitor_block[max], max);
+
+	uffs_PutDevice(dev);
+
+	return 0;
+}
+
+static const struct cli_command helper_cmds[] = 
+{
+    { cmd_format,	"format",		"[<mount>]",		"Format device" },
+    { cmd_mkf,		"mkfile",		"<name>",			"create a new file" },
+    { cmd_mkdir,	"mkdir",		"<name>",			"create a new directory" },
+    { cmd_rm,		"rm",			"<name>",			"delete file/directory" },
+    { cmd_ren,		"mv|ren",		"<old> <new>",		"rename file/directory" },
+    { cmd_ls,		"ls",			"<dir>",			"list dirs and files" },
+    { cmd_st,		"info|st",		"<mount>",			"show statistic infomation" },
+    { cmd_cp,		"cp",			"<src> <des>",		"copy files. the local file name start with '::'" },
+    { cmd_cat,		"cat",			"<name>",			"show file content" },
+    { cmd_pwd,		"pwd",			NULL,				"show current dir" },
+    { cmd_cd,		"cd",			"<path>",			"change current dir" },
+    { cmd_mount,	"mount",		"[<mount>]",		"mount partition or list mounted partitions" },
+    { cmd_unmount,	"umount",		"[<mount>]",		"unmount partition" },
+	{ cmd_dump,		"dump",			"[<mount>]",		"dump file system", },
+	{ cmd_wl,		"wl",			"[<mount>]",		"show block wear-leveling info", },
+	{ cmd_inspb,	"inspb",		"[<mount>]",		"inspect buffer", },
+    { NULL, NULL, NULL, NULL }
+};
+
+static struct cli_commandset helper_cmdset = {
+	helper_cmds,
+};
+
+struct cli_commandset * get_helper_cmds()
+{
+	return &helper_cmdset;
+};

+ 1186 - 0
src/emu/test_cmds.c

@@ -0,0 +1,1186 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file test_cmds.c
+ * \brief commands for test uffs
+ * \author Ricky Zheng
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_fd.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_find.h"
+#include "uffs/uffs_badblock.h"
+#include "cmdline.h"
+#include "api_test.h"
+
+#define PFX "test: "
+
+#define	MAX_TEST_BUF_LEN	8192
+
+#define SEQ_INIT	10
+#define SEQ_MOD_LEN	120
+
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+
+static void memcp_seq(void *des, int size, int start_pos)
+{
+	int i;
+	u8 *p = (u8 *)des;
+	
+	for (i = 0; i < size; i++, p++) {
+		*p = (start_pos + SEQ_INIT + i) % SEQ_MOD_LEN;
+	}
+}
+
+static UBOOL check_entry_exist(const char *name)
+{
+	struct uffs_stat sb;
+
+	return uffs_stat(name, &sb) < 0 ? U_FALSE : U_TRUE;
+}
+
+static URET do_write_test_file(int fd, int size)
+{
+	long pos;
+	unsigned char buf[100];
+	int len;
+
+	while (size > 0) {
+		pos = uffs_seek(fd, 0, USEEK_CUR);
+		len = (size > sizeof(buf) ? sizeof(buf) : size);
+		memcp_seq(buf, len, pos);
+		if (uffs_write(fd, buf, len) != len) {
+			MSGLN("Write file failed, size %d at %d", len, pos);
+			return U_FAIL;
+		}
+		size -= len;
+	}
+
+	return U_SUCC;
+}
+
+static URET test_write_file(const char *file_name, int pos, int size)
+{
+	int ret = U_FAIL;
+	int fd = -1;
+
+	if ((fd = uffs_open(file_name, UO_RDWR|UO_CREATE)) < 0) {
+		MSGLN("Can't open file %s for write.", file_name);
+		goto test_exit;
+	}
+
+	if (uffs_seek(fd, pos, USEEK_SET) != pos) {
+		MSGLN("Can't seek file %s at pos %d", file_name, pos);
+		goto test_failed;
+	}
+
+	if (do_write_test_file(fd, size) == U_FAIL) {
+		MSGLN("Write file %s failed.", file_name);
+		goto test_failed;
+	}
+	ret = U_SUCC;
+
+test_failed:
+	uffs_close(fd);
+
+test_exit:
+
+	return ret;
+}
+
+
+static URET test_verify_file(const char *file_name, UBOOL noecc)
+{
+	int fd;
+	int ret = U_FAIL;
+	unsigned char buf[100];
+	int i, pos, len;
+	u8 x;
+
+	if ((fd = uffs_open(file_name, (noecc ? UO_RDONLY|UO_NOECC : UO_RDONLY))) < 0) {
+		MSGLN("Can't open file %s for read.", file_name);
+		goto test_exit;
+	}
+
+	pos = 0;
+	while (!uffs_eof(fd)) {
+		len = uffs_read(fd, buf, sizeof(buf));
+		if (len <= 0)
+			goto test_failed;
+		for (i = 0; i < len; i++) {
+			x = (SEQ_INIT + pos + i) % SEQ_MOD_LEN;
+			if (buf[i] != x) {
+				MSGLN("Verify file %s failed at: %d, expect 0x%02x but got 0x%02x", file_name, pos + i, x, buf[i]);
+				goto test_failed;
+			}
+		}
+		pos += len;
+	}
+
+	if (pos != uffs_seek(fd, 0, USEEK_END)) {
+		MSGLN("Verify file %s failed. invalid file length.", file_name);
+		goto test_failed;
+	}
+
+	MSGLN("Verify file %s succ.", file_name);
+	ret = U_SUCC;
+
+test_failed:
+	uffs_close(fd);
+
+test_exit:
+
+	return ret;
+}
+
+static URET test_append_file(const char *file_name, int size)
+{
+	int ret = U_FAIL;
+	int fd = -1;
+
+	if ((fd = uffs_open(file_name, UO_RDWR|UO_APPEND|UO_CREATE)) < 0) {
+		MSGLN("Can't open file %s for append.", file_name);
+		goto test_exit;
+	}
+
+	uffs_seek(fd, 0, USEEK_END);
+
+	if (do_write_test_file(fd, size) == U_FAIL) {
+		MSGLN("Write file %s failed.", file_name);
+		goto test_failed;
+	}
+	ret = U_SUCC;
+
+test_failed:
+	uffs_close(fd);
+
+test_exit:
+
+	return ret;
+}
+
+
+/* test create file, write file and read back */
+static int cmd_t1(int argc, char *argv[])
+{
+	int fd;
+	URET ret;
+	char buf[100];
+	const char *name;
+
+	if (argc < 2) {
+		return CLI_INVALID_ARG;
+	}
+
+	name = argv[1];
+
+	fd = uffs_open(name, UO_RDWR|UO_CREATE|UO_TRUNC);
+	if (fd < 0) {
+		MSGLN("Can't open %s", name);
+		goto fail;
+	}
+
+	sprintf(buf, "123456789ABCDEF");
+	ret = uffs_write(fd, buf, strlen(buf));
+	MSGLN("write %d bytes to file, content: %s", ret, buf);
+
+	ret = uffs_seek(fd, 3, USEEK_SET);
+	MSGLN("new file position: %d", ret);
+
+	memset(buf, 0, sizeof(buf));
+	ret = uffs_read(fd, buf, 5);
+	MSGLN("read %d bytes, content: %s", ret, buf);
+
+	uffs_close(fd);
+
+	return 0;
+fail:
+
+	return -1;
+}
+
+
+static URET DoTest2(void)
+{
+	int fd = -1;
+	URET ret = U_FAIL;
+	char buf[100], buf_1[100];
+
+	fd = uffs_open("/abc/", UO_RDWR|UO_DIR);
+	if (fd < 0) {
+		MSGLN("Can't open dir abc, err: %d", uffs_get_error());
+		MSGLN("Try to create a new one...");
+		fd = uffs_open("/abc/", UO_RDWR|UO_CREATE|UO_DIR);
+		if (fd < 0) {
+			MSGLN("Can't create new dir /abc/");
+			goto exit_test;
+		}
+		else {
+			uffs_close(fd);
+		}
+	}
+	else {
+		uffs_close(fd);
+	}
+	
+	fd = uffs_open("/abc/test.txt", UO_RDWR|UO_CREATE);
+	if (fd < 0) {
+		MSGLN("Can't open /abc/test.txt");
+		goto exit_test;
+	}
+
+	sprintf(buf, "123456789ABCDEF");
+	ret = uffs_write(fd, buf, strlen(buf));
+	MSGLN("write %d bytes to file, content: %s", ret, buf);
+
+	ret = uffs_seek(fd, 3, USEEK_SET);
+	MSGLN("new file position: %d", ret);
+
+	memset(buf_1, 0, sizeof(buf_1));
+	ret = uffs_read(fd, buf_1, 5);
+	MSGLN("read %d bytes, content: %s", ret, buf_1);
+
+	if (memcmp(buf + 3, buf_1, 5) != 0) {
+		ret = U_FAIL;
+	}
+	else {
+		ret = U_SUCC;
+	}
+
+	uffs_close(fd);
+
+exit_test:
+
+	return ret;
+}
+
+
+static int cmd_t2(int argc, char *argv[])
+{
+	URET ret;
+	MSGLN("Test return: %s !", (ret = DoTest2()) == U_SUCC ? "succ" : "failed");
+
+	return (ret == U_SUCC) ? 0 : -1;
+}
+
+
+static int cmd_VerifyFile(int argc, char *argv[])
+{
+	const char *name;
+	UBOOL noecc = U_FALSE;
+
+	if (argc < 2) {
+		return CLI_INVALID_ARG;
+	}
+
+	name = argv[1];
+	if (argc > 2 && strcmp(argv[2], "noecc") == 0) {
+		noecc = U_TRUE;
+	}
+
+	MSGLN("Check file %s ... ", name);
+	if (test_verify_file(name, noecc) != U_SUCC) {
+		MSGLN("Verify file %s failed.", name);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Test file append and 'random' write */
+static int cmd_t3(int argc, char *argv[])
+{
+	const char *name;
+	int i;
+	UBOOL noecc = U_FALSE;
+	int write_test_seq[] = { 20, 10, 500, 40, 1140, 900, 329, 4560, 352, 1100 };
+
+	if (argc < 2) {
+		return CLI_INVALID_ARG;
+	}
+
+	name = argv[1];
+	if (argv[2] && strcmp(argv[2], "noecc") == 0) {
+		noecc = U_TRUE;
+	}
+
+	if (check_entry_exist(name)) {
+		MSGLN("Check file %s ... ", name);
+		if (test_verify_file(name, noecc) != U_SUCC) {
+			MSGLN("Verify file %s failed.", name);
+			return -1;
+		}
+	}
+
+	MSGLN("Test append file %s ...", name);
+	for (i = 1; i < 500; i += 29) {
+		if (test_append_file(name, i) != U_SUCC) {
+			MSGLN("Append file %s test failed at %d !", name, i);
+			return -1;
+		}
+	}
+
+	MSGLN("Check file %s ... ", name);
+	if (test_verify_file(name, noecc) != U_SUCC) {
+		MSGLN("Verify file %s failed.", name);
+		return -1;
+	}
+
+	MSGLN("Test write file ...");
+	for (i = 0; i < sizeof(write_test_seq) / sizeof(int) - 1; i++) {
+		if (test_write_file(name, write_test_seq[i], write_test_seq[i+1]) != U_SUCC) {
+			MSGLN("Test write file failed !");
+			return -1;
+		}
+	}
+
+	MSGLN("Check file %s ... ", name);
+	if (test_verify_file(name, noecc) != U_SUCC) {
+		MSGLN("Verify file %s failed.", name);
+		return -1;
+	}
+
+	MSGLN("Test succ !");
+
+	return 0;
+}
+
+/* open two files and test write */
+static int cmd_t4(int argc, char *argv[])
+{
+	int fd1 = -1, fd2 = -1;
+
+	MSGLN("open /a ...");
+	if ((fd1 = uffs_open("/a", UO_RDWR | UO_CREATE)) < 0) {
+		MSGLN("Can't open /a");
+		goto fail_exit;
+	}
+
+	MSGLN("open /b ...");
+	if ((fd2 = uffs_open("/b", UO_RDWR | UO_CREATE)) < 0) {
+		MSGLN("Can't open /b");
+		uffs_close(fd1);
+		goto fail_exit;
+	}
+
+	MSGLN("write (1) to /a ...");
+	uffs_write(fd1, "Hello,", 6);
+	MSGLN("write (1) to /b ...");
+	uffs_write(fd2, "Hello,", 6);
+	MSGLN("write (2) to /a ...");
+	uffs_write(fd1, "World.", 6);
+	MSGLN("write (2) to /b ...");
+	uffs_write(fd2, "World.", 6);
+	MSGLN("close /a ...");
+	uffs_close(fd1);
+	MSGLN("close /b ...");
+	uffs_close(fd2);
+
+	return 0;
+
+fail_exit:
+	return -1;
+}
+
+/* test appending file */
+static int cmd_t5(int argc, char *argv[])
+{
+	int fd = -1;
+	URET ret;
+	char buf[100];
+	const char *name;
+
+	if (argc < 2) {
+		return CLI_INVALID_ARG;
+	}
+
+	name = argv[1];
+
+	fd = uffs_open(name, UO_RDWR|UO_APPEND);
+	if (fd < 0) {
+		MSGLN("Can't open %s", name);
+		goto fail;
+	}
+
+	sprintf(buf, "append test...");
+	ret = uffs_write(fd, buf, strlen(buf));
+	if (ret != strlen(buf)) {
+		MSGLN("write file failed, %d/%d", ret, strlen(buf));
+		ret = -1;
+	}
+	else {
+		MSGLN("write %d bytes to file, content: %s", ret, buf);
+		ret = 0;
+	}
+
+	uffs_close(fd);
+
+	return ret;
+fail:
+	return -1;
+}
+
+
+/* usage: t_pgrw
+ *
+ * This test case test page read/write
+ */
+static int cmd_TestPageReadWrite(int argc, char *argv[])
+{
+	TreeNode *node = NULL;
+	uffs_Device *dev;
+	uffs_Tags local_tag;
+	uffs_Tags *tag = &local_tag;
+	int ret;
+	u16 block;
+	u16 page;
+	uffs_Buf *buf = NULL;
+
+	u32 i;
+	int rc = -1;
+
+	dev = uffs_GetDeviceFromMountPoint("/");
+	if (!dev)
+		goto ext;
+
+	buf = uffs_BufClone(dev, NULL);
+	if (!buf)
+		goto ext;
+
+	node = uffs_TreeGetErasedNode(dev);
+	if (!node) {
+		MSGLN("no free block ?");
+		goto ext;
+	}
+
+	for (i = 0; i < dev->com.pg_data_size; i++) {
+		buf->data[i] = i & 0xFF;
+	}
+
+	block = node->u.list.block;
+	page = 1;
+
+	TAG_DIRTY_BIT(tag) = TAG_DIRTY;
+	TAG_VALID_BIT(tag) = TAG_VALID;
+	TAG_DATA_LEN(tag) = dev->com.pg_data_size;
+	TAG_TYPE(tag) = UFFS_TYPE_DATA;
+	TAG_PAGE_ID(tag) = 3;
+	TAG_PARENT(tag) = 100;
+	TAG_SERIAL(tag) = 10;
+	TAG_BLOCK_TS(tag) = 1;
+	SEAL_TAG(tag);
+
+	ret = uffs_FlashWritePageCombine(dev, block, page, buf, tag);
+	if (UFFS_FLASH_HAVE_ERR(ret)) {
+		MSGLN("Write page error: %d", ret);
+		goto ext;
+	}
+
+	ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
+	if (UFFS_FLASH_HAVE_ERR(ret)) {
+		MSGLN("Read page error: %d", ret);
+		goto ext;
+	}
+
+	for (i = 0; i < dev->com.pg_data_size; i++) {
+		if (buf->data[i] != (i & 0xFF)) {
+			MSGLN("Data verify fail at: %d", i);
+			goto ext;
+		}
+	}
+
+	ret = uffs_FlashReadPageTag(dev, block, page, tag);
+	if (UFFS_FLASH_HAVE_ERR(ret)) {
+		MSGLN("Read tag (page spare) error: %d", ret);
+		goto ext;
+	}
+	
+	// verify tag:
+	if (!TAG_IS_SEALED(tag)) {
+		MSGLN("not sealed ? Tag verify fail!");
+		goto ext;
+	}
+
+	if (!TAG_IS_DIRTY(tag)) {
+		MSGLN("not dirty ? Tag verify fail!");
+		goto ext;
+	}
+
+	if (!TAG_IS_VALID(tag)) {
+		MSGLN("not valid ? Tag verify fail!");
+		goto ext;
+	}
+
+	if (TAG_DATA_LEN(tag) != dev->com.pg_data_size ||
+		TAG_TYPE(tag) != UFFS_TYPE_DATA ||
+		TAG_PAGE_ID(tag) != 3 ||
+		TAG_PARENT(tag) != 100 ||
+		TAG_SERIAL(tag) != 10 ||
+		TAG_BLOCK_TS(tag) != 1) {
+
+		MSGLN("Tag verify fail!");
+		goto ext;
+	}
+
+	MSGLN("Page read/write test succ.");
+	rc = 0;
+
+ext:
+	if (node) {
+		uffs_FlashEraseBlock(dev, node->u.list.block);
+		if (HAVE_BADBLOCK(dev))
+			uffs_BadBlockProcess(dev, node);
+		else
+			uffs_TreeInsertToErasedListTail(dev, node);
+	}
+
+	if (dev)
+		uffs_PutDevice(dev);
+
+	if (buf)
+		uffs_BufFreeClone(dev, buf);
+
+	return rc;
+}
+
+/* t_format : test format partition */
+static int cmd_TestFormat(int argc, char *argv[])
+{
+	URET ret;
+	const char *mount = "/";
+	uffs_Device *dev;
+	UBOOL force = U_FALSE;
+	const char *test_file = "/a.txt";
+	int fd;
+	int rc = -1;
+
+	if (argc > 1) {
+		mount = argv[1];
+		if (argc > 2 && strcmp(argv[2], "-f") == 0)
+			force = U_TRUE;
+	}
+
+	fd = uffs_open(test_file, UO_RDWR | UO_CREATE);
+	if (fd < 0) {
+		MSGLN("can't create test file %s", test_file);
+		goto ext;
+	}
+
+	MSGLN("Formating %s ... ", mount);
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point.");
+		goto ext;
+	}
+	else {
+		ret = uffs_FormatDevice(dev, force);
+		if (ret != U_SUCC) {
+			MSGLN("Format fail.");
+		}
+		else {
+			MSGLN("Format succ.");
+			rc = 0;
+		}
+		uffs_PutDevice(dev);
+	}
+
+	uffs_close(fd);  // this should fail on signature check !
+ext:
+	return rc;
+}
+
+
+
+/**
+ * usage: t_pfs <start> <n>
+ *
+ * for example: t_pfs /x/ 100
+ *
+ * This test case performs:
+ *   1) create <n> files under <start>, write full file name as file content
+ *   2) list files under <start>, check files are all listed once
+ *   3) check file content aganist file name
+ *   4) delete files on success
+ */
+static int cmd_TestPopulateFiles(int argc, char *argv[])
+{
+	const char *start = "/";
+	int count = 80;
+	int i, fd, num;
+	char name[128];
+	char buf[128];
+	uffs_DIR *dirp;
+	struct uffs_dirent *ent;
+	unsigned long bitmap[50] = {0};	// one bit per file, maximu 32*50 = 1600 files
+	UBOOL succ = U_TRUE;
+
+#define SBIT(n) bitmap[(n)/(sizeof(bitmap[0]) * 8)] |= (1 << ((n) % (sizeof(bitmap[0]) * 8)))
+#define GBIT(n) (bitmap[(n)/(sizeof(bitmap[0]) * 8)] & (1 << ((n) % (sizeof(bitmap[0]) * 8))))
+
+	if (argc > 1) {
+		start = argv[1];
+		if (argc > 2) {
+			count = strtol(argv[2], NULL, 10);
+		}
+	}
+
+	if (count > sizeof(bitmap) * 8)
+		count = sizeof(bitmap) * 8;
+
+	for (i = 0, fd = -1; i < count; i++) {
+		sprintf(name, "%sFile%03d", start, i);
+		fd = uffs_open(name, UO_RDWR|UO_CREATE|UO_TRUNC);
+		if (fd < 0) {
+			MSGLN("Create file %s failed", name);
+			break;
+		}
+		if (uffs_write(fd, name, strlen(name)) != strlen(name)) { // write full path name to file
+			MSGLN("Write to file %s failed", name);
+			uffs_close(fd);
+			break;
+		}
+		uffs_close(fd);
+	}
+
+	if (i < count) {
+		// not success, need to clean up
+		for (; i >= 0; i--) {
+			sprintf(name, "%sFile%03d", start, i);
+			if (uffs_remove(name) < 0)
+				MSGLN("Delete file %s failed", name);
+		}
+		succ = U_FALSE;
+		goto ext;
+	}
+
+	MSGLN("%d files created.", count);
+
+	// list files
+	dirp = uffs_opendir(start);
+	if (dirp == NULL) {
+		MSGLN("Can't open dir %s !", start);
+		succ = U_FALSE;
+		goto ext;
+	}
+	ent = uffs_readdir(dirp);
+	while (ent && succ) {
+
+		if (!(ent->d_type & FILE_ATTR_DIR) &&					// not a dir
+			ent->d_namelen == strlen("File000") &&				// check file name length
+			memcmp(ent->d_name, "File", strlen("File")) == 0) {	// file name start with "File"
+			
+			MSGLN("List entry %s", ent->d_name);
+
+			num = strtol(ent->d_name + 4, NULL, 10);
+			if (GBIT(num)) {
+				// file already listed ?
+				MSGLN("File %d listed twice !", ent->d_name);
+				succ = U_FALSE;
+				break;
+			}
+			SBIT(num);
+
+			// check file content
+			sprintf(name, "%s%s", start, ent->d_name);
+			fd = uffs_open(name, UO_RDONLY);
+			if (fd < 0) {
+				MSGLN("Open file %d for read failed !", name);
+			}
+			else {
+				memset(buf, 0, sizeof(buf));
+				num = uffs_read(fd, buf, sizeof(buf));
+				if (num != strlen(name)) {
+					MSGLN("%s Read data length expect %d but got %d !", name, strlen(name), num);
+					succ = U_FALSE;
+				}
+				else {
+					if (memcmp(name, buf, num) != 0) {
+						MSGLN("File %s have wrong content '%s' !", name, buf);
+						succ = U_FALSE;
+					}
+				}
+				uffs_close(fd);
+			}
+		}
+		ent = uffs_readdir(dirp);
+	}
+	uffs_closedir(dirp);
+
+	// check absent files
+	for (i = 0; i < count; i++) {
+		if (GBIT(i) == 0) {
+			sprintf(name, "%sFile%03d", start, i);
+			MSGLN("File %s not listed !", name);
+			succ = U_FALSE;
+		}
+	}
+	
+	// delete files if pass the test
+	for (i = 0; succ && i < count; i++) {
+		sprintf(name, "%sFile%03d", start, i);
+		if (uffs_remove(name) < 0) {
+			MSGLN("Delete file %s failed", name);
+			succ = U_FALSE;
+		}
+	}
+
+ext:
+	MSGLN("Populate files test %s !", succ ? "SUCC" : "FAILED");
+	return succ ? 0 : -1;
+
+}
+
+/**
+ * Open <file> with <oflag>, save fd to $1
+ *
+ *		t_open <oflag> <file>
+ */
+static int cmd_topen(int argc, char *argv[])
+{
+	int fd;
+	const char *name;
+	char *p;
+	int oflag = 0;
+
+	CHK_ARGC(3, 3);
+
+	name = argv[2];
+	p = argv[1];
+	while(*p) {
+		switch(*p++) {
+		case 'a':
+			oflag |= UO_APPEND;
+			break;
+		case 'c':
+			oflag |= UO_CREATE;
+			break;
+		case 't':
+			oflag |= UO_TRUNC;
+			break;
+		case 'w':
+			oflag |= UO_RDWR;
+			break;
+		case 'r':
+			oflag |= UO_RDONLY;
+			break;
+		}
+	}
+
+	fd = uffs_open(name, oflag);
+
+	if (fd >= 0) {
+		cli_env_set('1', fd);
+		return 0;
+	}
+	else {
+		return -1;
+	}
+}
+
+/**
+ * seek file pointer
+ *	t_seek <fd> <offset> [<origin>]
+ * if success, $1 = file position after seek
+ */
+static int cmd_tseek(int argc, char *argv[])
+{
+	int origin = USEEK_SET;
+	int offset;
+	int fd;
+	int ret;
+
+	CHK_ARGC(3, 4);
+
+	if (sscanf(argv[1], "%d", &fd) != 1 ||
+		sscanf(argv[2], "%d", &offset) != 1)
+	{
+		return CLI_INVALID_ARG;
+	}
+
+	if (argc > 3) {
+		switch(argv[3][0]) {
+		case 's':
+			origin = USEEK_SET;
+			break;
+		case 'c':
+			origin = USEEK_CUR;
+			break;
+		case 'e':
+			origin = USEEK_END;
+			break;
+		default:
+			return CLI_INVALID_ARG;
+		}
+	}
+
+	ret = uffs_seek(fd, offset, origin);
+	if (ret >= 0) {
+		cli_env_set('1', ret);
+		return 0;
+	}
+	else {
+		return -1;
+	}
+}
+
+/**
+ * close file
+ *	t_close <fd>
+ */
+static int cmd_tclose(int argc, char *argv[])
+{
+	int fd;
+
+	CHK_ARGC(2, 2);
+
+	if (sscanf(argv[1], "%d", &fd) == 1) {
+		return uffs_close(fd);
+	}
+	else
+		return -1;
+}
+
+/**
+ * write file
+ *	t_write <fd> <txt> [..]
+ */
+static int cmd_twrite(int argc, char *argv[])
+{
+	int fd;
+	int i, len = 0;
+	int ret = 0;
+
+	CHK_ARGC(3, 0);
+	if (sscanf(argv[1], "%d", &fd) != 1) {
+		return -1;
+	}
+	else {
+		for (i = 2; i < argc; i++) {
+			len = strlen(argv[i]);
+			if (uffs_write(fd, argv[i], len) != len) {
+				ret = -1;
+				break;
+			}
+		}
+	}
+
+	if (ret == 0)
+		cli_env_set('1', len);
+
+	return ret;
+}
+
+/**
+ * read and check seq file
+ *	t_check_seq <fd> <size>
+ */
+static int cmd_tcheck_seq(int argc, char *argv[])
+{
+	int fd;
+	int len, size;
+	int ret = 0, r_ret = 0;
+	long pos;
+	u8 buf[MAX_TEST_BUF_LEN];
+	int i;
+	u8 x;
+
+	CHK_ARGC(3, 3);
+
+	if (sscanf(argv[1], "%d", &fd) != 1) {
+		return -1;
+	}
+
+	if (sscanf(argv[2], "%d", &len) != 1) {
+		return -1;
+	}
+
+	pos = uffs_tell(fd);
+	while (len > 0) {
+		size = (len > sizeof(buf) ? sizeof(buf) : len);
+		if ((r_ret = uffs_read(fd, buf, size)) < 0) {
+			MSGLN("Read fail! fd = %d, size = %d, pos = %ld", fd, size, pos);
+			ret = -1;
+			break;
+		}
+
+		// check seq
+		for (i = 0; i < r_ret; i++) {
+			x = (pos + SEQ_INIT + i) % SEQ_MOD_LEN;
+			if (buf[i] != x) {
+				MSGLN("Check fail! fd = %d, pos = %ld (expect 0x%02x but 0x%02x)\n", fd, pos + i, x, buf[i]);
+				ret = -1;
+				break;
+			}
+		}
+
+		if (ret < 0)
+			break;
+
+		len -= r_ret;
+		pos += r_ret;
+	}
+
+	return ret;
+}
+
+
+
+/**
+ * write random seq to file
+ *	t_write_seq <fd> <size>
+ */
+static int cmd_twrite_seq(int argc, char *argv[])
+{
+	int fd;
+	int len = 0, size = 0;
+	long pos = 0;
+	int ret = 0, w_ret = 0;
+	u8 buf[MAX_TEST_BUF_LEN];
+
+	CHK_ARGC(3, 3);
+	if (sscanf(argv[1], "%d", &fd) != 1) {
+		return -1;
+	}
+
+	if (sscanf(argv[2], "%d", &len) != 1) {
+		return -1;
+	}
+
+	pos = uffs_tell(fd);
+	while (len > 0) {
+		size = (len < sizeof(buf) ? len : sizeof(buf));
+		memcp_seq(buf, size, pos);
+		if ((w_ret = uffs_write(fd, buf, size)) < 0) {
+			MSGLN("write fail! fd = %d, size = %d, pos = %ld", fd, size, pos);
+			ret = -1;
+			break;
+		}
+		pos += w_ret;
+		len -= w_ret;
+	}
+
+	if (ret == 0)
+		cli_env_set('1', len);
+
+	return ret;
+}
+
+
+/**
+ * read and check file
+ *	t_read <fd> <txt>
+ */
+static int cmd_tread(int argc, char *argv[])
+{
+	int fd;
+	int len, n;
+	int ret = 0;
+	char buf[64];
+	char *p;
+
+	CHK_ARGC(3, 3);
+
+	if (sscanf(argv[1], "%d", &fd) != 1) {
+		return -1;
+	}
+	else {
+		len = strlen(argv[2]);
+		n = 0;
+		p = argv[2];
+		while (n < len) {
+			n = (len > sizeof(buf) ? sizeof(buf) : len);
+			if (uffs_read(fd, buf, n) != n ||
+				memcmp(buf, p, n) != 0) {
+				ret = -1;
+				break;
+			}
+			len -= n;
+			p += n;
+		}
+	}
+
+	return ret;
+}
+
+
+static void do_dump_page(uffs_Device *dev, uffs_Buf *buf)
+{
+	int i, j;
+	const int line = 16;
+	struct uffs_MiniHeaderSt *header = (struct uffs_MiniHeaderSt *)buf->header;
+	MSG("  header.status = %d\n", header->status);
+	if (header->status != 0xFF) {
+		for (i = 0; i < 64; i += line) {
+			MSG("    ");
+			for (j = 0; j < line; j++)
+				MSG("%02X ", buf->header[i+j]);
+			MSG("\n");
+		}
+		MSG("\n");
+	}
+}
+
+static void do_dump_tag(uffs_Device *dev, uffs_Tags *tag)
+{
+	MSG("  tag sealed: %s\n", TAG_IS_SEALED(tag) ? "yes" : "no");
+	if (TAG_IS_GOOD(tag)) {
+		if (TAG_IS_DIRTY(tag)) {
+			MSG("    block_ts = %d\n", tag->s.block_ts);
+			MSG("    type = %d\n", tag->s.type);
+			MSG("    dirty = %d\n", tag->s.dirty);
+			MSG("    page_id = %d\n", tag->s.page_id);
+			MSG("    serial = %d\n", tag->s.serial);
+			MSG("    parent = %d\n", tag->s.parent);
+			MSG("    data_len = %d\n", tag->s.data_len);
+		}
+		else {
+			MSG("  tag is GOOD but NOT DIRTY !!!???\n");
+		}
+	}
+	else if (TAG_IS_SEALED(tag)) {
+		MSG(" tag is INVALID\n");
+	}
+}
+
+static void do_dump_device(uffs_Device *dev)
+{
+	URET ret;
+	int block, page;
+	uffs_Tags tag;
+	uffs_Buf *buf;
+
+	buf = uffs_BufClone(dev, NULL);
+	if (buf == NULL) {
+		MSGLN("Can't clone buf");
+		return;
+	}
+
+	for (block = dev->par.start; block <= dev->par.end; block++) {
+		MSG("---- block %d ----\n", block);
+		for (page = 0; page < dev->attr->pages_per_block; page++) {
+			MSG("  == page %d ==\n", page);
+			ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
+			if (UFFS_FLASH_HAVE_ERR(ret)) {
+				MSG(" !!! Read page failed, ret = %d !!!\n", ret);
+			}
+			else {
+				do_dump_page(dev, buf);
+				if (buf->header[0] != 0xFF) {
+					ret = uffs_FlashReadPageTag(dev, block, page, &tag);
+					if (UFFS_FLASH_HAVE_ERR(ret)) {
+						MSG(" !!! Read TAG failed, ret = %d !!!\n", ret);
+					}
+					else {
+						do_dump_tag(dev, &tag);
+					}
+				}
+			}
+		}
+	}
+	uffs_BufFreeClone(dev, buf);
+}
+
+static int cmd_dump(int argc, char *argv[])
+{
+	const char *mount = "/";
+	uffs_Device *dev;
+
+	if (argc > 1) {
+		mount = argv[1];
+	}
+
+	MSGLN("Dumping %s ... ", mount);
+
+	dev = uffs_GetDeviceFromMountPoint(mount);
+	if (dev == NULL) {
+		MSGLN("Can't get device from mount point.");
+	}
+	else {
+		do_dump_device(dev);
+		uffs_PutDevice(dev);
+	}
+
+	return 0;
+}
+
+static int cmd_apisrv(int argc, char *argv[])
+{
+	return api_server_start();
+}
+
+static const struct cli_command test_cmds[] = 
+{
+    { cmd_t1,					"t1",			"<name>",			"test 1" },
+    { cmd_t2,					"t2",			NULL,				"test 2" },
+    { cmd_t3,					"t3",			"<name> [<noecc>]",	"test 3" },
+    { cmd_t4,					"t4",			NULL,				"test 4" },
+    { cmd_t5,					"t5",			"<name>",			"test 5" },
+    { cmd_TestPageReadWrite,	"t_pgrw",		NULL,				"test page read/write" },
+    { cmd_TestFormat,			"t_format",		NULL,				"test format file system" },
+	{ cmd_TestPopulateFiles,	"t_pfs",		"[<start> [<n>]]",	"test populate <n> files under <start>" },
+	{ cmd_VerifyFile,			"t_vf",			"<file> [<noecc>]", "verify file" },
+
+	{ cmd_topen,				"t_open",		"<oflg> <file>",	"open file, fd save to $1", },
+	{ cmd_tread,				"t_read",		"<fd> <txt>",		"read <fd> and check against <txt>", },
+	{ cmd_tcheck_seq,			"t_check_seq",	"<fd> <size>",		"read seq file <fd> and check", },
+	{ cmd_twrite,				"t_write",		"<fd> <txt> [...]",	"write <fd>", },
+	{ cmd_twrite_seq,			"t_write_seq",	"<fd> <size>",	"write seq file <fd>", },
+	{ cmd_tseek,				"t_seek",		"<fd> <offset> [<origin>]",	"seek <fd> file pointer to <offset> from <origin>", },
+	{ cmd_tclose,				"t_close",		"<fd>",				"close <fd>", },
+	{ cmd_dump,					"dump",			"<mount>",			"dump <mount>", },
+
+	{ cmd_apisrv,				"apisrv",		NULL,				"start API test server", },
+
+    { NULL, NULL, NULL, NULL }
+};
+
+static struct cli_commandset test_cmdset = {
+	test_cmds,
+};
+
+struct cli_commandset * get_test_cmds()
+{
+	return &test_cmdset;
+};
+
+
+

+ 113 - 0
src/emu/uffs_fileem.c

@@ -0,0 +1,113 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem.c
+ * \brief emulate uffs file system
+ * \author Ricky Zheng, created 9th May, 2005
+ */
+  
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs_fileem.h"
+
+#define PFX "femu: "
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+
+static struct uffs_StorageAttrSt g_femu_storage = {0};
+
+static struct uffs_FileEmuSt g_femu_private = {0};
+
+
+struct uffs_StorageAttrSt * femu_GetStorage()
+{
+	return &g_femu_storage;
+}
+
+struct uffs_FileEmuSt *femu_GetPrivate()
+{
+	return &g_femu_private;
+}
+
+URET femu_InitDevice(uffs_Device *dev)
+{
+	uffs_FileEmu *emu = femu_GetPrivate();
+
+	// setup device storage attr and private data structure.
+	// all femu partition share one storage attribute
+
+	dev->attr = femu_GetStorage();
+	dev->attr->_private = (void *) emu;
+
+	// setup flash driver operations, according to the ecc option.
+	switch(dev->attr->ecc_opt) {
+		case UFFS_ECC_NONE:
+		case UFFS_ECC_SOFT:
+			dev->ops = &g_femu_ops_ecc_soft;
+			break;
+		case UFFS_ECC_HW:
+			dev->ops = &g_femu_ops_ecc_hw;
+			break;
+		case UFFS_ECC_HW_AUTO:
+			dev->ops = &g_femu_ops_ecc_hw_auto;
+			break;
+		default:
+			break;
+	}
+
+#ifdef UFFS_FEMU_ENABLE_INJECTION
+	// setup wrap functions, for inject ECC errors, etc.
+	// check wrap_inited so that multiple devices can share the same driver
+	if (!emu->wrap_inited) {
+		femu_setup_wrapper_functions(dev);
+		emu->wrap_inited = U_TRUE;
+	}
+#endif
+
+	return U_SUCC;
+}
+
+/* Nothing to do here */
+URET femu_ReleaseDevice(uffs_Device *dev)
+{
+	return U_SUCC;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////

+ 88 - 0
src/emu/uffs_fileem.h

@@ -0,0 +1,88 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_fileem.h
+ * \brief Emulate NAND flash with host file.
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_FILEEM_H_
+#define _UFFS_FILEEM_H_
+
+#include "uffs/uffs_device.h"
+
+#define UFFS_FEMU_FILE_NAME		"uffsemfile.bin"
+
+#define UFFS_FEMU_MAX_BLOCKS	(1024 * 16)		// maximum 16K blocks
+
+#define UFFS_FEMU_ENABLE_INJECTION		// enable bad block & ecc error injection
+
+extern struct uffs_FlashOpsSt g_femu_ops_ecc_soft;		// for software ECC or no ECC.
+extern struct uffs_FlashOpsSt g_femu_ops_ecc_hw;		// for hardware ECC
+extern struct uffs_FlashOpsSt g_femu_ops_ecc_hw_auto;	// for auto hardware ECC
+
+#define PAGE_DATA_WRITE_COUNT_LIMIT		1
+#define PAGE_SPARE_WRITE_COUNT_LIMIT	1
+
+typedef struct uffs_FileEmuSt {
+	int initCount;
+	FILE *fp;
+	FILE *dump_fp;
+	u8 *em_monitor_page;		// page write monitor
+	u8 * em_monitor_spare;		// spare write monitor
+	u32 *em_monitor_block;		// block erease monitor
+	const char *emu_filename;
+#ifdef UFFS_FEMU_ENABLE_INJECTION
+	struct uffs_FlashOpsSt ops_orig;
+	UBOOL wrap_inited;
+#endif
+} uffs_FileEmu;
+
+/* file emulator device init/release entry */
+URET femu_InitDevice(uffs_Device *dev);
+URET femu_ReleaseDevice(uffs_Device *dev);
+
+struct uffs_StorageAttrSt * femu_GetStorage(void);
+struct uffs_FileEmuSt * femu_GetPrivate(void);
+
+#ifdef UFFS_FEMU_ENABLE_INJECTION
+void femu_setup_wrapper_functions(uffs_Device *dev);
+#endif
+
+/* internal used functions, shared by all ecc option implementations */
+int femu_InitFlash(uffs_Device *dev);
+int femu_ReleaseFlash(uffs_Device *dev);
+int femu_EraseBlock(uffs_Device *dev, u32 blockNumber);
+
+#endif
+

+ 236 - 0
src/emu/uffs_fileem_ecc_hw.c

@@ -0,0 +1,236 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem_ecc_hw.c
+ * \brief emulate uffs file system for hardware ECC.
+ *
+ *	 In this emulator, we call 'uffs_FlashMakeSpare()' to do the layout job
+ *	 and call 'uffs_EccMake()' to calculate ECC.
+ *
+ * \author Ricky Zheng @ Oct, 2010
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs_fileem.h"
+#include "uffs/uffs_ecc.h"
+
+#define PFX "femu: "
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+static int femu_hw_WritePageWithLayout(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts)
+{
+	int written;
+	int abs_page;
+	int full_page_size;
+	uffs_FileEmu *emu;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	u8 spare[UFFS_MAX_SPARE_SIZE];
+	u8 ecc_buf[UFFS_MAX_ECC_SIZE];
+	int spare_len;
+
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto err;
+	}
+
+	abs_page = attr->pages_per_block * block + page;
+	full_page_size = attr->page_data_size + attr->spare_size;
+
+	if (data && data_len > 0) {
+		if (data_len > attr->page_data_size)
+			goto err;
+
+		emu->em_monitor_page[abs_page]++;
+		if (emu->em_monitor_page[abs_page] > PAGE_DATA_WRITE_COUNT_LIMIT) {
+			MSG("Warrning: block %d page %d exceed it's maximum write time!", block, page);
+			goto err;
+		}
+		
+		fseek(emu->fp, abs_page * full_page_size, SEEK_SET);
+
+		written = fwrite(data, 1, data_len, emu->fp);
+		
+		if (written != data_len) {
+			MSG("write page I/O error ?");
+			goto err;
+		}
+
+		dev->st.page_write_count++;
+		dev->st.io_write += written;
+
+	}
+
+	if (ts) {
+
+		emu->em_monitor_spare[abs_page]++;
+		if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
+			MSG("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page);
+			goto err;
+		}
+
+		if (!uffs_Assert(data != NULL, "BUG: Write spare without data ?"))
+			goto err;
+
+		uffs_EccMake(data, data_len, ecc_buf);
+		uffs_FlashMakeSpare(dev, ts, ecc_buf, spare);
+		spare_len = dev->mem.spare_data_size;
+		
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET);
+		written = fwrite(spare, 1, spare_len, emu->fp);
+		if (written != spare_len) {
+			MSG("write spare I/O error ?");
+			goto err;
+		}
+
+		dev->st.spare_write_count++;
+		dev->st.io_write += written;
+	}
+
+	if (data == NULL && ts == NULL) {
+		// mark bad block
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET);
+		written = fwrite("\0", 1, 1, emu->fp);
+		if (written != 1) {
+			MSG("write bad block mark I/O error ?");
+			goto err;
+		}
+		dev->st.io_write++;
+	}
+
+	fflush(emu->fp);
+	return UFFS_FLASH_NO_ERR;
+err:
+	fflush(emu->fp);
+	return UFFS_FLASH_IO_ERR;
+}
+
+
+static URET femu_hw_ReadPageWithLayout(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
+									uffs_TagStore *ts, u8 *ecc_store)
+{
+	int nread;
+	uffs_FileEmu *emu;
+	int abs_page;
+	int full_page_size;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	unsigned char status;
+	u8 spare[UFFS_MAX_SPARE_SIZE];
+	int spare_len;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto err;
+	}
+
+	abs_page = attr->pages_per_block * block + page;
+	full_page_size = attr->page_data_size + attr->spare_size;
+
+	if (data && data_len > 0) {
+		if (data_len > attr->page_data_size)
+			goto err;
+
+		fseek(emu->fp, abs_page * full_page_size, SEEK_SET);
+		nread = fread(data, 1, data_len, emu->fp);
+
+		if (nread != data_len) {
+			MSG("read page I/O error ?");
+			goto err;
+		}
+		dev->st.io_read += nread;
+		dev->st.page_read_count++;
+
+		if (ecc) {
+			// calculate ECC for data
+			uffs_EccMake(data, data_len, ecc);
+		}
+	}
+
+	if (ts) {
+
+		spare_len = dev->mem.spare_data_size;
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET);
+		nread = fread(spare, 1, spare_len, emu->fp);
+
+		if (nread != spare_len) {
+			MSG("read page spare I/O error ?");
+			goto err;
+		}
+
+		// unload ts and ecc from spare
+		uffs_FlashUnloadSpare(dev, spare, ts, ecc_store);
+
+		dev->st.io_read += nread;
+		dev->st.spare_read_count++;
+	}
+
+	if (data == NULL && ts == NULL) {
+		// read bad block mark
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET);
+		nread = fread(&status, 1, 1, emu->fp);
+
+		if (nread != 1) {
+			MSG("read badblock mark I/O error ?");
+			goto err;
+		}
+		dev->st.io_read++;
+
+		return status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
+	}
+
+	return UFFS_FLASH_NO_ERR;
+err:
+	return UFFS_FLASH_IO_ERR;
+}
+
+
+uffs_FlashOps g_femu_ops_ecc_hw = {
+	femu_InitFlash,				// InitFlash()
+	femu_ReleaseFlash,			// ReleaseFlash()
+	NULL,						// ReadPage()
+	femu_hw_ReadPageWithLayout,	// ReadPageWithLayout()
+	NULL,						// WritePage()
+	femu_hw_WritePageWithLayout,// WritePageWithLayout()
+	NULL,						// IsBadBlock(), let UFFS take care of it.
+	NULL,						// MarkBadBlock(), let UFFS take care of it.
+	femu_EraseBlock,			// EraseBlock()
+};

+ 356 - 0
src/emu/uffs_fileem_ecc_hw_auto.c

@@ -0,0 +1,356 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem_ecc_hw_auto.c
+ *
+ * \brief Emulate uffs file system for auto hardware ECC or RS error collection.
+ *
+ *    This emulator emulate LPC32x0 MLC NAND controller which generate 10 bytes
+ *    Reed-Solomon error correction code (RS-ECC) for every 518 bytes data.
+ *
+ *    For small page MLC have 16 bytes spare area leves only 6 bytes for 'meta-data',
+ *    no enough room for UFFS's 8 bytes tag and bad block mark. For this reason,
+ *    we adjust page data/spare boundary to 508/20.
+ *
+ *    This emulator does not calculate real RS-ECC code, instead, we use software ECC
+ *    to calculate 6 bytes ECC code, so this solution does not have the same error
+ *    correcting cabability of RS-ECC.
+ *
+ *    Note: the MLC controller strictly require sequencial access to serial data buffer.
+ *
+ * \author Ricky Zheng @ Oct, 2010
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_ecc.h"
+#include "uffs_fileem.h"
+
+#define PFX "femu: "
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+#define RS_ECC_SIZE			10
+#define PAGE_DATA_SIZE		508
+#define PAGE_SPARE_SIZE		20
+#define PAGE_FULL_SIZE		(PAGE_DATA_SIZE + PAGE_SPARE_SIZE)
+static u8 g_sdata_buf[PAGE_FULL_SIZE];	// emulating LPC32x0's 528-bytes serial data buffer
+
+static int g_sdata_buf_pointer = 0;
+
+static void start_sdata_access()
+{
+	g_sdata_buf_pointer = 0;
+}
+
+static void feed_sdata(const u8 *data, int len)
+{
+	if (!uffs_Assert(g_sdata_buf_pointer + len <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!"))
+		return;
+
+	if (data)
+		memcpy(g_sdata_buf + g_sdata_buf_pointer, data, len);
+	g_sdata_buf_pointer += len;
+}
+
+static void feed_sdata_constant(u8 val, int num)
+{
+	if (!uffs_Assert(g_sdata_buf_pointer + num <= sizeof(g_sdata_buf), "BUG: Serial Data Buffer overflow !!"))
+		return;
+
+	memset(g_sdata_buf + g_sdata_buf_pointer, val, num);
+	g_sdata_buf_pointer += num;
+}
+
+static void drain_sdata(u8 *data, int len)
+{
+	if (!uffs_Assert( (int)sizeof(g_sdata_buf) - g_sdata_buf_pointer >= len, "BUG: Serial Data Buffer overdrain !!"))
+		return;
+
+	if (data)
+		memcpy(data, g_sdata_buf + g_sdata_buf_pointer, len);
+	g_sdata_buf_pointer += len;
+}
+
+static int load_sdata(uffs_Device *dev, int block, int page)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	int abs_page;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	int nread;
+	int ret;
+	u8 ecc_buf[RS_ECC_SIZE];
+	u8 *ecc_store;
+
+	abs_page = attr->pages_per_block * block + page;
+
+	fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET);
+	nread = fread(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp);
+	g_sdata_buf_pointer = 0;
+
+	ret = ((nread == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR);
+
+	if (ret == UFFS_FLASH_NO_ERR) {
+
+		// Perform ECC check & correction
+		// In the real world, this is done by MLC controller hardware
+		memset(ecc_buf, 0xFF, RS_ECC_SIZE);
+		uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf);
+
+		ecc_store = g_sdata_buf + PAGE_FULL_SIZE - RS_ECC_SIZE;
+
+		ret = uffs_EccCorrect(g_sdata_buf, attr->page_data_size, ecc_store, ecc_buf);
+
+		ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
+				(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));
+		
+	}
+
+	return ret;
+}
+
+static int program_sdata(uffs_Device *dev, int block, int page)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	int abs_page;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	u8 ecc_buf[RS_ECC_SIZE];
+	int writtern = 0;
+
+	// In the real world, MLC controller will generate RS-ECC code in serial data buffer
+	// and might start auto programing NAND flash. Here, we use software ECC to emulate RS-ECC.
+	memset(ecc_buf, 0xFF, sizeof(ecc_buf));
+	uffs_EccMake(g_sdata_buf, attr->page_data_size, ecc_buf);
+	feed_sdata(ecc_buf, RS_ECC_SIZE);
+
+	if (!uffs_Assert(g_sdata_buf_pointer == PAGE_FULL_SIZE, "Serial Data Buffer is not fully filled !!"))
+		goto ext;
+
+	abs_page = attr->pages_per_block * block + page;
+
+	fseek(emu->fp, abs_page * PAGE_FULL_SIZE, SEEK_SET);
+	writtern = fwrite(g_sdata_buf, 1, PAGE_FULL_SIZE, emu->fp);
+ext:
+	return (writtern == PAGE_FULL_SIZE) ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
+}
+
+
+static int femu_hw_auto_InitFlash(uffs_Device *dev)
+{
+	struct uffs_StorageAttrSt *attr = dev->attr;
+
+	// now this is a good chance to adjust page data/spare boundary
+	if (attr->page_data_size + attr->spare_size != PAGE_FULL_SIZE) {
+		MSGLN("This emulator emulates only for page size %d bytes !", PAGE_FULL_SIZE);
+		return -1;
+	}
+	if (attr->spare_size < PAGE_SPARE_SIZE) {
+		attr->page_data_size -= (PAGE_SPARE_SIZE - attr->spare_size);
+		attr->spare_size = PAGE_SPARE_SIZE;
+		MSGLN("Adjust page data/spare boundary to %d/%d", attr->page_data_size, attr->spare_size);
+	}
+
+	// and fix ECC size
+	attr->ecc_size = RS_ECC_SIZE;
+	MSGLN("Adjust ECC size to %d bytes", attr->ecc_size);
+	
+	return femu_InitFlash(dev);
+}
+
+
+static int femu_hw_auto_WritePageWithLayout(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts)
+{
+	int abs_page;
+	uffs_FileEmu *emu;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	u8 spare[PAGE_SPARE_SIZE];
+	int ret = UFFS_FLASH_IO_ERR;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto err;
+	}
+
+	abs_page = attr->pages_per_block * block + page;
+
+	start_sdata_access();
+
+	dev->st.page_write_count++;
+	dev->st.spare_write_count++;
+	dev->st.io_write += PAGE_FULL_SIZE;
+
+	if (data || ts) {
+		// normal page write
+		if (data && data_len > 0) {
+			if (data_len > attr->page_data_size)
+				goto err;
+
+			emu->em_monitor_page[abs_page]++;
+			if (emu->em_monitor_page[abs_page] > PAGE_DATA_WRITE_COUNT_LIMIT) {
+				MSGLN("Warrning: block %d page %d exceed it's maximum write time!", block, page);
+				goto err;
+			}
+			
+			// Copy data to serial data buffer
+			feed_sdata(data, data_len);
+
+			// Pad the rest data as 0xFF
+			feed_sdata_constant(0xFF, attr->page_data_size - data_len);
+
+		}
+		else {
+			// We still need to feed data to serial data buffer to make MLC controller happy
+			// The current UFFS won't write ts only, so we'll never run to here.
+			feed_sdata_constant(0xFF, attr->page_data_size);
+		}
+
+		if (ts) {
+
+			emu->em_monitor_spare[abs_page]++;
+			if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
+				MSGLN("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page);
+				goto err;
+			}
+
+			memset(spare, 0xFF, sizeof(spare));
+			uffs_FlashMakeSpare(dev, ts, NULL, spare);	// do not pack ECC, as MLC controller will
+														// automatically write RS-ECC to the latest 10 bytes.
+
+			// feed spare data to serial data buffer
+			feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);			
+		}
+	}
+	else {
+		// mark bad block
+
+		// feed data to serial data buffer to make MLC controller happy
+		feed_sdata_constant(0xFF, attr->page_data_size);
+
+		memset(spare, 0xFF, sizeof(spare));
+		spare[attr->block_status_offs] = 0;
+
+		// feed spare data to serial data buffer
+		feed_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
+
+		dev->st.io_write++;
+	}
+
+	// now, program serial data buffer to NAND flash
+	ret = program_sdata(dev, block, page);
+
+	fflush(emu->fp);
+	return ret;
+err:
+	fflush(emu->fp);
+	return ret;
+}
+
+
+static URET femu_hw_auto_ReadPageWithLayout(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
+									uffs_TagStore *ts, u8 *ecc_store)
+{
+	uffs_FileEmu *emu;
+	int abs_page;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	unsigned char status;
+	u8 spare[PAGE_SPARE_SIZE];
+	int ret = UFFS_FLASH_IO_ERR;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto ext;
+	}
+
+	abs_page = attr->pages_per_block * block + page;
+
+	// now load full page to serial data buffer
+	ret = load_sdata(dev, block, page);
+	if (ret != UFFS_FLASH_NO_ERR)
+		goto ext;
+
+	start_sdata_access();
+
+	dev->st.io_read += PAGE_FULL_SIZE;
+	dev->st.page_read_count++;
+	dev->st.spare_read_count++;
+
+	if (data || ts) {
+
+		if (data && data_len > 0) {
+			if (data_len > attr->page_data_size)
+				goto ext;
+
+			drain_sdata(data, data_len);
+		}
+
+		if (ts) {
+			if (g_sdata_buf_pointer < attr->page_data_size)
+				drain_sdata(NULL, attr->page_data_size - g_sdata_buf_pointer);
+
+			drain_sdata(spare, PAGE_SPARE_SIZE - RS_ECC_SIZE);
+
+			// unload ts from spare
+			uffs_FlashUnloadSpare(dev, spare, ts, NULL);
+		}
+	}
+	else {
+		// read bad block mark
+		drain_sdata(NULL, attr->page_data_size + attr->block_status_offs - 1);
+		drain_sdata(&status, 1);
+
+		ret = (status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK);
+	}
+
+ext:
+	return ret;
+}
+
+
+uffs_FlashOps g_femu_ops_ecc_hw_auto = {
+	femu_hw_auto_InitFlash,			// InitFlash()
+	femu_ReleaseFlash,			// ReleaseFlash()
+	NULL,					// ReadPage()
+	femu_hw_auto_ReadPageWithLayout,	// ReadPageWithLayout()
+	NULL,					// WritePage()
+	femu_hw_auto_WritePageWithLayout,	// WritePageWithLayout()
+	NULL,					// IsBadBlock(), let UFFS take care of it.
+	NULL,					// MarkBadBlock(), let UFFS take care of it.
+	femu_EraseBlock,			// EraseBlock()
+};

+ 212 - 0
src/emu/uffs_fileem_ecc_soft.c

@@ -0,0 +1,212 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2010 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem_ecc_soft.c
+ * \brief emulate uffs file system for software ECC
+ * \author Ricky Zheng @ Oct, 2010
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_flash.h"
+#include "uffs_fileem.h"
+
+#define PFX "femu: "
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+static int femu_WritePage(uffs_Device *dev, u32 block, u32 page_num,
+							const u8 *data, int data_len, const u8 *spare, int spare_len)
+{
+	int written;
+	int abs_page;
+	int full_page_size;
+	uffs_FileEmu *emu;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto err;
+	}
+
+	abs_page = attr->pages_per_block * block + page_num;
+	full_page_size = attr->page_data_size + attr->spare_size;
+
+	if (data && data_len > 0) {
+		if (data_len > attr->page_data_size)
+			goto err;
+
+		emu->em_monitor_page[abs_page]++;
+		if (emu->em_monitor_page[abs_page] > PAGE_DATA_WRITE_COUNT_LIMIT) {
+			MSGLN("Warrning: block %d page %d exceed it's maximum write time!", block, page_num);
+			goto err;
+		}
+		
+		fseek(emu->fp, abs_page * full_page_size, SEEK_SET);
+
+		written = fwrite(data, 1, data_len, emu->fp);
+		
+		if (written != data_len) {
+			MSGLN("write page I/O error ?");
+			goto err;
+		}
+
+		dev->st.page_write_count++;
+		dev->st.io_write += written;
+	}
+
+	if (spare && spare_len > 0) {
+		if (spare_len > attr->spare_size)
+			goto err;
+
+		emu->em_monitor_spare[abs_page]++;
+		if (emu->em_monitor_spare[abs_page] > PAGE_SPARE_WRITE_COUNT_LIMIT) {
+			MSGLN("Warrning: block %d page %d (spare) exceed it's maximum write time!", block, page_num);
+			goto err;
+		}
+		
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET);
+		written = fwrite(spare, 1, spare_len, emu->fp);
+		if (written != spare_len) {
+			MSGLN("write spare I/O error ?");
+			goto err;
+		}
+
+		dev->st.spare_write_count++;
+		dev->st.io_write += written;
+	}
+
+	if (data == NULL && spare == NULL) {
+		// mark bad block
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET);
+		written = fwrite("\0", 1, 1, emu->fp);
+		if (written != 1) {
+			MSGLN("write bad block mark I/O error ?");
+			goto err;
+		}
+		dev->st.io_write++;
+	}
+
+	fflush(emu->fp);
+	return UFFS_FLASH_NO_ERR;
+err:
+	fflush(emu->fp);
+	return UFFS_FLASH_IO_ERR;
+}
+
+
+static URET femu_ReadPage(uffs_Device *dev, u32 block, u32 page_num, u8 *data, int data_len, u8 *ecc,
+							u8 *spare, int spare_len)
+{
+	int nread;
+	uffs_FileEmu *emu;
+	int abs_page;
+	int full_page_size;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	unsigned char status;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (!emu || !(emu->fp)) {
+		goto err;
+	}
+
+	abs_page = attr->pages_per_block * block + page_num;
+	full_page_size = attr->page_data_size + attr->spare_size;
+
+	if (data && data_len > 0) {
+		if (data_len > attr->page_data_size)
+			goto err;
+
+		fseek(emu->fp, abs_page * full_page_size, SEEK_SET);
+		nread = fread(data, 1, data_len, emu->fp);
+
+		if (nread != data_len) {
+			MSGLN("read page I/O error ?");
+			goto err;
+		}
+		dev->st.io_read += nread;
+		dev->st.page_read_count++;
+	}
+
+	if (spare && spare_len > 0) {
+		if (spare_len > attr->spare_size)
+			goto err;
+
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size, SEEK_SET);
+		nread = fread(spare, 1, spare_len, emu->fp);
+
+		if (nread != spare_len) {
+			MSGLN("read page spare I/O error ?");
+			goto err;
+		}
+		dev->st.io_read += nread;
+		dev->st.spare_read_count++;
+	}
+
+	if (data == NULL && spare == NULL) {
+		// read bad block mark
+		fseek(emu->fp, abs_page * full_page_size + attr->page_data_size + attr->block_status_offs, SEEK_SET);
+		nread = fread(&status, 1, 1, emu->fp);
+
+		if (nread != 1) {
+			MSGLN("read badblock mark I/O error ?");
+			goto err;
+		}
+		dev->st.io_read++;
+
+		return status == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
+	}
+
+	return UFFS_FLASH_NO_ERR;
+err:
+	return UFFS_FLASH_IO_ERR;
+}
+
+
+uffs_FlashOps g_femu_ops_ecc_soft = {
+	femu_InitFlash,		// InitFlash()
+	femu_ReleaseFlash,	// ReleaseFlash()
+	femu_ReadPage,		// ReadPage()
+	NULL,		        // ReadPageWithLayout
+	femu_WritePage,		// WritePage()
+	NULL,			// WritePageWithLayout
+	NULL,			// IsBadBlock(), let UFFS take care of it.
+	NULL,			// MarkBadBlock(), let UFFS take care of it.
+	femu_EraseBlock,	// EraseBlock()
+};

+ 232 - 0
src/emu/uffs_fileem_share.c

@@ -0,0 +1,232 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem_share.c
+ * \brief emulate uffs file system, shared functions
+ * \author Ricky Zheng, created Nov, 2010
+ */
+  
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs_fileem.h"
+
+#define PFX "femu: "
+
+/****************************************************************/
+/*           Shared flash driver functions:                     */
+/*                                                              */
+/*   femu_InitFlash(), femu_ReleaseFlash(), femu_EraseBlock()   */
+/*                                                              */
+/****************************************************************/
+
+static u8 g_page_buf[UFFS_MAX_PAGE_SIZE + UFFS_MAX_SPARE_SIZE];
+
+/*
+ * Create emulator disk, initialise monitors, inject manufacture bad blocks, etc.
+ *
+ */
+int femu_InitFlash(uffs_Device *dev)
+{
+	int i;
+	int fSize;
+	int written;
+	u8 * p = g_page_buf;
+	uffs_FileEmu *emu;
+
+	struct uffs_StorageAttrSt *attr = dev->attr;
+
+	int full_page_size = attr->page_data_size + attr->spare_size;
+	int total_pages = attr->total_blocks * attr->pages_per_block;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	if (emu->initCount > 0) {
+		emu->initCount++;
+		return 0;
+	}
+
+	if (emu->emu_filename == NULL)
+		emu->emu_filename = UFFS_FEMU_FILE_NAME;
+
+	uffs_Perror(UFFS_MSG_NORMAL,  "femu device init.");
+
+	emu->em_monitor_page = (u8 *) malloc(sizeof(emu->em_monitor_page[0]) * total_pages);
+	if (!emu->em_monitor_page)
+		return -1;
+	emu->em_monitor_spare = (u8 *) malloc(sizeof(emu->em_monitor_spare[0]) * total_pages);
+	if (!emu->em_monitor_spare)
+		return -1;
+
+	emu->em_monitor_block = (u32 *) malloc(sizeof(emu->em_monitor_block[0]) * attr->total_blocks);
+	if (!emu->em_monitor_block)
+		return -1;
+
+	//clear monitor
+	memset(emu->em_monitor_page, 0, sizeof(emu->em_monitor_page[0]) * total_pages);
+	memset(emu->em_monitor_spare, 0, sizeof(emu->em_monitor_spare[0]) * total_pages);
+	memset(emu->em_monitor_block, 0, sizeof(emu->em_monitor_block[0]) * attr->total_blocks);
+
+	emu->fp = fopen(emu->emu_filename, "rb");
+	if (emu->fp == NULL) {
+		emu->fp = fopen(emu->emu_filename, "ab+");
+		if (emu->fp == NULL) {
+			printf(PFX"Failed to create uffs emulation file.");
+			return -1;
+		}
+
+		fseek(emu->fp, 0, SEEK_END);
+		fSize = ftell(emu->fp);
+		
+		if (fSize < total_pages * full_page_size)	{
+			printf("Creating uffs emulation file\n");
+			fseek(emu->fp, 0, SEEK_SET);
+			memset(p, 0xff, full_page_size);
+			for (i = 0; i < total_pages; i++)	{
+				written = fwrite(p, 1, full_page_size, emu->fp);
+				if (written != full_page_size)	{
+					printf("Write failed\n");
+					fclose(emu->fp);
+					emu->fp = NULL;
+					return -1;
+				}
+			}		
+		}
+	}
+
+	fflush(emu->fp);	
+	fclose(emu->fp);
+
+	emu->fp = fopen(emu->emu_filename, "rb+");
+	if (emu->fp == NULL) {
+		printf(PFX"Can't open emulation file.\n");
+		return -1;
+	}
+
+	emu->initCount++;
+
+	return 0;
+}
+
+/*
+ * Release resources
+ */
+int femu_ReleaseFlash(uffs_Device *dev)
+{
+	uffs_FileEmu *emu;
+
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	emu->initCount--;
+
+	if (emu->initCount == 0) {
+
+		uffs_Perror(UFFS_MSG_NORMAL,  "femu device release.");
+
+		if (emu->fp) {
+			fclose(emu->fp);
+			emu->fp = NULL;
+		}
+
+		if (emu->em_monitor_page)
+			free(emu->em_monitor_page);
+		if (emu->em_monitor_spare) 
+			free(emu->em_monitor_spare);
+		if (emu->em_monitor_block)
+			free(emu->em_monitor_block);
+		emu->em_monitor_page = NULL;
+		emu->em_monitor_spare = NULL;
+		emu->em_monitor_block = NULL;
+	}
+
+	return 0;
+}
+
+int femu_EraseBlock(uffs_Device *dev, u32 blockNumber)
+{
+
+	int i;
+	u8 * pg = g_page_buf;
+	int pg_size, pgd_size, sp_size, blks, blk_pgs, blk_size;
+	uffs_FileEmu *emu;
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+	if (!emu || !(emu->fp))
+		goto err;
+
+	pg_size = dev->attr->page_data_size + dev->attr->spare_size;
+	pgd_size = dev->attr->page_data_size;
+	sp_size = dev->attr->spare_size;
+	blk_pgs = dev->attr->pages_per_block;
+	blks = dev->attr->total_blocks;
+	blk_size = dev->attr->page_data_size * dev->attr->pages_per_block;
+	
+	printf("femu: erase block %d\n", blockNumber);
+
+	if ((int)blockNumber >= blks) {
+		printf("Attempt to erase non-existant block %d\n",blockNumber);
+		goto err;
+	}
+	else {
+
+		//clear this block monitors
+		memset(emu->em_monitor_page + (blockNumber * blk_pgs), 
+			0, 
+			blk_pgs * sizeof(u8));
+		memset(emu->em_monitor_spare + (blockNumber * blk_pgs),
+			0,
+			blk_pgs * sizeof(u8));
+
+		emu->em_monitor_block[blockNumber]++;
+		
+		memset(pg, 0xff, (pgd_size + sp_size));
+		
+		fseek(emu->fp, blockNumber * blk_pgs * (pgd_size + sp_size), SEEK_SET);
+		
+		for (i = 0; i < blk_pgs; i++)	{
+			fwrite(pg, 1, (pgd_size + sp_size), emu->fp);
+		}
+
+		fflush(emu->fp);
+		dev->st.block_erase_count++;
+	}
+
+	return UFFS_FLASH_NO_ERR;
+err:
+	return UFFS_FLASH_IO_ERR;
+	
+}
+

+ 311 - 0
src/emu/uffs_fileem_wrap.c

@@ -0,0 +1,311 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fileem_wrap.c
+ *
+ * \brief file emulator wrapper functions for injecting bad blocks or ECC errors.
+ *
+ * \author Ricky Zheng, created Nov, 2010
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs_fileem.h"
+
+#define PFX "femu: "
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+// #define UFFS_FEMU_SHOW_FLASH_IO
+
+#ifdef UFFS_FEMU_ENABLE_INJECTION
+
+struct uffs_FileEmuBitFlip {
+	int block;
+	int page;
+	int offset;
+	u8 mask;
+};
+
+/* simulate bad blocks */
+#define FILEEMU_STOCK_BAD_BLOCKS	{5, 180}	// bad block come from manufacture
+#define FILEEMU_ERASE_BAD_BLOCKS	{100, 150}	// new bad block discovered when erasing
+
+/* simulating bit flip */
+#define FILEEMU_WRITE_BIT_FLIP \
+	{ \
+		{20, 2, 10, 1 << 4},	/* block 20, page 2, offset 10, bit 4 */	\
+		{24, 4, -3, 1 << 2},	/* block 24, page 4, spare offset 3, bit 2*/ \
+		{60, 1, 5, 1 << 3},		/* block 60, page 1, offset 5, bit 3 */ \
+		{66, 1, 15, 1 << 7},	/* block 66, page 1, offset 300, bit 7 */ \
+		{80, 2, 2, 1 << 1},		/* block 80, page 2, offset 2, bit 1 */ \
+		{88, 2, 100, 1 << 5},	/* block 88, page 2, offset 100, bit 5 */ \
+	}
+
+
+static int femu_InitFlash_wrap(uffs_Device *dev);
+
+static int femu_ReadPage_wrap(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
+							u8 *spare, int spare_len);
+static int femu_ReadPageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
+									uffs_TagStore *ts, u8 *ecc_store);
+static int femu_WritePage_wrap(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *spare, int spare_len);
+static int femu_WritePageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, const u8* data, int data_len, const u8 *ecc,
+									const uffs_TagStore *ts);
+static int femu_EraseBlock_wrap(uffs_Device *dev, u32 blockNumber);
+
+
+/////////////////////////////////////////////////////////////////////////////////
+
+void femu_setup_wrapper_functions(uffs_Device *dev)
+{
+	uffs_FileEmu *emu;
+	emu = (uffs_FileEmu *)(dev->attr->_private);
+
+	// setup wrap functions, for inject ECC errors, etc.
+
+	memcpy(&emu->ops_orig, dev->ops, sizeof(struct uffs_FlashOpsSt));
+
+	if (dev->ops->InitFlash)
+		dev->ops->InitFlash = femu_InitFlash_wrap;
+	if (dev->ops->EraseBlock)
+		dev->ops->EraseBlock = femu_EraseBlock_wrap;
+	if (dev->ops->ReadPage)
+		dev->ops->ReadPage = femu_ReadPage_wrap;
+	if (dev->ops->ReadPageWithLayout)
+		dev->ops->ReadPageWithLayout = femu_ReadPageWithLayout_wrap;
+	if (dev->ops->WritePage)
+		dev->ops->WritePage = femu_WritePage_wrap;
+	if (dev->ops->WritePageWithLayout)
+		dev->ops->WritePageWithLayout = femu_WritePageWithLayout_wrap;
+}
+
+static int femu_InitFlash_wrap(uffs_Device *dev)
+{
+	int ret;
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+
+#ifdef FILEEMU_STOCK_BAD_BLOCKS
+	u32 bad_blocks[] = FILEEMU_STOCK_BAD_BLOCKS;
+	int j;
+	u8 x = 0;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	int full_page_size = attr->page_data_size + attr->spare_size;
+	int blk_size = full_page_size * attr->pages_per_block;
+#endif
+
+	if (emu->initCount == 0) {
+		ret = emu->ops_orig.InitFlash(dev);
+#ifdef FILEEMU_STOCK_BAD_BLOCKS
+		if (ret >= 0) {
+			for (j = 0; j < ARRAY_SIZE(bad_blocks); j++) {
+				if (bad_blocks[j] < dev->attr->total_blocks) {
+					printf(" --- manufacture bad block %d ---\n", bad_blocks[j]);
+					fseek(emu->fp, bad_blocks[j] * blk_size + attr->page_data_size + dev->attr->block_status_offs, SEEK_SET);
+					fwrite(&x, 1, 1, emu->fp);
+				}
+			}
+		}
+#endif
+	}
+	else {
+		ret = emu->ops_orig.InitFlash(dev);
+	}
+
+	return ret;
+}
+
+static int femu_ReadPage_wrap(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
+							u8 *spare, int spare_len)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+
+#ifdef UFFS_FEMU_SHOW_FLASH_IO
+	if (data || spare) {
+		MSG(PFX " Read block %d page %d", block, page);
+		if (data)
+			MSG(" DATA[%d]", data_len);
+		if (spare)
+			MSG(" SPARE[%d]", spare_len);
+		MSG(TENDSTR);
+	}
+#endif
+	return emu->ops_orig.ReadPage(dev, block, page, data, data_len, ecc, spare, spare_len);
+}
+
+static int femu_ReadPageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
+									uffs_TagStore *ts, u8 *ecc_store)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+
+#ifdef UFFS_FEMU_SHOW_FLASH_IO
+	if (data || ts) {
+		MSG(PFX " Read block %d page %d", block, page);
+		if (data)
+			MSG(" DATA[%d]", data_len);
+		if (ts)
+			MSG(" TS");
+		MSG(TENDSTR);
+	}
+#endif
+	return emu->ops_orig.ReadPageWithLayout(dev, block, page, data, data_len, ecc, ts, ecc_store);
+}
+
+
+////////////////////// wraper functions ///////////////////////////
+
+static void InjectBitFlip(uffs_Device *dev, u32 block, u32 page)
+{
+#ifdef FILEEMU_WRITE_BIT_FLIP
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	struct uffs_FileEmuBitFlip flips[] = FILEEMU_WRITE_BIT_FLIP;
+	struct uffs_FileEmuBitFlip *x;
+	u8 buf[UFFS_MAX_PAGE_SIZE + UFFS_MAX_SPARE_SIZE];
+	u8 *data = buf;
+	u8 *spare = buf + dev->attr->page_data_size;
+	int full_page_size = dev->attr->page_data_size + dev->attr->spare_size;
+	int blk_size = full_page_size * dev->attr->pages_per_block;
+	int page_offset = block * blk_size + full_page_size * page;
+
+	int i;
+	u8 *p;
+
+	fseek(emu->fp, page_offset, SEEK_SET);
+	fread(buf, 1, full_page_size, emu->fp);
+
+	p = NULL;
+	for (i = 0; i < ARRAY_SIZE(flips); i++) {
+		x = &flips[i];
+		if (x->block == block && x->page == page) {
+			if (x->offset >= 0) {
+				printf(" --- Inject data bit flip at block%d, page%d, offset%d, mask%d --- \n", block, page, x->offset, x->mask);
+				p = (u8 *)(data + x->offset);
+			}
+			else {
+				printf(" --- Inject spare bit flip at block%d, page%d, offset%d, mask%d --- \n", block, page, -x->offset, x->mask);
+				p = (u8 *)(spare - x->offset);
+			}
+			*p = (*p & ~x->mask) | (~(*p & x->mask) & x->mask);
+		}
+	}
+
+	if (p) {
+		fseek(emu->fp, page_offset, SEEK_SET);
+		fwrite(buf, 1, full_page_size, emu->fp);
+	}
+#endif	
+}
+
+static int femu_WritePage_wrap(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *spare, int spare_len)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	int ret;
+
+#ifdef UFFS_FEMU_SHOW_FLASH_IO
+	if (data || spare) {
+		MSG(PFX " Write block %d page %d", block, page);
+		if (data)
+			MSG(" DATA[%d]", data_len);
+		if (spare)
+			MSG(" SPARE[%d]", spare_len);
+		MSG(TENDSTR);
+	}
+#endif
+	
+	ret = emu->ops_orig.WritePage(dev, block, page, data, data_len, spare, spare_len);
+
+	InjectBitFlip(dev, block, page);
+
+	return ret;
+}
+
+static int femu_WritePageWithLayout_wrap(uffs_Device *dev, u32 block, u32 page, const u8* data, int data_len, const u8 *ecc,
+									const uffs_TagStore *ts)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+	int ret;
+	
+#ifdef UFFS_FEMU_SHOW_FLASH_IO
+	if (data || ts) {
+		MSG(PFX " Write block %d page %d", block, page);
+		if (data)
+			MSG(" DATA[%d]", data_len);
+		if (ts)
+			MSG(" TS");
+		MSG(TENDSTR);
+	}
+#endif
+
+	ret = emu->ops_orig.WritePageWithLayout(dev, block, page, data, data_len, ecc, ts);
+
+	InjectBitFlip(dev, block, page);
+
+	return ret;
+}
+
+
+static int femu_EraseBlock_wrap(uffs_Device *dev, u32 blockNumber)
+{
+	uffs_FileEmu *emu = (uffs_FileEmu *)(dev->attr->_private);
+
+#ifdef FILEEMU_ERASE_BAD_BLOCKS
+	int blocks[] = FILEEMU_ERASE_BAD_BLOCKS;
+	int i;
+	URET ret;
+	ret = emu->ops_orig.EraseBlock(dev, blockNumber);
+
+	for (i = 0; i < ARRAY_SIZE(blocks); i++) {
+		if (blockNumber == blocks[i]) {
+			printf(" --- Inject bad block%d when erasing --- \n", blockNumber);
+			ret = UFFS_FLASH_BAD_BLK;
+		}
+	}
+
+	return ret;		
+
+#else
+
+	return emu->ops_orig.EraseBlock(dev, blockNumber);
+
+#endif
+}
+
+#endif // UFFS_FEMU_ENABLE_INJECTION
+
+/////////////////////////////////////////////////////////////////////////////////

+ 22 - 0
src/example/CMakeLists.txt

@@ -0,0 +1,22 @@
+IF (UNIX)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/posix)
+ENDIF()
+IF (WIN32)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/win32)
+ENDIF()
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc)
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/emu)
+
+LINK_DIRECTORIES(${uffs_BINARY_DIR}/src/emu)
+LINK_DIRECTORIES(${uffs_BINARY_DIR}/src/uffs)
+
+SET(static_mem_SRCS static-mem-allocate.c)
+SET(flash_if_SRCS flash-interface-example.c)
+ADD_EXECUTABLE(static-mem-example ${static_mem_SRCS})
+ADD_EXECUTABLE(flash-if-example ${flash_if_SRCS})
+TARGET_LINK_LIBRARIES(static-mem-example emu uffs emu platform apitest_server)
+TARGET_LINK_LIBRARIES(flash-if-example emu uffs emu platform)
+IF (UNIX)
+    TARGET_LINK_LIBRARIES(static-mem-example pthread)
+ENDIF ()
+

+ 434 - 0
src/example/flash-interface-example.c

@@ -0,0 +1,434 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/**
+ * \file flash-interface-example.c
+ * \brief example for using flash driver and multiple partitions, with static memory allocator.
+ * \author Ricky Zheng, created at 27 Nov, 2007
+ */
+  
+#include <string.h>
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_flash.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_fs.h"
+
+#define PFX "ndrv: "
+
+struct my_nand_chip {
+	void *IOR_ADDR;
+	void *IOW_ADDR;
+	UBOOL inited;
+	// ... 
+};
+
+/*
+ * Standard NAND flash commands
+ */
+#define NAND_CMD_READ0		0
+#define NAND_CMD_READ1		1
+#define NAND_CMD_RNDOUT		5
+#define NAND_CMD_PAGEPROG	0x10
+#define NAND_CMD_READOOB	0x50
+#define NAND_CMD_ERASE1		0x60
+#define NAND_CMD_STATUS		0x70
+#define NAND_CMD_STATUS_MULTI	0x71
+#define NAND_CMD_SEQIN		0x80
+#define NAND_CMD_RNDIN		0x85
+#define NAND_CMD_READID		0x90
+#define NAND_CMD_ERASE2		0xd0
+#define NAND_CMD_RESET		0xff
+
+
+/* impelent the following functions for your NAND flash */
+#define CHIP_SET_CLE(chip) { chip = chip; }
+#define CHIP_CLR_CLE(chip) {}
+#define CHIP_SET_ALE(chip) {}
+#define CHIP_CLR_ALE(chip) {}
+#define CHIP_SET_NCS(chip) {}
+#define CHIP_CLR_NCS(chip) {}
+#define CHIP_BUSY(chip) {}
+#define CHIP_READY(chip) {}
+#define WRITE_COMMAND(chip, cmd) {}
+#define WRITE_DATA_ADDR(chip, block, page, offset) {}
+#define WRITE_ERASE_ADDR(chip, block) {}
+#define WRITE_DATA(chip, data, len) {}
+#define READ_DATA(chip, data, len) {}
+
+#define PARSE_STATUS(v) (UFFS_FLASH_NO_ERR)	// parse status to UFFS_FLASH_NO_ERR or UFFS_FLASH_BAD_BLK
+
+
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR == 0
+int main()
+{
+	uffs_Perror(UFFS_MSG_NORMAL, "This example need CONFIG_USE_STATIC_MEMORY_ALLOCATOR = 1");
+	return 0;
+}
+#else
+
+
+static int nand_read_page(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
+						u8 *spare, int spare_len)
+{
+	u8 val = 0;
+	int ret = UFFS_FLASH_NO_ERR;
+	struct my_nand_chip *chip = (struct my_nand_chip *) dev->attr->_private;
+
+	CHIP_CLR_NCS(chip);
+	if (data && data_len > 0) {
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_READ0);
+		CHIP_CLR_CLE(chip);
+		CHIP_SET_ALE(chip);
+		WRITE_DATA_ADDR(chip, block, page, 0);
+		CHIP_CLR_ALE(chip);
+		READ_DATA(chip, data, data_len);
+
+		// for now, we return all 0xFF to pass UFFS mount, you should remove this at your driver
+		memset(data, 0xFF, data_len);
+	}
+
+	if (spare && spare_len > 0) {
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_READOOB);
+		CHIP_CLR_CLE(chip);
+		CHIP_SET_ALE(chip);
+		WRITE_DATA_ADDR(chip, block, page, dev->attr->page_data_size);
+		CHIP_CLR_ALE(chip);
+		READ_DATA(chip, spare, spare_len);
+
+		// for now, we return all 0xFF to pass UFFS mount, you should remove this at your driver
+		memset(spare, 0xFF, spare_len);
+	}
+
+	if (data == NULL && spare == NULL) {
+		// read bad block mark
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_READOOB);
+		CHIP_CLR_CLE(chip);
+		CHIP_SET_ALE(chip);
+		WRITE_DATA_ADDR(chip, block, page, dev->attr->page_data_size + attr->block_status_offs);
+		CHIP_CLR_ALE(chip);
+		READ_DATA(chip, &val, 1);
+		ret = (val == 0xFF ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK);
+
+		// for now, we return UFFS_FLASH_NO_ERR to pass UFFS mount, you should remove this at your driver
+		ret = UFFS_FLASH_NO_ERR;
+	}
+
+	CHIP_SET_NCS(chip);
+
+	return ret;
+}
+
+static int nand_write_page(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *spare, int spare_len)
+{
+	u8 val = 0;
+	int ret = UFFS_FLASH_NO_ERR;
+	UBOOL fall_through = FALSE;
+	struct my_nand_chip *chip = (struct my_nand_chip *) dev->attr->_private;
+
+	CHIP_CLR_NCS(chip);
+
+	if (data && data_len > 0) {
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_READ0);
+		WRITE_COMMAND(chip, NAND_CMD_SEQIN);
+		CHIP_CLR_CLE(chip);
+		CHIP_SET_ALE(chip);
+		WRITE_DATA_ADDR(chip, block, page, 0);
+		CHIP_CLR_ALE(chip);
+		CHIP_BUSY(chip);
+		WRITE_DATA(chip, data, data_len);
+		if (data_len == dev->attr->page_data_size)
+			fall_through = U_TRUE;
+		else {
+			CHIP_SET_CLE(chip);
+			WRITE_COMMAND(chip, NAND_CMD_PAGEPROG);
+			WRITE_COMMAND(chip, NAND_CMD_STATUS);
+			CHIP_CLR_CLE(chip);
+			CHIP_READY(chip);
+			READ_DATA(chip, &val, 1);
+			ret = PARSE_STATUS(val);
+		}
+	}
+
+	if (ret != UFFS_FLASH_NO_ERR)
+		goto ext;
+
+	if (spare && spare_len > 0) {
+		if (!fall_through) {
+			CHIP_SET_CLE(chip);
+			WRITE_COMMAND(chip, NAND_CMD_READOOB);
+			WRITE_COMMAND(chip, NAND_CMD_SEQIN);
+			CHIP_CLR_CLE(chip);
+			CHIP_SET_ALE(chip);
+			WRITE_DATA_ADDR(chip, block, page, dev->attr->page_data_size);
+			CHIP_CLR_ALE(chip);
+			CHIP_BUSY(chip);
+		}
+		WRITE_DATA(chip, spare, spare_len);
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_PAGEPROG);
+		WRITE_COMMAND(chip, NAND_CMD_STATUS);
+		CHIP_CLR_CLE(chip);
+		CHIP_READY(chip);
+		READ_DATA(chip, &val, 1);
+		ret = PARSE_STATUS(val);
+	}
+
+	if (data == NULL && spare == NULL) {
+		// mark bad block
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_READOOB);
+		WRITE_COMMAND(chip, NAND_CMD_SEQIN);
+		CHIP_CLR_CLE(chip);
+		CHIP_SET_ALE(chip);
+		WRITE_DATA_ADDR(chip, block, page, dev->attr->page_data_size + attr->block_status_offs);
+		CHIP_CLR_ALE(chip);
+		CHIP_BUSY(chip);
+		val = 0;
+		WRITE_DATA(chip, &val, 1);
+		CHIP_SET_CLE(chip);
+		WRITE_COMMAND(chip, NAND_CMD_PAGEPROG);
+		WRITE_COMMAND(chip, NAND_CMD_STATUS);
+		CHIP_CLR_CLE(chip);
+		CHIP_READY(chip);
+		READ_DATA(chip, &val, 1);
+		ret = PARSE_STATUS(val);
+	}
+
+ext:
+	CHIP_SET_NCS(chip);
+
+	return ret;
+}
+
+static int nand_erase_block(uffs_Device *dev, u32 block)
+{
+	u8 val = 0;
+	struct my_nand_chip *chip = (struct my_nand_chip *) dev->attr->_private;
+
+	CHIP_CLR_NCS(chip);
+
+	CHIP_SET_CLE(chip);
+	WRITE_COMMAND(chip, NAND_CMD_ERASE1);
+	CHIP_CLR_CLE(chip);
+	CHIP_SET_ALE(chip);
+	WRITE_ERASE_ADDR(chip, blcok);
+	CHIP_CLR_ALE(chip);
+	CHIP_SET_CLE(chip);
+	WRITE_COMMAND(chip, NAND_CMD_ERASE2);
+	WRITE_COMMAND(chip, NAND_CMD_STATUS);
+	CHIP_CLR_CLE(chip);
+	CHIP_READY(chip);
+	READ_DATA(chip, &val, 1);
+
+	CHIP_SET_NCS(chip);
+
+	val = val; // just for eliminating warning
+
+	return PARSE_STATUS(val);
+}
+
+
+static int nand_init_flash(uffs_Device *dev)
+{
+	// initialize your hardware here ...
+	struct my_nand_chip *chip = (struct my_nand_chip *) dev->attr->_private;
+
+	if (!chip->inited) {
+		// setup chip I/O address, setup NAND flash controller ... etc.
+		// chip->IOR_ADDR = 0xF0000000
+		// chip->IOW_ADDR = 0xF0000000
+		chip->inited = U_TRUE;
+	}
+	return 0;
+}
+
+static int nand_release_flash(uffs_Device *dev)
+{
+	// release your hardware here
+	struct my_nand_chip *chip = (struct my_nand_chip *) dev->attr->_private;
+
+	chip = chip;
+
+	return 0;
+}
+
+static uffs_FlashOps g_my_nand_ops = {
+	nand_init_flash,	// InitFlash()
+	nand_release_flash,	// ReleaseFlash()
+	nand_read_page,		// ReadPage()
+	NULL,			// ReadPageWithLayout
+	nand_write_page,	// WritePage()
+	NULL,			// WritePageWithLayout
+	NULL,			// IsBadBlock(), let UFFS take care of it.
+	NULL,			// MarkBadBlock(), let UFFS take care of it.
+	nand_erase_block,	// EraseBlock()
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+// change these parameters to fit your nand flash specification
+
+#define TOTAL_BLOCKS    1024
+#define PAGE_DATA_SIZE  512
+#define PAGE_SPARE_SIZE 16
+#define PAGES_PER_BLOCK 32
+#define PAGE_SIZE		(PAGE_DATA_SIZE + PAGE_SPARE_SIZE)
+#define BLOCK_DATA_SIZE (PAGE_DATA_SIZE * PAGES_PER_BLOCK)
+
+#define NR_PARTITION	2								/* total partitions */
+#define PAR_1_BLOCKS	100								/* partition 1 */
+#define PAR_2_BLOCKS	(TOTAL_BLOCKS - PAR_1_BLOCKS)	/* partition 2 */
+
+struct my_nand_chip g_nand_chip = {0};
+static struct uffs_StorageAttrSt g_my_flash_storage = {0};
+
+/* define mount table */
+static uffs_Device demo_device_1 = {0};
+static uffs_Device demo_device_2 = {0};
+
+static uffs_MountTable demo_mount_table[] = {
+	{ &demo_device_1,  0, PAR_1_BLOCKS - 1, "/data/" },
+	{ &demo_device_2,  PAR_1_BLOCKS, PAR_1_BLOCKS + PAR_2_BLOCKS - 1, "/" },
+	{ NULL, 0, 0, NULL }
+};
+
+/* static alloc the memory for each partition */
+static int static_buffer_par1[UFFS_STATIC_BUFF_SIZE(PAGES_PER_BLOCK, PAGE_SIZE, PAR_1_BLOCKS) / sizeof(int)];
+static int static_buffer_par2[UFFS_STATIC_BUFF_SIZE(PAGES_PER_BLOCK, PAGE_SIZE, PAR_2_BLOCKS) / sizeof(int)];;
+
+static void init_nand_chip(struct my_nand_chip *chip)
+{
+	// init chip IO address, etc.
+}
+
+static void setup_flash_storage(struct uffs_StorageAttrSt *attr)
+{
+	memset(attr, 0, sizeof(struct uffs_StorageAttrSt));
+	
+	// setup NAND flash attributes.	
+	attr->total_blocks = TOTAL_BLOCKS;			/* total blocks */
+	attr->page_data_size = PAGE_DATA_SIZE;		/* page data size */
+	attr->pages_per_block = PAGES_PER_BLOCK;	/* pages per block */
+	attr->spare_size = PAGE_SPARE_SIZE;		  	/* page spare size */
+	attr->block_status_offs = 4;				/* block status offset is 5th byte in spare */
+	attr->ecc_opt = UFFS_ECC_SOFT;              /* ecc option */
+	attr->layout_opt = UFFS_LAYOUT_UFFS;        /* let UFFS do the spare layout */    
+}
+
+static URET my_InitDevice(uffs_Device *dev)
+{
+	dev->attr = &g_my_flash_storage;			// NAND flash attributes
+	dev->attr->_private = (void *) &g_nand_chip;// hook nand_chip data structure to attr->_private
+	dev->ops = &g_my_nand_ops;					// NAND driver
+
+	init_nand_chip(&g_nand_chip);
+    
+	return U_SUCC;
+}
+
+static URET my_ReleaseDevice(uffs_Device *dev)
+{
+	return U_SUCC;
+}
+
+
+static int my_init_filesystem(void)
+{
+	uffs_MountTable *mtbl = &(demo_mount_table[0]);
+
+	/* setup nand storage attributes */
+	setup_flash_storage(&g_my_flash_storage);
+
+	/* setup memory allocator */
+	uffs_MemSetupStaticAllocator(&demo_device_1.mem, static_buffer_par1, sizeof(static_buffer_par1));
+	uffs_MemSetupStaticAllocator(&demo_device_2.mem, static_buffer_par2, sizeof(static_buffer_par2));
+
+	/* register mount table */
+	while(mtbl->dev) {
+		// setup device init/release entry
+		mtbl->dev->Init = my_InitDevice;
+		mtbl->dev->Release = my_ReleaseDevice;
+		uffs_RegisterMountTable(mtbl);
+		mtbl++;
+	}
+
+	// mount partitions
+	for (mtbl = &(demo_mount_table[0]); mtbl->mount != NULL; mtbl++) {
+		uffs_Mount(mtbl->mount);
+	}
+
+	return uffs_InitFileSystemObjects() == U_SUCC ? 0 : -1;
+}
+
+static int my_release_filesystem(void)
+{
+	uffs_MountTable *mtb;
+	int ret = 0;
+
+	// unmount parttions
+	for (mtb = &(demo_mount_table[0]); ret == 0 && mtb->mount != NULL; mtb++) {
+		ret = uffs_UnMount(mtb->mount);
+	}
+
+	// release objects
+	if (ret == 0)
+		ret = (uffs_ReleaseFileSystemObjects() == U_SUCC ? 0 : -1);
+
+	return ret;
+}
+
+/* application entry */
+int main()
+{
+	uffs_SetupDebugOutput(); 	// setup debug output as early as possible
+
+	my_init_filesystem();
+
+	// ... my application codes ....
+	// read/write/create/delete files ...
+
+	my_release_filesystem();
+
+	return 0;
+}
+
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////////

+ 163 - 0
src/example/static-mem-allocate.c

@@ -0,0 +1,163 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file static-mem-allocate.c
+ * \brief demostrate how to use static memory allocation. This example use 
+ *        file emulated NAND flash, one partition only.
+ * \author Ricky Zheng
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_mtb.h"
+#include "cmdline.h"
+#include "uffs_fileem.h"
+
+#define PFX "sexp: "
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR == 0
+int main()
+{
+	uffs_Perror(UFFS_MSG_NORMAL, "This example need CONFIG_USE_STATIC_MEMORY_ALLOCATOR = 1");
+	return 0;
+}
+#else
+
+extern struct cli_commandset * get_helper_cmds(void);
+extern struct cli_commandset * get_test_cmds(void);
+
+#define PAGE_DATA_SIZE    512
+#define PAGE_SPARE_SIZE   16
+#define PAGES_PER_BLOCK   32
+#define TOTAL_BLOCKS      128
+
+#define PAGE_SIZE					(PAGE_DATA_SIZE + PAGE_SPARE_SIZE)
+#define BLOCK_DATA_SIZE				(PAGES_PER_BLOCK * PAGE_DATA_SIZE)
+#define TOTAL_DATA_SIZE				(TOTAL_BLOCKS * BLOCK_DATA_SIZE)
+#define BLOCK_SIZE					(PAGES_PER_BLOCK * PAGE_SIZE)
+#define TOTAL_SIZE					(BLOCK_SIZE * TOTAL_BLOCKS)
+
+#define MAX_MOUNT_TABLES		10
+#define MAX_MOUNT_POINT_NAME	32
+
+static uffs_Device demo_device = {0};
+static struct uffs_MountTableEntrySt demo_mount = {
+	&demo_device,
+	0,    /* start from block 0 */
+	-1,   /* use whole chip */
+	"/",  /* mount point */
+	NULL
+};
+
+/* static alloc the memory */
+static int static_buffer_pool[UFFS_STATIC_BUFF_SIZE(PAGES_PER_BLOCK, PAGE_SIZE, TOTAL_BLOCKS) / sizeof(int)];
+
+
+static void setup_storage(struct uffs_StorageAttrSt *attr)
+{
+	attr->total_blocks = TOTAL_BLOCKS;			/* total blocks */
+	attr->page_data_size = PAGE_DATA_SIZE;		/* page data size */
+	attr->spare_size = PAGE_SPARE_SIZE;		  	/* page spare size */
+	attr->pages_per_block = PAGES_PER_BLOCK;	/* pages per block */
+	attr->block_status_offs = 4;				/* block status offset is 5th byte in spare */
+    attr->ecc_opt = UFFS_ECC_SOFT;              /* use UFFS software ecc */
+    attr->layout_opt = UFFS_LAYOUT_UFFS;        /* let UFFS do the spare layout */    
+}
+
+static void setup_device(uffs_Device *dev)
+{
+	// using file emulator device
+	dev->Init = femu_InitDevice;
+	dev->Release = femu_ReleaseDevice;
+	dev->attr = femu_GetStorage();
+}
+
+static int init_uffs_fs(void)
+{
+	struct uffs_MountTableEntrySt *mtbl = &demo_mount;
+
+	/* setup flash storage attributes */
+	setup_storage(femu_GetStorage());
+
+	/* setup memory allocator */
+	uffs_MemSetupStaticAllocator(&mtbl->dev->mem, static_buffer_pool, sizeof(static_buffer_pool));
+
+	/* setup device: init, release, attr */
+	setup_device(mtbl->dev);
+
+	/* register mount table */
+	uffs_RegisterMountTable(mtbl);
+
+	/* mount it */
+	uffs_Mount("/");
+
+	return uffs_InitFileSystemObjects() == U_SUCC ? 0 : -1;
+}
+
+static int release_uffs_fs(void)
+{
+	uffs_UnMount("/");
+
+	return uffs_ReleaseFileSystemObjects();
+}
+
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	uffs_SetupDebugOutput(); 	// setup debug output as early as possible
+
+	ret = init_uffs_fs();
+
+	if (ret != 0) {
+		printf ("Init file system fail: %d\n", ret);
+		return -1;
+	}
+
+	cli_add_commandset(get_helper_cmds());
+	cli_add_commandset(get_test_cmds());
+	cli_main_entry();
+
+	release_uffs_fs();
+
+	return 0;
+}
+
+#endif
+

+ 101 - 0
src/inc/uffs/uffs.h

@@ -0,0 +1,101 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/** 
+ * \file uffs.h
+ * \brief uffs basic defines
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_H_
+#define _UFFS_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define UO_RDONLY		0x0000		/** read only */
+#define UO_WRONLY		0x0001		/** write only */
+#define UO_RDWR			0x0002		/** read and write */
+#define UO_APPEND		0x0008		/** append */
+
+#define UO_BINARY		0x0000		/** no used in uffs */
+
+#define UO_CREATE		0x0100
+#define UO_TRUNC		0x0200
+#define UO_EXCL			0x0400
+
+#define UO_NOECC		0x0800		/** skip ECC when reading file data from media */
+
+
+#define UO_DIR			0x1000		/** open a directory */
+
+
+
+#define UENOERR 0		/** no error */
+#define UEACCES	1		/** Tried to open read-only file
+						 for writing, or files sharing mode
+						 does not allow specified operations,
+						 or given path is directory */
+
+#define UEEXIST	2		/** _O_CREAT and _O_EXCL flags specified,
+							but filename already exists */
+#define UEINVAL	3		/** Invalid oflag or pmode argument */
+#define UEMFILE	4		/** No more file handles available
+						  (too many open files)  */
+#define UENOENT	5		/** file or path not found */
+#define UETIME	6		/** can't set file time */
+#define UEBADF	9		/** invalid file handle */
+#define UENOMEM	10		/** no enough memory */
+#define UEIOERR	11		/** I/O error from lower level flash operation */
+#define UENOTDIR 12		/** Not a directory */
+#define UEISDIR 13		/** Is a directory */    
+
+#define UEUNKNOWN_ERR	100	/** unknown error */
+
+
+
+#define _SEEK_CUR		0		/** seek from current position */
+#define _SEEK_SET		1		/** seek from beginning of file */
+#define _SEEK_END		2		/** seek from end of file */
+
+#define USEEK_CUR		_SEEK_CUR
+#define USEEK_SET		_SEEK_SET
+#define USEEK_END		_SEEK_END
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 75 - 0
src/inc/uffs/uffs_badblock.h

@@ -0,0 +1,75 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/** 
+ * \file uffs_badblock.h
+ * \brief bad block management
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_BADBLOCK_H_
+#define _UFFS_BADBLOCK_H_
+
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+#define HAVE_BADBLOCK(dev) (dev->bad.block != UFFS_INVALID_BLOCK)
+
+/** initialize bad block management data structures for uffs device */
+void uffs_BadBlockInit(uffs_Device *dev);
+
+/** processing bad block: erase bad block,
+	mark it as 'bad' and put it to bad block list */
+void uffs_BadBlockProcess(uffs_Device *dev, TreeNode *node);
+
+/** processing bad block: erase bad block,
+	mark it as 'bad' and put it to suspend block list */
+void uffs_BadBlockProcessSuspend(uffs_Device *dev, TreeNode *node);
+
+/** try to recover data from a new discovered bad block */
+void uffs_BadBlockRecover(uffs_Device *dev);
+
+/** put a new block to the bad block waiting list */
+void uffs_BadBlockAdd(uffs_Device *dev, int block);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 107 - 0
src/inc/uffs/uffs_blockinfo.h

@@ -0,0 +1,107 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/** 
+ * \file uffs_blockinfo.h
+ * \brief data structure for operating block information
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_BLOCKINFO_H_
+#define _UFFS_BLOCKINFO_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+/** 
+ * \struct uffs_PageSpareSt
+ * \brief this structure is for storing uffs tag and more. 
+ */
+struct uffs_PageSpareSt {
+	uffs_Tags tag;			//!< page tag
+	int expired:1;
+};
+
+/** 
+ * \struct uffs_BlockInfoSt
+ * \brief block information data. Block info is frequently accessed,
+          UFFS use a cache system to speed up block info access.
+ */
+struct uffs_BlockInfoSt {
+	struct uffs_BlockInfoSt *next;
+	struct uffs_BlockInfoSt *prev;
+	u16 block;							//!< block number
+	struct uffs_PageSpareSt *spares;	//!< page spare info array
+	int expired_count;					//!< how many pages expired in this block ? 
+	int ref_count;						//!< reference counter, it's safe to reuse this block memory when the counter is 0.
+};
+
+/** get tag from block info */
+#define GET_TAG(bc, page) (&(bc)->spares[page].tag)
+
+
+/** initialize block info caches */
+URET uffs_BlockInfoInitCache(uffs_Device *dev, int maxCachedBlocks);
+
+/** release block info caches */
+URET uffs_BlockInfoReleaseCache(uffs_Device *dev);
+
+/** load page spare to block info cache */
+URET uffs_BlockInfoLoad(uffs_Device *dev, uffs_BlockInfo *work, int page);
+
+/** find block info cache */
+uffs_BlockInfo * uffs_BlockInfoFindInCache(uffs_Device *dev, int block);
+
+/** get block info cache, load it on demand */
+uffs_BlockInfo * uffs_BlockInfoGet(uffs_Device *dev, int block);
+
+/** put info cache back to pool, should be called with #uffs_BlockInfoGet in pairs. */
+void uffs_BlockInfoPut(uffs_Device *dev, uffs_BlockInfo *p);
+
+/** explicitly expire a block info cache */
+void uffs_BlockInfoExpire(uffs_Device *dev, uffs_BlockInfo *p, int page);
+
+/** no one hold any block info cache ? safe to release block info caches */
+UBOOL uffs_BlockInfoIsAllFree(uffs_Device *dev);
+
+/** explicitly expire all block info caches */
+void uffs_BlockInfoExpireAll(uffs_Device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 178 - 0
src/inc/uffs/uffs_buf.h

@@ -0,0 +1,178 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_buf.h
+ * \brief page buffers
+ * \author Ricky Zheng
+ */
+
+#ifndef UFFS_BUF_H
+#define UFFS_BUF_H
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_tree.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+	
+#define CLONE_BUF_MARK		0xffff		//!< set uffs_BufSt::ref_count to this for a 'cloned' buffer
+
+/** for uffs_BufSt::mark */
+#define UFFS_BUF_EMPTY		0			//!< buffer is empty
+#define UFFS_BUF_VALID		1			//!< buffer is holding valid data
+#define UFFS_BUF_DIRTY		2			//!< buffer data is modified
+
+/** for uffs_BufSt::ext_mark */
+#define UFFS_BUF_EXT_MARK_TRUNC_TAIL 1	//!< the last page of file (when truncating a file)
+
+/** uffs page buffer */
+struct uffs_BufSt{
+	struct uffs_BufSt *next;			//!< link to next buffer
+	struct uffs_BufSt *prev;			//!< link to previous buffer
+	struct uffs_BufSt *next_dirty;		//!< link to next dirty buffer
+	struct uffs_BufSt *prev_dirty;		//!< link to previous dirty buffer
+	u8 type;							//!< #UFFS_TYPE_DIR or #UFFS_TYPE_FILE or #UFFS_TYPE_DATA
+	u8 ext_mark;						//!< extension mark. 
+	u16 parent;							//!< parent serial
+	u16 serial;							//!< serial 
+	u16 page_id;						//!< page id 
+	u16 mark;							//!< #UFFS_BUF_EMPTY or #UFFS_BUF_VALID, or #UFFS_BUF_DIRTY ?
+	u16 ref_count;						//!< reference counter, or #CLONE_BUF_MARK for a cloned buffer
+	u16 data_len;						//!< length of data
+	u16 check_sum;						//!< checksum field
+	u8 * data;							//!< data buffer
+	u8 * header;						//!< header
+};
+
+#define uffs_BufIsFree(buf) (buf->ref_count == 0 ? U_TRUE : U_FALSE)
+
+/** initialize page buffers */
+URET uffs_BufInit(struct uffs_DeviceSt *dev, int buf_max, int dirty_buf_max);
+
+/** release page buffers */
+URET uffs_BufReleaseAll(struct uffs_DeviceSt *dev);
+
+/** find the page buffer, move to link list head if found */
+uffs_Buf * uffs_BufGet(struct uffs_DeviceSt *dev, u16 parent, u16 serial, u16 page_id);
+uffs_Buf *uffs_BufGetEx(struct uffs_DeviceSt *dev, u8 type, TreeNode *node, u16 page_id, int oflag);
+
+/** alloc a new page buffer */
+uffs_Buf *uffs_BufNew(struct uffs_DeviceSt *dev, u8 type, u16 parent, u16 serial, u16 page_id);
+
+/** find the page buffer (not affect the reference counter) */
+uffs_Buf * uffs_BufFind(uffs_Device *dev, u16 parent, u16 serial, u16 page_id);
+
+/** find the page buffer from #start (not affect the reference counter) */
+uffs_Buf * uffs_BufFindFrom(uffs_Device *dev, uffs_Buf *start,
+						u16 parent, u16 serial, u16 page_id);
+
+/** put page buffer back to pool, called in pair with #uffs_Get,#uffs_GetEx or #uffs_BufNew */
+URET uffs_BufPut(uffs_Device *dev, uffs_Buf *buf);
+
+/** increase buffer references */
+void uffs_BufIncRef(uffs_Buf *buf);
+
+/** decrease buffer references */
+void uffs_BufDecRef(uffs_Buf *buf);
+
+/** write data to a page buffer */
+URET uffs_BufWrite(struct uffs_DeviceSt *dev, uffs_Buf *buf, void *data, u32 ofs, u32 len);
+
+/** read data from a page buffer */
+URET uffs_BufRead(struct uffs_DeviceSt *dev, uffs_Buf *buf, void *data, u32 ofs, u32 len);
+
+/** mark buffer as #UFFS_BUF_EMPTY if ref_count == 0, and discard all data it holds */
+void uffs_BufMarkEmpty(uffs_Device *dev, uffs_Buf *buf);
+
+/** if there is no free dirty group slot, flush the most dirty group */
+URET uffs_BufFlush(struct uffs_DeviceSt *dev);
+URET uffs_BufFlushEx(struct uffs_DeviceSt *dev, UBOOL force_block_recover);
+
+/** flush dirty group */
+URET uffs_BufFlushGroup(struct uffs_DeviceSt *dev, u16 parent, u16 serial);
+URET uffs_BufFlushGroupEx(struct uffs_DeviceSt *dev, u16 parent, u16 serial, UBOOL force_block_recover);
+
+/** find free dirty group slot */
+int uffs_BufFindFreeGroupSlot(struct uffs_DeviceSt *dev);
+
+/** find the dirty group slot */
+int uffs_BufFindGroupSlot(struct uffs_DeviceSt *dev, u16 parent, u16 serial);
+
+/** lock dirty group */
+URET uffs_BufLockGroup(struct uffs_DeviceSt *dev, int slot);
+
+/** unlock dirty group */
+URET uffs_BufUnLockGroup(struct uffs_DeviceSt *dev, int slot);
+
+/** flush most dirty group */
+URET uffs_BufFlushMostDirtyGroup(struct uffs_DeviceSt *dev);
+
+/** flush all groups under the same parent number */
+URET uffs_BufFlushGroupMatchParent(struct uffs_DeviceSt *dev, u16 parent);
+
+/** flush all page buffers */
+URET uffs_BufFlushAll(struct uffs_DeviceSt *dev);
+
+/** no one holding any page buffer ? safe to release page buffers */
+UBOOL uffs_BufIsAllFree(struct uffs_DeviceSt *dev);
+
+/** are all page buffer marked with #UFFS_BUF_EMPTY ? */
+UBOOL uffs_BufIsAllEmpty(struct uffs_DeviceSt *dev);
+
+/** mark all page buffer as #UFFS_BUF_EMPTY */
+URET uffs_BufSetAllEmpty(struct uffs_DeviceSt *dev);
+
+/** clone a page buffer */
+uffs_Buf * uffs_BufClone(struct uffs_DeviceSt *dev, uffs_Buf *buf);
+
+/** release a cloned page buffer, call in pair with #uffs_BufClone */
+URET uffs_BufFreeClone(uffs_Device *dev, uffs_Buf *buf);
+
+/** load physical storage data to page buffer */
+URET uffs_BufLoadPhyData(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page);
+
+/** load physical storage data to page buffer withouth checking ECC */
+URET uffs_LoadPhyDataToBufEccUnCare(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page);
+
+/** showing page buffers info, for debug only */
+void uffs_BufInspect(uffs_Device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 59 - 0
src/inc/uffs/uffs_core.h

@@ -0,0 +1,59 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_CORE_H_
+#define _UFFS_CORE_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/** \typedef uffs_Device */
+typedef struct uffs_DeviceSt		uffs_Device;
+/** \typedef uffs_FlashOps */
+typedef struct uffs_FlashOpsSt		uffs_FlashOps;
+
+typedef struct uffs_BlockInfoSt uffs_BlockInfo;
+typedef struct uffs_PageSpareSt uffs_PageSpare;
+typedef struct uffs_TagsSt			uffs_Tags;		//!< UFFS page tags
+typedef struct uffs_TagStoreSt      uffs_TagStore;  //!< UFFS page tags physical store structure
+
+typedef struct uffs_BufSt uffs_Buf;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif

+ 46 - 0
src/inc/uffs/uffs_crc.h

@@ -0,0 +1,46 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_crc.h
+ * \author Ricky Zheng, created 23 Nov, 2011
+ */
+
+#ifndef _UFFS_CRC_H_
+#define _UFFS_CRC_H_
+
+#include "uffs/uffs_types.h"
+
+u16 uffs_crc16update(const void *data, int length, u16 crc);
+u16 uffs_crc16sum(const void *data, int length);
+
+#endif

+ 211 - 0
src/inc/uffs/uffs_device.h

@@ -0,0 +1,211 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_device.h
+ * \brief uffs device structures definition
+ * \author Ricky Zheng
+ */
+
+#ifndef UFFS_DEVICE_H
+#define UFFS_DEVICE_H
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_buf.h"
+#include "uffs/uffs_blockinfo.h"
+#include "uffs/uffs_pool.h"
+#include "uffs/uffs_tree.h"
+#include "uffs/uffs_mem.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_flash.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/**
+ * \def MAX_DIRTY_BUF_GROUPS
+ */
+#define MAX_DIRTY_BUF_GROUPS    3
+
+
+/** 
+ * \struct uffs_BlockInfoCacheSt
+ * \brief block information structure, used to manager block information caches
+ */
+struct uffs_BlockInfoCacheSt {
+	uffs_BlockInfo *head;			//!< buffer head of block info(spares)
+	uffs_BlockInfo *tail;			//!< buffer tail
+	void *mem_pool;					//!< internal memory pool, used for release whole buffer
+};
+
+/** 
+ * \struct uffs_PartitionSt
+ * \brief partition basic information
+ */
+struct uffs_PartitionSt {
+	u16 start;		//!< start block number of partition
+	u16 end;		//!< end block number of partition
+};
+
+/** 
+ * \struct uffs_LockSt
+ * \brief lock stuffs
+ */
+struct uffs_LockSt {
+	OSSEM sem;
+	int task_id;
+	int counter;
+};
+
+/** 
+ * \struct uffs_DirtyGroupSt
+ * \brief manager dirty page buffers
+ */
+struct uffs_DirtyGroupSt {
+	int count;					//!< dirty buffers count
+	int lock;					//!< dirty group lock (0: unlocked, >0: locked)
+	uffs_Buf *dirty;			//!< dirty buffer list
+};
+
+/** 
+ * \struct uffs_PageBufDescSt
+ * \brief uffs page buffers descriptor
+ */
+struct uffs_PageBufDescSt {
+	uffs_Buf *head;			//!< head of buffers
+	uffs_Buf *tail;			//!< tail of buffers
+	struct uffs_DirtyGroupSt dirtyGroup[MAX_DIRTY_BUF_GROUPS];	//!< dirty buffer groups
+	int buf_max;			//!< maximum buffers
+	int dirty_buf_max;		//!< maximum dirty buffer allowed
+	void *pool;				//!< memory pool for buffers
+};
+
+
+/** 
+ * \struct uffs_PageCommInfoSt
+ * \brief common data for device, should be initialised at early
+ * \note it is possible that pg_size is smaller than physical page size, but normally they are the same.
+ * \note page data layout: [HEADER] + [DATA]
+ */
+struct uffs_PageCommInfoSt {
+	u16 pg_data_size;			//!< page data size
+	u16 header_size;			//!< header size
+	u16 pg_size;				//!< page size
+};
+
+/** 
+ * \struct uffs_NewBadBlockSt
+ * \brief holding new discovered bad block
+ */
+struct uffs_NewBadBlockSt {
+	u16 block;				//!< bad block, FIX ME to process more than one bad block
+};
+
+/**
+ * \struct uffs_FlashStatSt
+ * \typedef uffs_FlashStat
+ * \brief statistic data of flash read/write/erase activities
+ */
+typedef struct uffs_FlashStatSt {
+	int block_erase_count;
+	int page_write_count;
+	int page_read_count;
+	int page_header_read_count;
+	int spare_write_count;
+	int spare_read_count;
+	unsigned long io_read;
+	unsigned long io_write;
+} uffs_FlashStat;
+
+
+/**
+ * \struct uffs_ConfigSt
+ * \typedef uffs_Config
+ * \brief uffs config parameters
+ */
+typedef struct uffs_ConfigSt {
+	int bc_caches;
+	int page_buffers;
+	int dirty_pages;
+	int dirty_groups;
+	int reserved_free_blocks;
+} uffs_Config;
+
+
+/** 
+ * \struct uffs_DeviceSt
+ * \brief The core data structure of UFFS, all information needed by manipulate UFFS object
+ * \note one partition corresponding one uffs device.
+ */
+struct uffs_DeviceSt {
+	URET (*Init)(uffs_Device *dev);				//!< low level initialisation
+	URET (*Release)(uffs_Device *dev);			//!< low level release
+	void *_private;								//!< private data for device
+
+	struct uffs_StorageAttrSt		*attr;		//!< storage attribute
+	struct uffs_PartitionSt			par;		//!< partition information
+	struct uffs_FlashOpsSt			*ops;		//!< flash operations
+	struct uffs_BlockInfoCacheSt	bc;			//!< block info cache
+	struct uffs_LockSt				lock;		//!< lock data structure
+	struct uffs_PageBufDescSt		buf;		//!< page buffers
+	struct uffs_PageCommInfoSt		com;		//!< common information
+	struct uffs_TreeSt				tree;		//!< tree list of block
+	struct uffs_NewBadBlockSt		bad;		//!< new discovered bad block
+	struct uffs_FlashStatSt			st;			//!< statistic (counters)
+	struct uffs_memAllocatorSt		mem;		//!< uffs memory allocator
+	struct uffs_ConfigSt			cfg;		//!< uffs config
+	u32	ref_count;								//!< device reference count
+	int	dev_num;								//!< device number (partition number)	
+};
+
+
+/** create the lock for uffs device */
+void uffs_DeviceInitLock(uffs_Device *dev);
+
+/** delete the lock of uffs device */
+void uffs_DeviceReleaseLock(uffs_Device *dev);
+
+/** lock uffs device */
+void uffs_DeviceLock(uffs_Device *dev);
+
+/** unlock uffs device */
+void uffs_DeviceUnLock(uffs_Device *dev);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 89 - 0
src/inc/uffs/uffs_ecc.h

@@ -0,0 +1,89 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_ecc.h
+ * \brief file handle operations
+ * \author Ricky Zheng, created 8th Jun, 2005
+ */
+
+#ifndef _UFFS_ECC_H_
+#define _UFFS_ECC_H_
+
+#include <string.h>
+
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+
+#define MAX_ECC_LENGTH	24	//!< 2K page ecc length is 24 bytes.
+
+/**
+ * calculate ECC
+ * \return length of generated ECC. (3 bytes ECC per 256 data) 
+ */
+int uffs_EccMake(const void *data, int data_len, void *ecc);
+
+/** 
+ * correct data by ECC.
+ *
+ * return:   0 -- no error
+ *			-1 -- can not be corrected
+ *			>0 -- how many bits are corrected
+ */
+int uffs_EccCorrect(void *data, int data_len, void *read_ecc, const void *test_ecc);
+
+
+/**
+ * generate 12 bit ecc for maximum 8 bytes data
+ */
+u16 uffs_EccMake8(const void *data, int data_len);
+
+/**
+ * correct maximum 8 bytes data from 12 bits ECC
+ *
+ * return:   0 -- no error
+ *			-1 -- can not be corrected
+ *			>0 -- how many bits are corrected
+ */
+int uffs_EccCorrect8(void *data, u16 read_ecc, u16 test_ecc, int errtop);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 151 - 0
src/inc/uffs/uffs_fd.h

@@ -0,0 +1,151 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fd.h
+ * \brief PISIX like file operations
+ * \author Ricky Zheng, created 8th Jun, 2005
+ */
+
+#ifndef _UFFS_FD_H_
+#define _UFFS_FD_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "uffs/uffs.h"
+
+/**
+ * \brief definitions for uffs_stat::st_mode
+ */
+#define	US_IFMT		0xF000	/* file type make */
+#define	US_IFREG	0x8000	/* regular */
+#define	US_IFLNK	0xA000	/* symbolic link */
+#define	US_IFDIR	0x4000	/* directory */
+#define	US_IREAD	0000400	/* read permission */
+#define	US_IWRITE	0000200	/* write permission */
+
+#define	US_IRWXU	00700	/* RWX	owner */
+#define	US_IRUSR	00400	/* R	owner */
+#define	US_IWUSR	00200	/* W	owner */
+#define	US_IXUSR	00100	/* X	owner */
+#define	US_IRWXG	00070	/* RWX	group */
+#define	US_IRGRP	00040	/* R	group */
+#define	US_IWGRP	00020	/* W	group */
+#define	US_IXGRP	00010	/* X	group */
+#define	US_IRWXO	00007	/* RWX	other */
+#define	US_IROTH	00004	/* R	other */
+#define	US_IWOTH	00002	/* W	other */
+#define	US_IXOTH	00001	/* X	other */
+
+/**
+ * \brief POSIX dirent
+ */
+struct uffs_dirent {
+    int d_ino;							/* inode number (serial number of this object) */
+    int d_off;							/* offset to this dirent */
+    unsigned short int d_reclen;		/* length of this uffs_dirent */
+    unsigned short int d_namelen;		/* length of this d_name */
+    unsigned char d_type;				/* type of this record */
+    char d_name[256];					/* name of this object */
+};
+
+struct uffs_dirSt;
+typedef struct uffs_dirSt uffs_DIR;
+
+/**
+ * \brief POSIX stat
+ */
+struct uffs_stat {
+    int			st_dev;     /* ID of device containing file */
+    int			st_ino;     /* inode number */
+    int			st_mode;    /* protection */
+    int			st_nlink;   /* number of hard links */
+    int			st_uid;     /* user ID of owner */
+    int			st_gid;     /* group ID of owner */
+    int			st_rdev;    /* device ID (if special file) */
+    long		st_size;    /* total size, in bytes */
+    int			st_blksize; /* blocksize for filesystem I/O */
+    int			st_blocks;  /* number of blocks allocated */
+    unsigned int	st_atime;   /* time of last access */
+    unsigned int	st_mtime;   /* time of last modification */
+    unsigned int	st_ctime;   /* time of last status change */
+};
+
+/* POSIX complaint file system APIs */
+
+int uffs_open(const char *name, int oflag, ...);
+int uffs_close(int fd);
+int uffs_read(int fd, void *data, int len);
+int uffs_write(int fd, const void *data, int len);
+long uffs_seek(int fd, long offset, int origin);
+long uffs_tell(int fd);
+int uffs_eof(int fd);
+int uffs_flush(int fd);
+int uffs_rename(const char *old_name, const char *new_name);
+int uffs_remove(const char *name);
+int uffs_ftruncate(int fd, long remain);
+
+int uffs_mkdir(const char *name, ...);
+int uffs_rmdir(const char *name);
+
+int uffs_stat(const char *name, struct uffs_stat *buf);
+int uffs_lstat(const char *name, struct uffs_stat *buf);
+int uffs_fstat(int fd, struct uffs_stat *buf);
+
+int uffs_closedir(uffs_DIR *dirp);
+uffs_DIR * uffs_opendir(const char *path);
+struct uffs_dirent * uffs_readdir(uffs_DIR *dirp);
+
+void uffs_rewinddir(uffs_DIR *dirp);
+
+
+int uffs_get_error(void);
+int uffs_set_error(int err);
+
+int uffs_version(void);
+int uffs_format(const char *mount_point);
+
+long uffs_space_total(const char *mount_point);
+long uffs_space_used(const char *mount_point);
+long uffs_space_free(const char *mount_point);
+
+void uffs_flush_all(const char *mount_point);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+
+

+ 76 - 0
src/inc/uffs/uffs_find.h

@@ -0,0 +1,76 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_find.h
+ * \brief find objects under dir
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_FIND_H_
+#define _UFFS_FIND_H_
+
+#include "uffs/uffs_fs.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+typedef struct uffs_FindInfoSt {
+	uffs_Device *dev;				//!< the device to be searched
+	u16 serial;						//!< the dir serial number
+	int step;						//!< step:	0 - working on dir entries,
+									//			1 - working on file entries,
+									//			2 - stoped.
+	int hash;						//!< hash entry, internal used
+	TreeNode *work;					//!< working node, internal used.
+	int pos;						//!< current position
+} uffs_FindInfo;
+
+
+URET uffs_GetObjectInfo(uffs_Object *obj, uffs_ObjectInfo *info, int *err);
+URET uffs_FindObjectOpen(uffs_FindInfo *find_handle, uffs_Object *dir);
+URET uffs_FindObjectOpenEx(uffs_FindInfo *f, uffs_Device *dev, int dir);
+URET uffs_FindObjectFirst(uffs_ObjectInfo *info, uffs_FindInfo *find_handle);
+URET uffs_FindObjectNext(uffs_ObjectInfo *info, uffs_FindInfo *find_handle);
+URET uffs_FindObjectRewind(uffs_FindInfo *find_handle);
+URET uffs_FindObjectClose(uffs_FindInfo * find_handle);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
+

+ 311 - 0
src/inc/uffs/uffs_flash.h

@@ -0,0 +1,311 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_public.h
+ * \brief flash interface for UFFS
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_FLASH_H_
+#define _UFFS_FLASH_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_fs.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+/** ECC options (uffs_StorageAttrSt.ecc_opt) */
+#define UFFS_ECC_NONE		0	//!< do not use ECC
+#define UFFS_ECC_SOFT		1	//!< UFFS calculate the ECC
+#define UFFS_ECC_HW			2	//!< Flash driver(or by hardware) calculate the ECC
+#define UFFS_ECC_HW_AUTO	3	//!< Hardware calculate the ECC and automatically write to spare.
+
+#define UFFS_ECC_OPTION_STRING \
+	{ "none", "soft", "hw", "auto" }	// index is the ecc option value.
+
+/** spare layout options (uffs_StorageAttrSt.layout_opt) */
+#define UFFS_LAYOUT_UFFS	0	//!< do layout by dev->attr information
+#define UFFS_LAYOUT_FLASH	1	//!< flash driver do the layout
+
+#define UFFS_SPARE_LAYOUT_SIZE	16	//!< maximum spare layout array size, 2 segments
+
+/** flash operation return code */
+#define UFFS_FLASH_NO_ERR		0		//!< no error
+#define UFFS_FLASH_ECC_OK		1		//!< bit-flip found, but corrected by ECC
+#define UFFS_FLASH_NOT_SEALED	2		//!< page spare area is not sealed properly (only for ReadPageWithLayout())
+#define UFFS_FLASH_IO_ERR		-1		//!< I/O error
+#define UFFS_FLASH_ECC_FAIL		-2		//!< ECC failed
+#define UFFS_FLASH_BAD_BLK		-3		//!< bad block
+#define UFFS_FLASH_CRC_ERR		-4		//!< CRC failed
+#define UFFS_FLASH_UNKNOWN_ERR	-100	//!< unkown error?
+
+#define UFFS_FLASH_HAVE_ERR(e)		((e) < 0)
+
+#if defined(CONFIG_BAD_BLOCK_POLICY_STRICT)
+# define UFFS_FLASH_IS_BAD_BLOCK(e)	\
+	((e) == UFFS_FLASH_ECC_FAIL || (e) == UFFS_FLASH_ECC_OK || (e) == UFFS_FLASH_BAD_BLK || (e) == UFFS_FLASH_CRC_ERR)
+#else
+# define UFFS_FLASH_IS_BAD_BLOCK(e)	\
+	((e) == UFFS_FLASH_ECC_FAIL || (e) == UFFS_FLASH_BAD_BLK || (e) == UFFS_FLASH_CRC_ERR)
+#endif
+
+
+/** defines for page info (data length and data sum) */
+#define UFFS_PAGE_INFO_CLEAN	0xFFFFFFFF
+#define UFFS_PAGE_INFO_IOERR	0xDEADFFFF
+#define UFFS_PAGE_GET_LEN(info)	(info & 0xFFFF)
+#define UFFS_PAGE_GET_DSUM(info) (info >> 16)
+#define UFFS_PAGE_MAKE_INFO(d_len, d_sum) ((d_sum << 16) | d_len)
+
+/** 
+ * \struct uffs_StorageAttrSt
+ * \brief uffs device storage attribute, provide by nand specific file
+ */
+struct uffs_StorageAttrSt {
+	u32 total_blocks;		//!< total blocks in this chip
+	u16 page_data_size;		//!< page data size (physical page data size, e.g. 512)
+	u16 pages_per_block;	//!< pages per block
+	u8 spare_size;			//!< page spare size (physical page spare size, e.g. 16)
+	u8 block_status_offs;	//!< block status byte offset in spare
+	int ecc_opt;			//!< ecc option ( #UFFS_ECC_[NONE|SOFT|HW|HW_AUTO] )
+	int layout_opt;			//!< layout option (#UFFS_LAYOUT_UFFS or #UFFS_LAYOUT_FLASH)
+	int ecc_size;			//!< ecc size in bytes
+	const u8 *ecc_layout;	//!< page data ECC layout: [ofs1, size1, ofs2, size2, ..., 0xFF, 0]
+	const u8 *data_layout;	//!< spare data layout: [ofs1, size1, ofs2, size2, ..., 0xFF, 0]
+	u8 _uffs_ecc_layout[UFFS_SPARE_LAYOUT_SIZE];	//!< uffs spare ecc layout
+	u8 _uffs_data_layout[UFFS_SPARE_LAYOUT_SIZE];	//!< uffs spare data layout
+	void *_private;			//!< private data for storage attribute
+};
+
+
+/**
+ * \struct uffs_FlashOpsSt 
+ * \brief low level flash operations, should be implement in flash driver
+ */
+struct uffs_FlashOpsSt {
+	/**
+	 * Initilize flash driver, called once when UFFS initilize partition
+	 *
+	 * \return 0 if no error, return -1 when failed.
+	 *
+	 * \note This function is optional.
+	 */
+	int (*InitFlash)(uffs_Device *dev);
+
+	/**
+	 * Release flash driver, called once when UFFS unmount partition
+	 *
+	 * \return 0 if no error, return -1 when failed.
+	 *
+	 * \note This function is optional.
+	 */
+	int (*ReleaseFlash)(uffs_Device *dev);
+
+	/**
+	 * Read a full nand page, UFFS do the layout.
+	 * 
+	 * \param[out] ecc ecc of page data
+	 *   if ecc_opt is UFFS_ECC_HW, flash driver must calculate and return ecc of data(if ecc != NULL).
+	 *   if ecc_opt is UFFS_ECC_HW_AUTO, flash driver do ecc correction before return data and flash driver do not need to return ecc.
+	 *   if ecc_opt is UFFS_ECC_NONE or UFFS_ECC_SOFT, flash driver do not need to calculate data ecc and return ecc.
+	 *
+	 * \return	#UFFS_FLASH_NO_ERR: success and/or has no flip bits.
+	 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+	 *			#UFFS_FLASH_ECC_FAIL: page data has flip bits and ecc correct failed (when ecc_opt == UFFS_ECC_HW_AUTO)
+	 *			#UFFS_FLASH_ECC_OK: page data has flip bits and corrected by ecc (when ecc_opt == UFFS_ECC_HW_AUTO)
+	 *          #UFFS_FLASH_BAD_BLK: if the block is a bad block (e.g., the bad block mark byte is not 0xFF).
+	 *
+	 * \note if data is NULL, do not return data; if spare is NULL, do not return spare; if both data and spare are all NULL,
+	 *       then read bad block mark and return UFFS_FLASH_BAD_BLK if bad block mark is not 0xFF.
+	 *
+	 * \note pad 0xFF for calculating ECC if len < page_data_size
+	 */
+	int (*ReadPage)(uffs_Device *dev, u32 block, u32 page, u8 *data, int data_len, u8 *ecc,
+						u8 *spare, int spare_len);
+
+	/**
+	 * Read a full nand page, driver do the layout.
+	 *
+	 * \param[out] ecc ecc of page data
+	 *   if ecc_opt is UFFS_ECC_HW, flash driver must calculate and return ecc of data(if ecc != NULL).
+	 *   if ecc_opt is UFFS_ECC_HW_AUTO, flash driver do ecc correction before return data and flash driver do not need to return ecc.
+	 *   if ecc_opt is UFFS_ECC_NONE or UFFS_ECC_SOFT, flash driver do not need calculate data ecc and return ecc.
+	 *
+	 * \param[out] ecc_store ecc store on spare area
+	 *   if ecc_opt is UFFS_ECC_NONE or UFFS_ECC_HW_AUTO, do not need to return ecc_store.
+	 *
+	 * \note flash driver must provide this function if layout_opt is UFFS_LAYOUT_FLASH.
+	 *       UFFS will use this function (if exist) prio to 'ReadPageSpare()'
+	 *
+	 * \return	#UFFS_FLASH_NO_ERR: success
+	 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+	 *			#UFFS_FLASH_ECC_FAIL: page data has flip bits and ecc correct failed (when ecc_opt == UFFS_ECC_HW_AUTO)
+	 *			#UFFS_FLASH_ECC_OK: page data has flip bits and corrected by ecc (when ecc_opt == UFFS_ECC_HW_AUTO)
+	 *          #UFFS_FLASH_BAD_BLK: if the block is a bad block (e.g., the bad block mark byte is not 0xFF)
+	 *			#UFFS_FLASH_NOT_SEALED: if the page spare is not sealed properly
+	 *
+	 * \note if data is NULL, do not return data; if ts is NULL, do not read tag; if both data and ts are NULL,
+	 *       then read bad block mark and return UFFS_FLASH_BAD_BLK if bad block mark is not 0xFF.
+	 *
+	 * \note flash driver DO NOT need to do ecc correction for tag,
+	 *		UFFS will take care of tag ecc.
+	 */
+	int (*ReadPageWithLayout)(uffs_Device *dev, u32 block, u32 page, u8* data, int data_len, u8 *ecc,
+									uffs_TagStore *ts, u8 *ecc_store);
+
+	/**
+	 * Write a full page, UFFS do the layout for spare area.
+	 *
+	 * \note If you have ecc_opt UFFS_ECC_HW or UFFS_ECC_HW_AUTO, you MUST implement WritePageWithLayout().
+	 *       Implement WritePage() function if you have ecc_opt UFFS_ECC_NONE or UFFS_ECC_SOFT and
+	 *       WritePageWithLayout() is not implemented.
+	 *
+	 * \note If data == NULL && spare == NULL, driver should mark this block as a 'bad block'.
+	 *
+	 * \return	#UFFS_FLASH_NO_ERR: success
+	 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+	 *			#UFFS_FLASH_BAD_BLK: a bad block detected.
+	 */
+	int (*WritePage)(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *spare, int spare_len);
+
+	/**
+	 * Write full page, flash driver do the layout for spare area.
+	 *
+	 * \note if layout_opt is UFFS_LAYOUT_FLASH or ecc_opt is UFFS_ECC_HW/UFFS_ECC_HW_AUTO, flash driver MUST implement
+	 *       this function. UFFS will use this function (if provided) prio to 'WritePage()'
+	 *
+	 * \param[in] ecc ecc of data. if ecc_opt is UFFS_ECC_SOFT and this function is implemented,
+	 *                UFFS will calculate page data ecc and pass it to WritePageWithLayout().
+	 *            if ecc_opt is UFFS_ECC_NONE/UFFS_ECC_HW/UFFS_ECC_HW_AUTO, UFFS pass ecc = NULL.
+	 *
+	 * \note If data == NULL && ts == NULL, driver should mark this block as a 'bad block'.
+	 *
+	 * \return	#UFFS_FLASH_NO_ERR: success
+	 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+	 *			#UFFS_FLASH_BAD_BLK: a bad block detected.
+	 */
+	int (*WritePageWithLayout)(uffs_Device *dev, u32 block, u32 page,
+							const u8 *data, int data_len, const u8 *ecc, const uffs_TagStore *ts);
+
+	/**
+	 * Check block status.
+	 *
+	 * \note flash driver may maintain a bad block table to speed up bad block checking or
+	 *		it will require one or two read spare I/O to check block status.
+	 *
+	 * \note if this function is not implented by driver, UFFS check the block_status byte in spare.
+	 *
+	 * \return 1 if it's a bad block, 0 if it's not.
+	 */
+	int (*IsBadBlock)(uffs_Device *dev, u32 block);
+
+	/**
+	 * Mark a new bad block.
+	 *
+	 * \note if this function is not implemented, UFFS mark bad block by call 'WritePage()/WritePageWithLayout()'
+	 *       with: data == NULL && spare == NULL && ts == NULL.
+	 *
+	 * \return 0 if success, otherwise return -1.
+	 */
+	int (*MarkBadBlock)(uffs_Device *dev, u32 block);
+
+	/**
+	 * Erase a block, driver MUST implement this function.
+	 *
+	 * \return	#UFFS_FLASH_NO_ERR: success
+	 *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+	 *			#UFFS_FLASH_BAD_BLK: a bad block detected.
+	 */
+	int (*EraseBlock)(uffs_Device *dev, u32 block);
+};
+
+/** make spare from tag store and ecc */
+void uffs_FlashMakeSpare(uffs_Device *dev, const uffs_TagStore *ts, const u8 *ecc, u8* spare);
+
+/** unload tag and ecc from spare */
+void uffs_FlashUnloadSpare(uffs_Device *dev, const u8 *spare, struct uffs_TagStoreSt *ts, u8 *ecc);
+
+/** read page spare and fill to tag */
+int uffs_FlashReadPageTag(uffs_Device *dev, int block, int page, uffs_Tags *tag);
+
+/** read page data to page buf and do ECC correct */
+int uffs_FlashReadPage(uffs_Device *dev, int block, int page, uffs_Buf *buf, UBOOL skip_ecc);
+
+/** write page data and spare */
+int uffs_FlashWritePageCombine(uffs_Device *dev, int block, int page, uffs_Buf *buf, uffs_Tags *tag);
+
+/** Mark this block as bad block */
+int uffs_FlashMarkBadBlock(uffs_Device *dev, int block);
+
+/** Is this block a bad block ? */
+UBOOL uffs_FlashIsBadBlock(uffs_Device *dev, int block);
+
+/** Erase flash block */
+int uffs_FlashEraseBlock(uffs_Device *dev, int block);
+
+/* mark a clean page as 'dirty' (and 'invalid') */
+int uffs_FlashMarkDirtyPage(uffs_Device *dev, uffs_BlockInfo *bc, int page);
+
+/* check if the block pages are all clean */
+URET uffs_FlashCheckErasedBlock(uffs_Device *dev, int block);
+	
+/**
+ * get page head info
+ *
+ * \return #UFFS_PAGE_INFO_IOERR if I/O error, otherwise return page info
+ */
+u32 uffs_FlashGetPageInfo(uffs_Device *dev, int block, int page);
+
+/** load uffs_FileInfo from flash storage */
+URET uffs_FlashReadFileinfoPhy(uffs_Device *dev, int block, int page, uffs_FileInfo *info);
+
+/**
+ * Initialize UFFS flash interface
+ */
+URET uffs_FlashInterfaceInit(uffs_Device *dev);
+
+/**
+ * Release UFFS flash interface
+ */
+URET uffs_FlashInterfaceRelease(uffs_Device *dev);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 138 - 0
src/inc/uffs/uffs_fs.h

@@ -0,0 +1,138 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_fs.h
+ * \brief uffs basic file operations
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_FS_H_
+#define _UFFS_FS_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/** file object */
+struct uffs_ObjectSt {
+	/******* objects manager ********/
+	int dev_lock_count;
+	int dev_get_count;
+
+	/******** init level 0 ********/
+	const char * name;					//!< pointer to the start of name, for open or create
+	u32 name_len;						//!< name length
+	u16 sum;							//!< sum of name
+	uffs_Device *dev;					//!< uffs device
+	u32 oflag;
+	u8 type;
+	u16 head_pages;						//!< data pages on file head block
+	u16 parent;
+
+	/******* init level 1 ********/
+	TreeNode *node;						//!< file entry node in tree
+	u16 serial;
+	
+	/******* output ******/
+	int err;							//!< error number
+
+	/******* current *******/
+	u32 pos;							//!< current position in file
+
+	/***** others *******/
+	UBOOL attr_loaded;					//!< attributes loaded ?
+	UBOOL open_succ;					//!< U_TRUE or U_FALSE
+
+};
+
+typedef struct uffs_ObjectSt uffs_Object;
+
+
+
+#define uffs_GetObjectErr(obj) ((obj)->err)
+#define uffs_ClearObjectErr(obj) do { (obj)->err = UENOERR; } while (0)
+
+uffs_Pool * uffs_GetObjectPool(void);
+
+URET uffs_InitObjectBuf(void);
+URET uffs_ReleaseObjectBuf(void);
+int uffs_PutAllObjectBuf(uffs_Device *dev);
+uffs_Object * uffs_GetObject(void);
+void uffs_PutObject(uffs_Object *obj);
+int uffs_GetObjectIndex(uffs_Object *obj);
+uffs_Object * uffs_GetObjectByIndex(int idx);
+
+
+/**
+ * Re-initialize an object.
+ * should call this function if you want to re-use an object.
+ */
+URET uffs_ReInitObject(uffs_Object *obj);
+
+URET uffs_ParseObject(uffs_Object *obj, const char *name);
+
+URET uffs_CreateObjectEx(uffs_Object *obj, uffs_Device *dev, 
+								   int dir, const char *name, int name_len, int oflag);
+URET uffs_OpenObjectEx(uffs_Object *obj, uffs_Device *dev, 
+								   int dir, const char *name, int name_len, int oflag);
+
+URET uffs_OpenObject(uffs_Object *obj, const char *fullname, int oflag);
+URET uffs_TruncateObject(uffs_Object *obj, u32 remain);
+URET uffs_CreateObject(uffs_Object *obj, const char *fullname, int oflag);
+
+URET uffs_CloseObject(uffs_Object *obj);
+int uffs_WriteObject(uffs_Object *obj, const void *data, int len);
+int uffs_ReadObject(uffs_Object *obj, void *data, int len);
+long uffs_SeekObject(uffs_Object *obj, long offset, int origin);
+int uffs_GetCurOffset(uffs_Object *obj);
+int uffs_EndOfFile(uffs_Object *obj);
+URET uffs_FlushObject(uffs_Object *obj);
+
+URET uffs_RenameObject(const char *old_name, const char *new_name, int *err);
+URET uffs_DeleteObject(const char * name, int *err);
+
+int uffs_GetFreeObjectHandlers(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 83 - 0
src/inc/uffs/uffs_mem.h

@@ -0,0 +1,83 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_MEM_H_
+#define _UFFS_MEM_H_
+
+#include "uffs/uffs_device.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/** uffs memory allocator */
+typedef struct uffs_memAllocatorSt {
+	URET (*init)(struct uffs_DeviceSt *dev);			//!< init memory allocator, setup buffer sizes
+	URET (*release)(struct uffs_DeviceSt *dev);			//!< release memory allocator (for dynamic memory allocation)
+	
+	void * (*malloc)(struct uffs_DeviceSt *dev, unsigned int size); //!< allocate memory (for dynamic memory allocation)
+	URET (*free)(struct uffs_DeviceSt *dev, void *p);   //!< free memory (for dynamic memory allocation)
+
+	void * blockinfo_pool_buf;			//!< block info cache buffers
+	void * pagebuf_pool_buf;			//!< page buffers
+	void * tree_nodes_pool_buf;			//!< tree nodes buffer
+	void * spare_pool_buf;				//!< spare buffers
+
+	int blockinfo_pool_size;			//!< block info cache buffers size
+	int pagebuf_pool_size;				//!< page buffers size
+	int tree_nodes_pool_size;			//!< tree nodes buffer size
+	int spare_pool_size;				//!< spare buffer pool size
+
+	uffs_Pool tree_pool;
+	uffs_Pool spare_pool;
+
+	int spare_data_size;				//!< spare size consumed by UFFS, 
+										//!< calculated by UFFS according to the layout information.
+
+	/* for static memory allocator */
+	char *buf_start;
+	int buf_size;
+	int pos;
+
+
+} uffs_MemAllocator;
+
+void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator);
+void uffs_MemSetupStaticAllocator(uffs_MemAllocator *allocator, void *pool, int size);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 96 - 0
src/inc/uffs/uffs_mtb.h

@@ -0,0 +1,96 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_mtb.h
+ * \brief mount table related stuff
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_MTB_H_
+#define _UFFS_MTB_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+typedef struct uffs_MountTableEntrySt {
+	uffs_Device *dev;		// UFFS 'device' - core internal data structure for partition
+	int start_block;		// partition start block
+	int end_block;			// partition end block ( if < 0, reserve space form the end of storage)
+	const char *mount;		// mount point
+	struct uffs_MountTableEntrySt *prev;
+	struct uffs_MountTableEntrySt *next;
+} uffs_MountTable;
+
+/** Register mount entry, will be put at 'unmounted' list */
+int uffs_RegisterMountTable(uffs_MountTable *mtb);
+
+/** Remove mount entry from the list */
+int uffs_UnRegisterMountTable(uffs_MountTable *mtb);
+
+/** mount partition */
+int uffs_Mount(const char *mount);
+
+/** unmount parttion */
+int uffs_UnMount(const char *mount);
+
+/** get mounted entry list */
+uffs_MountTable * uffs_MtbGetMounted(void);
+
+/** get unmounted entry list */
+uffs_MountTable * uffs_MtbGetUnMounted(void);
+
+/** get matched mount point from absolute path */
+int uffs_GetMatchedMountPointSize(const char *path);			
+
+/** get uffs device from mount point */
+uffs_Device * uffs_GetDeviceFromMountPoint(const char *mount);	
+
+/** get uffs device from mount point */
+uffs_Device * uffs_GetDeviceFromMountPointEx(const char *mount, int len);	
+
+/** get mount point name from uffs device */
+const char * uffs_GetDeviceMountPoint(uffs_Device *dev);		
+
+/** down crease uffs device references by uffs_GetDeviceXXX() */
+void uffs_PutDevice(uffs_Device *dev);							
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 75 - 0
src/inc/uffs/uffs_os.h

@@ -0,0 +1,75 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_OS_H_
+#define _UFFS_OS_H_
+
+#include <rtthread.h>
+#define RT_THREAD
+#if !defined(RT_THREAD)
+#include <stdarg.h>
+#endif
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define UFFS_TASK_ID_NOT_EXIST	-1
+
+typedef void * OSSEM;
+#define OSSEM_NOT_INITED	(NULL)
+
+#if !defined(RT_THREAD)
+struct uffs_DebugMsgOutputSt {
+	void (*output)(const char *msg);
+	void (*vprintf)(const char *fmt, va_list args);
+};
+
+void uffs_SetupDebugOutput(void);
+#endif
+
+/* OS specific functions */
+int uffs_SemCreate(OSSEM *sem);
+int uffs_SemWait(OSSEM sem);
+int uffs_SemSignal(OSSEM sem);
+int uffs_SemDelete(OSSEM *sem);
+
+int uffs_OSGetTaskId(void);	//get current task id
+unsigned int uffs_GetCurDateTime(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 94 - 0
src/inc/uffs/uffs_pool.h

@@ -0,0 +1,94 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_pool.h
+ * \brief Fast fixed size memory pool management.
+ * \author Ricky Zheng, Simon Kallweit
+ */
+
+#ifndef _UFFS_POOL_H_
+#define _UFFS_POOL_H_
+
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_os.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/**
+ * \struct uffs_PoolEntrySt
+ * \brief Helper type for free buffer entries.
+ */
+typedef struct uffs_PoolEntrySt {
+    struct uffs_PoolEntrySt *next;
+} uffs_PoolEntry;
+
+/**
+ * \struct uffs_PoolSt
+ * \brief Memory pool.
+ */
+typedef struct uffs_PoolSt {
+	u8 *mem;					//!< memory pool
+	u32 buf_size;				//!< size of a buffer
+	u32 num_bufs;				//!< number of buffers in the pool
+	uffs_PoolEntry *free_list;	//!< linked list of free buffers
+	OSSEM sem;					//!< buffer lock
+} uffs_Pool;
+
+URET uffs_PoolInit(uffs_Pool *pool, void *mem, u32 mem_size, u32 buf_size, u32 num_bufs);
+URET uffs_PoolRelease(uffs_Pool *pool);
+
+UBOOL uffs_PoolVerify(uffs_Pool *pool, void *p);
+
+void *uffs_PoolGet(uffs_Pool *pool);
+void *uffs_PoolGetLocked(uffs_Pool *pool);
+
+int uffs_PoolPut(uffs_Pool *pool, void *p);
+int uffs_PoolPutLocked(uffs_Pool *pool, void *p);
+
+void *uffs_PoolGetBufByIndex(uffs_Pool *pool, u32 index);
+u32 uffs_PoolGetIndex(uffs_Pool *pool, void *p);
+UBOOL uffs_PoolCheckFreeList(uffs_Pool *pool, void *p);
+
+void * uffs_PoolFindNextAllocated(uffs_Pool *pool, void *from);
+
+int uffs_PoolGetFreeCount(uffs_Pool *pool);
+int uffs_PoolPutAll(uffs_Pool *pool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _UFFS_POOL_H_

+ 336 - 0
src/inc/uffs/uffs_public.h

@@ -0,0 +1,336 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_public.h
+ * \brief public data structures for uffs
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_PUBLIC_H_
+#define _UFFS_PUBLIC_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs.h"
+#include "uffs/uffs_pool.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))
+#endif
+
+#ifndef offsetof
+# define offsetof(T, x) ((size_t) &((T *)0)->x)
+#endif
+#ifndef container_of
+#define container_of(p, T, x) ((T *)((char *)(p) - offsetof(T,x)))
+#endif
+
+/** 
+ * \def MAX_FILENAME_LENGTH 
+ * \note Be careful: it's part of the physical format (see: uffs_FileInfoSt.name)
+ *    !!DO NOT CHANGE IT AFTER FILE SYSTEM IS FORMATED!!
+ */
+#define MAX_FILENAME_LENGTH         128
+
+/** \note 8-bits attr goes to uffs_dirent::d_type */
+#define FILE_ATTR_DIR       (1 << 7)    //!< attribute for directory
+#define FILE_ATTR_WRITE     (1 << 0)    //!< writable
+
+
+/**
+ * \structure uffs_FileInfoSt
+ * \brief file/dir entry info in physical storage format
+ */
+struct uffs_FileInfoSt {
+    u32 attr;               //!< file/dir attribute
+    u32 create_time;
+    u32 last_modify;
+    u32 access;
+    u32 reserved;
+    u32 name_len;           //!< length of file/dir name
+    char name[MAX_FILENAME_LENGTH];
+}; //6*4 + sizeof(name) = 24 + 128 = 152 Bytes
+typedef struct uffs_FileInfoSt uffs_FileInfo;
+
+/**
+ * \struct uffs_ObjectInfoSt
+ * \brief object info
+ */
+typedef struct uffs_ObjectInfoSt {
+    uffs_FileInfo info;
+    u32 len;                //!< length of file
+    u16 serial;             //!< object serial num
+} uffs_ObjectInfo;
+
+
+/**
+ * \struct uffs_TagStoreSt
+ * \brief uffs tag, 8 bytes, will be store in page spare area.
+ */
+struct uffs_TagStoreSt {
+	u32 dirty:1;		//!< 0: dirty, 1: clear
+	u32 valid:1;		//!< 0: valid, 1: invalid
+	u32 type:2;			//!< block type: #UFFS_TYPE_DIR, #UFFS_TYPE_FILE, #UFFS_TYPE_DATA
+	u32 block_ts:2;		//!< time stamp of block;
+	u32 data_len:12;	//!< length of page data
+	u32 serial:14;		//!< serial number
+
+	u32 parent:10;		//!< parent's serial number
+	u32 page_id:6;		//!< page id
+	u32 reserved:4;		//!< reserved, for UFFS2
+	u32 tag_ecc:12;		//!< tag ECC
+};
+
+#define TAG_ECC_DEFAULT (0xFFF)	//!< 12-bit '1'
+
+
+/** 
+ * \struct uffs_TagsSt
+ */
+struct uffs_TagsSt {
+	struct uffs_TagStoreSt s;		/* store must be the first member */
+
+	/** data_sum for file or dir name */
+	u16 data_sum;
+
+	/** internal used */
+	u8 seal_byte;			//!< seal byte.
+};
+
+/** 
+ * \struct uffs_MiniHeaderSt
+ * \brief the mini header resides on the head of page data
+ */
+struct uffs_MiniHeaderSt {
+	u8 status;
+	u8 reserved;
+	u16 crc;
+};
+
+
+/** uffs_TagsSt.dirty */
+#define TAG_VALID		0
+#define TAG_INVALID		1
+
+/** uffs_TagsSt.valid */
+#define TAG_DIRTY		0
+#define TAG_CLEAR		1
+
+#define TAG_IS_DIRTY(tag) (*((u32 *) &((tag)->s)) != 0xFFFFFFFF)	// tag is dirty if first 4 bytes not all 0xFF
+#define TAG_IS_VALID(tag) ((tag)->s.valid == TAG_VALID)
+#define TAG_IS_SEALED(tag) ((tag)->seal_byte != 0xFF)
+
+#define TAG_IS_GOOD(tag) (TAG_IS_SEALED(tag) && TAG_IS_VALID(tag))
+
+#define TAG_VALID_BIT(tag) (tag)->s.valid
+#define TAG_DIRTY_BIT(tag) (tag)->s.dirty
+#define TAG_SERIAL(tag) (tag)->s.serial
+#define TAG_PARENT(tag) (tag)->s.parent
+#define TAG_PAGE_ID(tag) (tag)->s.page_id
+#define TAG_DATA_LEN(tag) (tag)->s.data_len
+#define TAG_TYPE(tag) (tag)->s.type
+#define TAG_BLOCK_TS(tag) (tag)->s.block_ts
+#define SEAL_TAG(tag) (tag)->seal_byte = 0
+
+int uffs_GetFirstBlockTimeStamp(void);
+int uffs_GetNextBlockTimeStamp(int prev);
+UBOOL uffs_IsSrcNewerThanObj(int src, int obj);
+
+
+#include "uffs_device.h"
+
+
+
+/********************************** debug & error *************************************/
+#define UFFS_MSG_NOISY		-1
+#define UFFS_MSG_NORMAL		0
+#define UFFS_MSG_SERIOUS	1
+#define UFFS_MSG_DEAD		2
+#define UFFS_MSG_NOMSG		100
+
+#define TENDSTR "\n"
+
+#if !defined(RT_THREAD)
+struct uffs_DebugMsgOutputSt;
+URET uffs_InitDebugMessageOutput(struct uffs_DebugMsgOutputSt *ops, int msg_level);
+void uffs_DebugSetMessageLevel(int msg_level);
+
+void uffs_DebugMessage(int level, const char *prefix, const char *suffix, const char *errFmt, ...);
+void uffs_AssertCall(const char *file, int line, const char *msg, ...);
+#else
+
+#define UFFS_DBG_LEVEL  UFFS_MSG_NORMAL
+
+#ifdef CONFIG_ENABLE_UFFS_DEBUG_MSG
+#define uffs_DebugMessage(level, prefix, suffix, errFmt, ...) do { \
+	if (level >= UFFS_DBG_LEVEL) \
+		rt_kprintf(prefix errFmt suffix, ##__VA_ARGS__); \
+} while(0)
+
+#define uffs_AssertCall(file, line, msg, ...) \
+	rt_kprintf("ASSERT %s:%d - :" msg "\n", (const char *)file, (int)line, ##__VA_ARGS__)
+#else
+#define uffs_DebugMessage(level, prefix, suffix, errFmt, ...)
+#define uffs_AssertCall(file, line, msg, ...)
+#endif //CONFIG_ENABLE_UFFS_DEBUG_MSG
+#endif //RT_THREAD
+
+#ifdef _COMPILER_DO_NOT_SUPPORT_MACRO_VALIST_REPLACE_
+/* For those compilers do not support valist parameter replace in macro define */
+void uffs_Perror(int level, const char *fmt, ...);
+void uffs_PerrorRaw(int level, const char *fmt, ...);
+UBOOL uffs_Assert(UBOOL expr, const char *fmt, ...);
+#else
+
+#if !defined(RT_THREAD)
+#define uffs_Perror(level, fmt, ... ) \
+	uffs_DebugMessage(level, PFX, TENDSTR, fmt, ## __VA_ARGS__)
+
+#define uffs_PerrorRaw(level, fmt, ... ) \
+	uffs_DebugMessage(level, NULL, NULL, fmt, ## __VA_ARGS__)
+
+#else
+
+#ifdef CONFIG_ENABLE_UFFS_DEBUG_MSG
+
+#define uffs_Perror(level, fmt, ... ) do{\
+	if (level >= UFFS_DBG_LEVEL) \
+		rt_kprintf(PFX fmt TENDSTR, ##__VA_ARGS__); \
+} while(0)
+
+#define uffs_PerrorRaw(level, fmt, ... ) do{\
+	if (level >= UFFS_DBG_LEVEL) \
+		rt_kprintf(fmt, ##__VA_ARGS__); \
+} while(0)
+#else
+#define uffs_Perror(level, fmt, ... )
+#define uffs_PerrorRaw(level, fmt, ... )
+#endif // CONFIG_ENABLE_UFFS_DEBUG_MSG
+#endif // RT_THREAD
+
+#define uffs_Assert(expr, msg, ...) \
+	((expr) ? U_TRUE : (uffs_AssertCall(__FILE__, __LINE__, msg, ## __VA_ARGS__), U_FALSE))
+
+#endif //_COMPILER_DO_NOT_SUPPORT_MACRO_VALIST_REPLACE_
+
+#define uffs_Panic() \
+	do { \
+		uffs_AssertCall(__FILE__, __LINE__, "Bam !!\n"); \
+		while(1); \
+	} while(0)
+
+/********************************** NAND **********************************************/
+//NAND flash specific file must implement these interface
+URET uffs_LoadPageSpare(uffs_Device *dev, int block, int page, uffs_Tags *tag);
+URET uffs_WritePageSpare(uffs_Device *dev, int block, int page, uffs_Tags *tag);
+URET uffs_MakePageValid(uffs_Device *dev, int block, int page, uffs_Tags *tag);
+UBOOL uffs_IsBlockBad(uffs_Device *dev, uffs_BlockInfo *bc);
+
+/********************************** Public defines *****************************/
+/**
+ * \def UFFS_ALL_PAGES 
+ * \brief UFFS_ALL_PAGES if this value presented, that means the objects are all pages in the block
+ */
+#define UFFS_ALL_PAGES (0xffff)
+
+/** 
+ * \def UFFS_INVALID_PAGE
+ * \brief macro for invalid page number
+ */
+#define UFFS_INVALID_PAGE	(0xfffe)
+
+/** 
+ * \def UFFS_INVALID_BLOCK
+ * \brief macro for invalid block number
+ */
+#define UFFS_INVALID_BLOCK	(0xfffe)
+
+
+URET uffs_NewBlock(uffs_Device *dev, u16 block, uffs_Tags *tag, uffs_Buf *buf);
+URET uffs_BlockRecover(uffs_Device *dev, uffs_BlockInfo *old, u16 newBlock);
+URET uffs_PageRecover(uffs_Device *dev, 
+					  uffs_BlockInfo *bc, 
+					  u16 oldPage, 
+					  u16 newPage, 
+					  uffs_Buf *buf);
+int uffs_FindFreePageInBlock(uffs_Device *dev, uffs_BlockInfo *bc);
+u16 uffs_FindBestPageInBlock(uffs_Device *dev, uffs_BlockInfo *bc, u16 page);
+u16 uffs_FindFirstFreePage(uffs_Device *dev, uffs_BlockInfo *bc, u16 pageFrom);
+u16 uffs_FindPageInBlockWithPageId(uffs_Device *dev, uffs_BlockInfo *bc, u16 page_id);
+
+u8 uffs_MakeSum8(const void *p, int len);
+u16 uffs_MakeSum16(const void *p, int len);
+URET uffs_CreateNewFile(uffs_Device *dev, u16 parent, u16 serial, uffs_BlockInfo *bc, uffs_FileInfo *fi);
+
+int uffs_GetBlockFileDataLength(uffs_Device *dev, uffs_BlockInfo *bc, u8 type);
+UBOOL uffs_IsPageErased(uffs_Device *dev, uffs_BlockInfo *bc, u16 page);
+int uffs_GetFreePagesCount(uffs_Device *dev, uffs_BlockInfo *bc);
+UBOOL uffs_IsDataBlockReguFull(uffs_Device *dev, uffs_BlockInfo *bc);
+UBOOL uffs_IsThisBlockUsed(uffs_Device *dev, uffs_BlockInfo *bc);
+
+int uffs_GetBlockTimeStamp(uffs_Device *dev, uffs_BlockInfo *bc);
+
+int uffs_GetDeviceUsed(uffs_Device *dev);
+int uffs_GetDeviceFree(uffs_Device *dev);
+int uffs_GetDeviceTotal(uffs_Device *dev);
+
+URET uffs_LoadMiniHeader(uffs_Device *dev, int block, u16 page, struct uffs_MiniHeaderSt *header);
+
+
+/* some functions from uffs_fd.c */
+void uffs_FdSignatureIncrease(void);
+URET uffs_DirEntryBufInit(void);
+URET uffs_DirEntryBufRelease(void);
+uffs_Pool * uffs_DirEntryBufGetPool(void);
+int uffs_DirEntryBufPutAll(uffs_Device *dev);
+
+
+/************************************************************************/
+/*  init functions                                                                     */
+/************************************************************************/
+URET uffs_InitDevice(uffs_Device *dev);
+URET uffs_ReleaseDevice(uffs_Device *dev);
+
+URET uffs_InitFileSystemObjects(void);
+URET uffs_ReleaseFileSystemObjects(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif	// _UFFS_PUBLIC_H_
+

+ 237 - 0
src/inc/uffs/uffs_tree.h

@@ -0,0 +1,237 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_TREE_H_
+#define _UFFS_TREE_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_pool.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define UFFS_TYPE_DIR		0
+#define UFFS_TYPE_FILE		1
+#define UFFS_TYPE_DATA		2
+#define UFFS_TYPE_RESV		3
+#define UFFS_TYPE_INVALID	0xFF
+
+struct uffs_NodeTypeNameMapSt {
+	int type;
+	const char *name;
+};
+
+#define UFFS_TYPE_NAME_MAP { \
+	{UFFS_TYPE_DIR, "DIR"}, \
+	{UFFS_TYPE_FILE, "FILE"}, \
+	{UFFS_TYPE_DATA, "DATA"}, \
+	{UFFS_TYPE_RESV, "RESV"}, \
+	{UFFS_TYPE_INVALID, "INVALID"} \
+}
+
+struct BlockListSt {	/* 12 bytes */
+	struct uffs_TreeNodeSt * next;
+	struct uffs_TreeNodeSt * prev;
+	u16 block;
+	union {
+		u16 serial;    /* for suspended block list */
+		u8 need_check; /* for erased block list */
+	} u;
+};
+
+struct DirhSt {		/* 8 bytes */
+	u16 checksum;	/* check sum of dir name */
+	u16 block;
+	u16 parent;
+	u16 serial;
+};
+
+
+struct FilehSt {	/* 12 bytes */
+	u16 block;
+	u16 checksum;	/* check sum of file name */
+	u16 parent;
+	u16 serial;
+	u32 len;		/* file length total */
+};
+
+struct FdataSt {	/* 10 bytes */
+	u16 block;
+	u16 parent;
+	u32 len;		/* file data length on this block */
+	u16 serial;
+};
+
+//UFFS TreeNode (14 or 16 bytes)
+typedef struct uffs_TreeNodeSt {
+	union {
+		struct BlockListSt list;
+		struct DirhSt dir;
+		struct FilehSt file;
+		struct FdataSt data;
+	} u;
+	u16 hash_next;		
+	u16 hash_prev;			
+} TreeNode;
+
+
+//TODO: UFFS2 Tree structures
+/*
+struct FdataSt {
+	u32 len;
+};
+
+struct filebSt {
+	u16 bls;		//how many blocks this file contents ...
+	u8 offs;		//the offset of this file header on FILE block
+	u8 sum;			//short sum of file name
+};
+
+//Extra data structure for storing file length information
+struct FilehSt {
+	u32 len;
+};
+
+//UFFS2 TreeNode (12 bytes)
+typedef struct uffs_TreeNodeSt {
+	u16 nextIdx;
+	u16 block;
+	u16 parent;
+	u16 serial;
+	union {
+		struct FilehSt h;
+		struct filedSt file;
+		struct data;
+	} u;
+} TreeNode;
+
+*/
+
+
+#define EMPTY_NODE 0xffff				//!< special index num of empty node.
+
+#define ROOT_DIR_SERIAL	0				//!< serial num of root dir
+#define MAX_UFFS_FSN			0x3ff	//!< maximum dir|file serial number (uffs_TagStore#parent: 10 bits)
+#define MAX_UFFS_FDN			0x3fff	//!< maximum file data block serial numbers (uffs_TagStore#serial: 14 bits)
+#define PARENT_OF_ROOT			0xfffd	//!< parent of ROOT ? kidding me ...
+#define INVALID_UFFS_SERIAL		0xffff	//!< invalid serial num
+
+#define DIR_NODE_HASH_MASK		0x1f
+#define DIR_NODE_ENTRY_LEN		(DIR_NODE_HASH_MASK + 1)
+
+#define FILE_NODE_HASH_MASK		0x3f
+#define FILE_NODE_ENTRY_LEN		(FILE_NODE_HASH_MASK + 1)
+
+#define DATA_NODE_HASH_MASK		0x1ff
+#define DATA_NODE_ENTRY_LEN		(DATA_NODE_HASH_MASK + 1)
+#define FROM_IDX(idx, pool)		((TreeNode *)uffs_PoolGetBufByIndex(pool, idx))
+#define TO_IDX(p, pool)			((u16)uffs_PoolGetIndex(pool, (void *) p))
+
+
+#define GET_FILE_HASH(serial)			(serial & FILE_NODE_HASH_MASK)
+#define GET_DIR_HASH(serial)			(serial & DIR_NODE_HASH_MASK)
+#define GET_DATA_HASH(parent, serial)	((parent + serial) & DATA_NODE_HASH_MASK)
+
+
+struct uffs_TreeSt {
+	TreeNode *erased;					//!< erased block list head
+	TreeNode *erased_tail;				//!< erased block list tail
+	TreeNode *suspend;					//!< suspended block list
+	int erased_count;					//!< erased block counter
+	TreeNode *bad;						//!< bad block list
+	int bad_count;						//!< bad block count
+	u16 dir_entry[DIR_NODE_ENTRY_LEN];
+	u16 file_entry[FILE_NODE_ENTRY_LEN];
+	u16 data_entry[DATA_NODE_ENTRY_LEN];
+	u16 max_serial;
+};
+
+
+URET uffs_TreeInit(uffs_Device *dev);
+URET uffs_TreeRelease(uffs_Device *dev);
+URET uffs_BuildTree(uffs_Device *dev);
+u16 uffs_FindFreeFsnSerial(uffs_Device *dev);
+TreeNode * uffs_TreeFindFileNode(uffs_Device *dev, u16 serial);
+TreeNode * uffs_TreeFindFileNodeWithParent(uffs_Device *dev, u16 parent);
+TreeNode * uffs_TreeFindDirNode(uffs_Device *dev, u16 serial);
+TreeNode * uffs_TreeFindDirNodeWithParent(uffs_Device *dev, u16 parent);
+TreeNode * uffs_TreeFindFileNodeByName(uffs_Device *dev, const char *name, u32 len, u16 sum, u16 parent);
+TreeNode * uffs_TreeFindDirNodeByName(uffs_Device *dev, const char *name, u32 len, u16 sum, u16 parent);
+TreeNode * uffs_TreeFindDataNode(uffs_Device *dev, u16 parent, u16 serial);
+
+
+TreeNode * uffs_TreeFindDirNodeByBlock(uffs_Device *dev, u16 block);
+TreeNode * uffs_TreeFindFileNodeByBlock(uffs_Device *dev, u16 block);
+TreeNode * uffs_TreeFindDataNodeByBlock(uffs_Device *dev, u16 block);
+TreeNode * uffs_TreeFindErasedNodeByBlock(uffs_Device *dev, u16 block);
+TreeNode * uffs_TreeFindBadNodeByBlock(uffs_Device *dev, u16 block);
+
+void uffs_TreeSuspendAdd(uffs_Device *dev, TreeNode *node);
+TreeNode * uffs_TreeFindSuspendNode(uffs_Device *dev, u16 serial);
+void uffs_TreeRemoveSuspendNode(uffs_Device *dev, TreeNode *node);
+
+#define SEARCH_REGION_DIR		1
+#define SEARCH_REGION_FILE		2
+#define SEARCH_REGION_DATA		4
+#define SEARCH_REGION_BAD		8
+#define SEARCH_REGION_ERASED	16
+TreeNode * uffs_TreeFindNodeByBlock(uffs_Device *dev, u16 block, int *region);
+
+
+
+UBOOL uffs_TreeCompareFileName(uffs_Device *dev, const char *name, u32 len, u16 sum, TreeNode *node, int type);
+
+TreeNode * uffs_TreeGetErasedNode(uffs_Device *dev);
+
+void uffs_InsertNodeToTree(uffs_Device *dev, u8 type, TreeNode *node);
+void uffs_InsertToErasedListHead(uffs_Device *dev, TreeNode *node);
+void uffs_TreeInsertToErasedListTail(uffs_Device *dev, TreeNode *node);
+void uffs_TreeInsertToErasedListTailEx(uffs_Device *dev, TreeNode *node, int need_check);
+void uffs_TreeInsertToBadBlockList(uffs_Device *dev, TreeNode *node);
+
+void uffs_BreakFromEntry(uffs_Device *dev, u8 type, TreeNode *node);
+
+void uffs_TreeSetNodeBlock(u8 type, TreeNode *node, u16 block);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif
+
+

+ 160 - 0
src/inc/uffs/uffs_types.h

@@ -0,0 +1,160 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_TYPES_H_
+#define _UFFS_TYPES_H_
+
+#ifdef __CDT_PARSER__
+#undef __cplusplus
+#endif
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#ifdef _UBASE_
+#include <sys/utypes.h>
+#endif
+
+/** 
+ * \file uffs_types.h
+ * \brief basic types used on uffs
+ * \author Ricky Zheng
+ */
+
+/* basic types */
+
+/** \typedef i8 
+ * \brief 8 bit integer
+ */
+typedef char i8;
+
+/** \typedef u8 
+ * \brief 8 bit unsigned integer
+ */
+typedef unsigned char u8;
+
+/** \typedef i16 
+ * \brief 16 bit integer
+ */
+typedef short int i16;
+
+
+/** \typedef u16
+ * \brief 16 bit unsigned integer
+ */
+typedef unsigned short int u16;
+
+
+/** \typedef i32
+ * \brief 32 bit integer
+ */
+typedef int i32;
+
+/** \typedef u32 
+ * \brief 32 bit unsigned integer
+ */
+typedef unsigned int u32;
+
+
+#ifndef _UBASE_
+
+#ifndef TRUE
+#define TRUE	1
+#endif
+
+#ifndef FALSE
+#define FALSE	0
+#endif
+
+/* boolean type */
+
+/** \typedef UBOOL 
+ * \brief boolean type for uffs, the value would be: #U_TRUE or #U_FALSE
+ */
+typedef int UBOOL;
+
+/** \def U_TRUE 
+ * \brief boolean true for uffs
+ */
+#define U_TRUE (TRUE)
+
+
+/** \def U_FALSE 
+ * \brief boolean false for uffs
+ */
+#define U_FALSE (FALSE)
+
+
+/** \typedef URET 
+ * \brief return type for uffs, should be #U_FAIL or #U_SUCC
+ */
+typedef int URET;
+
+/** \def U_FAIL 
+ * \brief indicator of fail
+ */
+#define U_FAIL -1
+
+/** \def U_SUCC 
+ * \brief indicator of successful
+ */
+#define U_SUCC 0
+
+/** \def IS_SUCC(ret) 
+ * \brief is it successful ?
+ */
+#define IS_SUCC(ret) (ret >= 0 ? U_TRUE : U_FALSE)
+
+
+/** \def IS_FAIL(ret) 
+ * \brief is it fail ?
+ */
+#define IS_FAIL(ret) (ret < 0 ? U_TRUE : U_FALSE)
+
+#ifndef NULL
+/** \def NULL 
+ * \brief zero for pointer
+ */
+#define NULL 0
+#endif
+
+#endif // _UBASE_
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 64 - 0
src/inc/uffs/uffs_utils.h

@@ -0,0 +1,64 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_UTILS_H_
+#define _UFFS_UTILS_H_
+
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_core.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+//get UFFS disk version, if fail, return 0
+int uffs_GetUFFSVersion(struct uffs_DeviceSt *dev);
+
+void uffs_InitGlobalFsLock(void);
+void uffs_ReleaseGlobalFsLock(void);
+void uffs_GlobalFsLockLock(void);
+void uffs_GlobalFsLockUnlock(void);
+
+URET uffs_FormatDevice(uffs_Device *dev, UBOOL force);
+
+#ifdef __cplusplus
+}
+#endif
+
+typedef void dump_msg_cb(struct uffs_DeviceSt *dev, const char *fmt, ...);
+
+void uffs_DumpDevice(struct uffs_DeviceSt *dev, dump_msg_cb *dump);
+
+#endif
+

+ 55 - 0
src/inc/uffs/uffs_version.h

@@ -0,0 +1,55 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef _UFFS_VERSION_H_
+#define _UFFS_VERSION_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+#define UFFS_VERSION		0x01030400	//"01.03.0400"
+
+const char * uffs_Version2Str(int ver);
+int uffs_GetVersion(void);
+int uffs_GetMainVersion(int ver);
+int uffs_GetMinorVersion(int ver);
+int uffs_GetTrivialVersion(int ver);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+

+ 13 - 0
src/platform/CMakeLists.txt

@@ -0,0 +1,13 @@
+IF (UNIX)
+	SET (libplatform_SRCS posix/uffs_os.c posix/uffs_config.h)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/posix)
+ENDIF()
+
+IF (WIN32)
+	SET (libplatform_SRCS win32/uffs_os.c win32/uffs_config.h)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/win32)
+ENDIF()
+
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc)
+ADD_LIBRARY(platform STATIC ${libplatform_SRCS} )
+

+ 322 - 0
src/platform/posix/uffs_config.h

@@ -0,0 +1,322 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_config.h
+ * \brief basic configuration of uffs
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_CONFIG_H_
+#define _UFFS_CONFIG_H_
+
+/**
+ * \def UFFS_MAX_PAGE_SIZE
+ * \note maximum page size UFFS support
+ */
+#define UFFS_MAX_PAGE_SIZE		2048
+
+/**
+ * \def UFFS_MAX_SPARE_SIZE
+ */
+#define UFFS_MAX_SPARE_SIZE ((UFFS_MAX_PAGE_SIZE / 256) * 8)
+
+/**
+ * \def UFFS_MAX_ECC_SIZE
+ */
+#define UFFS_MAX_ECC_SIZE  ((UFFS_MAX_PAGE_SIZE / 256) * 5)
+
+/**
+ * \def MAX_CACHED_BLOCK_INFO
+ * \note uffs cache the block info for opened directories and files,
+ *       a practical value is 5 ~ MAX_OBJECT_HANDLE
+ */
+#define MAX_CACHED_BLOCK_INFO	50
+
+/** 
+ * \def MAX_PAGE_BUFFERS
+ * \note the bigger value will bring better read/write performance.
+ *       but few writing performance will be improved when this 
+ *       value is become larger than 'max pages per block'
+ */
+#define MAX_PAGE_BUFFERS		40
+
+
+/** 
+ * \def CLONE_BUFFER_THRESHOLD
+ * \note reserve buffers for clone. 1 or 2 should be enough.
+ */
+#define CLONE_BUFFERS_THRESHOLD	2
+
+/**
+ * \def MAX_SPARE_BUFFERS
+ * \note spare buffers are used for lower level flash operations,
+ *		 5 should be enough.
+ */
+#define MAX_SPARE_BUFFERS		5
+
+
+/**
+ * \def MAX_DIRTY_PAGES_IN_A_BLOCK 
+ * \note this value should be between '2' and the lesser of
+ *		 'max pages per block' and (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1).
+ *
+ *       the smaller the value the frequently the buffer will be flushed.
+ */
+#define MAX_DIRTY_PAGES_IN_A_BLOCK 32	
+
+/**
+ * \def MAX_DIRTY_BUF_GROUPS
+ */
+#define MAX_DIRTY_BUF_GROUPS	3
+
+/**
+ * \def CONFIG_ENABLE_UFFS_DEBUG_MSG
+ * \note Enable debug message output. You must call uffs_InitDebugMessageOutput()
+ *      to initialize debug apart from enable debug feature.
+ */
+#define CONFIG_ENABLE_UFFS_DEBUG_MSG
+
+/**
+ * \def CONFIG_USE_GLOBAL_FS_LOCK
+ * \note use global lock instead of per-device lock.
+ *       this is required if you use fd APIs in multi-thread environment.
+ */
+#define CONFIG_USE_GLOBAL_FS_LOCK
+
+
+/**
+ * \def CONFIG_USE_PER_DEVICE_LOCK
+ * \note use per-device lock.
+ *		 this is required if you use fs APIs in multi-thread environment.
+ */
+//#define CONFIG_USE_PER_DEVICE_LOCK
+
+
+
+/**
+ * \def CONFIG_USE_STATIC_MEMORY_ALLOCATOR
+ * \note uffs will use static memory allocator if this is defined.
+ *       to use static memory allocator, you need to provide memory
+ *       buffer when creating uffs_Device.
+ *
+ *       use UFFS_STATIC_BUFF_SIZE() to calculate memory buffer size.
+ */
+#define CONFIG_USE_STATIC_MEMORY_ALLOCATOR 0
+
+/**
+ * \def CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR
+ * \note  using system platform's 'malloc' and 'free'.
+ */
+#define CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR 1
+
+
+
+/** 
+ * \def CONFIG_FLUSH_BUF_AFTER_WRITE
+ * \note UFFS will write all data directly into flash in 
+ *       each 'write' call if you enable this option.
+ *       (which means lesser data lost when power failure but
+ *		 poorer writing performance).
+ *		 It's not recommended to open this define for normal applications.
+ */
+#define CONFIG_FLUSH_BUF_AFTER_WRITE
+
+
+/**
+ * \def CONFIG_UFFS_AUTO_LAYOUT_MTD_COMP
+ * \note Use Linux MTD compatiable spare placement for UFFS_LAYOUT_AUTO,
+ *       only valid for page data size 512 or 2048.
+ */
+//#define CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME
+
+
+/** 
+ * \def MAX_OBJECT_HANDLE
+ * maximum number of object handle 
+ */
+#define MAX_OBJECT_HANDLE	50
+#define FD_SIGNATURE_SHIFT	6	
+
+
+/**
+ * \def MAX_DIR_HANDLE
+ * maximum number of uffs_DIR
+ */
+#define MAX_DIR_HANDLE 10	
+
+/**
+ * \def MINIMUN_ERASED_BLOCK
+ *  UFFS will not allow appending or creating new files when the free/erased block
+ *  is lower then MINIMUN_ERASED_BLOCK.
+ */
+#define MINIMUN_ERASED_BLOCK 2
+
+/**
+ * \def CONFIG_CHANGE_MODIFY_TIME
+ * \note If defined, closing a file which is opened for writing/appending will
+ *       update the file's modify time as well. Disable this feature will save a
+ *       lot of writing activities if you frequently open files for write and close it.
+ */
+//#define CONFIG_CHANGE_MODIFY_TIME
+
+
+/**
+ * \def CONFIG_ENABLE_BAD_BLOCK_VERIFY
+ * \note allow erase and verify block marked as 'bad' when format UFFS partition.
+ *		it's not recommended for most NAND flash.
+ */
+#define CONFIG_ENABLE_BAD_BLOCK_VERIFY
+
+/**
+ * \def CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+ * \note erase block again before mark bad block
+ */
+//#define CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+
+/**
+ * \def CONFIG_PAGE_WRITE_VERIFY
+ * \note verify page data after write, for extra safe data storage.
+ */
+#define CONFIG_PAGE_WRITE_VERIFY
+
+/**
+ * \def CONFIG_BAD_BLOCK_POLICY_STRICT
+ * \note If this is enabled, UFFS will report the block as 'bad' if any bit-flips found;
+ *       otherwise, UFFS report bad block only when ECC failed or reported
+ *		 by low level flash driver.
+ *
+ * \note Enable this will ensure your data always be stored on completely good blocks.
+ */
+#define CONFIG_BAD_BLOCK_POLICY_STRICT
+
+
+/**
+ * \def CONFIG_ENABLE_PAGE_DATA_CRC
+ * \note If this is enabled, UFFS save page data CRC16 sum in mini header,
+ *       it provides extra protection for data integrity.
+ */
+#define CONFIG_ENABLE_PAGE_DATA_CRC
+
+
+/** micros for calculating buffer sizes */
+
+/**
+ *	\def UFFS_BLOCK_INFO_BUFFER_SIZE
+ *	\brief calculate memory bytes for block info caches
+ */
+#define UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block)	\
+			(											\
+				(										\
+					sizeof(uffs_BlockInfo) +			\
+					sizeof(uffs_PageSpare) * n_pages_per_block \
+				 ) * MAX_CACHED_BLOCK_INFO				\
+			)
+
+/**
+ *	\def UFFS_PAGE_BUFFER_SIZE
+ *	\brief calculate memory bytes for page buffers
+ */
+#define UFFS_PAGE_BUFFER_SIZE(n_page_size)	\
+			(								\
+				(							\
+					sizeof(uffs_Buf) + n_page_size	\
+				) * MAX_PAGE_BUFFERS		\
+			)
+
+/**
+ *	\def UFFS_TREE_BUFFER_SIZE
+ *	\brief calculate memory bytes for tree nodes
+ */
+#define UFFS_TREE_BUFFER_SIZE(n_blocks) (sizeof(TreeNode) * n_blocks)
+
+
+#define UFFS_SPARE_BUFFER_SIZE (MAX_SPARE_BUFFERS * UFFS_MAX_SPARE_SIZE)
+
+
+/**
+ *	\def UFFS_STATIC_BUFF_SIZE
+ *	\brief calculate total memory usage of uffs system
+ */
+#define UFFS_STATIC_BUFF_SIZE(n_pages_per_block, n_page_size, n_blocks) \
+			(		\
+				UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block) + \
+				UFFS_PAGE_BUFFER_SIZE(n_page_size) + \
+				UFFS_TREE_BUFFER_SIZE(n_blocks) + \
+				UFFS_SPARE_BUFFER_SIZE \
+			 )
+
+
+
+/* config check */
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD) < 3
+#error "MAX_PAGE_BUFFERS is too small"
+#endif
+
+#if (MAX_DIRTY_PAGES_IN_A_BLOCK < 2)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should >= 2"
+#endif
+
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1 < MAX_DIRTY_PAGES_IN_A_BLOCK)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should < (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD)"
+#endif
+
+#if defined(CONFIG_PAGE_WRITE_VERIFY) && (CLONE_BUFFERS_THRESHOLD < 2)
+#error "CLONE_BUFFERS_THRESHOLD should >= 2 when CONFIG_PAGE_WRITE_VERIFY is enabled."
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 1
+#error "Please enable ONLY one memory allocator"
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR == 0
+#error "Please enable ONE of memory allocators"
+#endif
+
+#if defined(CONFIG_USE_GLOBAL_FS_LOCK) && defined(CONFIG_USE_PER_DEVICE_LOCK)
+#error "enable either CONFIG_USE_GLOBAL_FS_LOCK or CONFIG_USE_PER_DEVICE_LOCK, not both"
+#endif
+
+#if (MAX_OBJECT_HANDLE > (1 << FD_SIGNATURE_SHIFT))
+#error "Please increase FD_SIGNATURE_SHIFT !"
+#endif
+
+#ifdef WIN32
+# pragma warning(disable : 4996)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4214)
+# pragma warning(disable : 4127)
+# pragma warning(disable : 4389)
+# pragma warning(disable : 4100)
+#endif
+
+#endif

+ 148 - 0
src/platform/posix/uffs_os.c

@@ -0,0 +1,148 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_os_posix.c
+ * \brief Emulation on POSIX host. This is just a dumb implementation, does not really create semaphores.
+ * \author Ricky Zheng
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include <memory.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <pthread.h>
+
+#define PFX "os  : "
+
+int uffs_SemCreate(OSSEM *sem)
+{
+	pthread_mutex_t *mutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t));
+	int ret = -1;
+
+	if (mutex) {
+		ret = pthread_mutex_init(mutex, NULL /* default attr */);
+		if (ret == 0) {
+			*sem = (OSSEM *)mutex;			
+		}
+		else {
+			free(mutex);
+		}
+	}
+	
+	return ret;
+}
+
+int uffs_SemWait(OSSEM sem)
+{
+	return pthread_mutex_lock((pthread_mutex_t *)sem);
+}
+
+int uffs_SemSignal(OSSEM sem)
+{
+	return pthread_mutex_unlock((pthread_mutex_t *)sem);;
+}
+
+int uffs_SemDelete(OSSEM *sem)
+{
+	pthread_mutex_t *mutex = (pthread_mutex_t *) (*sem);
+	int ret = -1;
+	
+	if (mutex) {
+		ret = pthread_mutex_destroy(mutex);
+		if (ret == 0) {
+			free(mutex);
+			*sem = 0;
+		}			
+	}
+	return ret;
+}
+
+int uffs_OSGetTaskId(void)
+{
+	//TODO: ... return current task ID ...
+	return 0;
+}
+
+unsigned int uffs_GetCurDateTime(void)
+{
+	// FIXME: return system time, please modify this for your platform ! 
+	//			or just return 0 if you don't care about file time.
+	time_t tvalue;
+
+	tvalue = time(NULL);
+	
+	return (unsigned int)tvalue;
+}
+
+#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
+static void * sys_malloc(struct uffs_DeviceSt *dev, unsigned int size)
+{
+	dev = dev;
+	uffs_Perror(UFFS_MSG_NORMAL, "system memory alloc %d bytes", size);
+	return malloc(size);
+}
+
+static URET sys_free(struct uffs_DeviceSt *dev, void *p)
+{
+	dev = dev;
+	free(p);
+	return U_SUCC;
+}
+
+void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator)
+{
+	allocator->malloc = sys_malloc;
+	allocator->free = sys_free;
+}
+#endif
+
+
+/* debug message output throught 'printf' */
+static void output_dbg_msg(const char *msg);
+static struct uffs_DebugMsgOutputSt m_dbg_ops = {
+	output_dbg_msg,
+	NULL,
+};
+
+static void output_dbg_msg(const char *msg)
+{
+	printf("%s", msg);
+}
+
+void uffs_SetupDebugOutput(void)
+{
+	uffs_InitDebugMessageOutput(&m_dbg_ops, UFFS_MSG_NOISY);
+}

+ 317 - 0
src/platform/win32/uffs_config.h

@@ -0,0 +1,317 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_config.h
+ * \brief basic configuration of uffs
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_CONFIG_H_
+#define _UFFS_CONFIG_H_
+
+/**
+ * \def UFFS_MAX_PAGE_SIZE
+ * \note maximum page size UFFS support
+ */
+#define UFFS_MAX_PAGE_SIZE		2048
+
+/**
+ * \def UFFS_MAX_SPARE_SIZE
+ */
+#define UFFS_MAX_SPARE_SIZE ((UFFS_MAX_PAGE_SIZE / 256) * 8)
+
+/**
+ * \def UFFS_MAX_ECC_SIZE
+ */
+#define UFFS_MAX_ECC_SIZE  ((UFFS_MAX_PAGE_SIZE / 256) * 5)
+
+/**
+ * \def MAX_CACHED_BLOCK_INFO
+ * \note uffs cache the block info for opened directories and files,
+ *       a practical value is 5 ~ MAX_OBJECT_HANDLE
+ */
+#define MAX_CACHED_BLOCK_INFO	50
+
+/** 
+ * \def MAX_PAGE_BUFFERS
+ * \note the bigger value will bring better read/write performance.
+ *       but few writing performance will be improved when this 
+ *       value is become larger than 'max pages per block'
+ */
+#define MAX_PAGE_BUFFERS		40
+
+
+/** 
+ * \def CLONE_BUFFER_THRESHOLD
+ * \note reserve buffers for clone. 1 or 2 should be enough.
+ */
+#define CLONE_BUFFERS_THRESHOLD	2
+
+/**
+ * \def MAX_SPARE_BUFFERS
+ * \note spare buffers are used for lower level flash operations,
+ *		 5 should be enough.
+ */
+#define MAX_SPARE_BUFFERS		5
+
+
+/**
+ * \def MAX_DIRTY_PAGES_IN_A_BLOCK 
+ * \note this value should be between '2' and the lesser of
+ *		 'max pages per block' and (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1).
+ *
+ *       the smaller the value the frequently the buffer will be flushed.
+ */
+#define MAX_DIRTY_PAGES_IN_A_BLOCK	32
+
+/**
+ * \def CONFIG_ENABLE_UFFS_DEBUG_MSG
+ * \note Enable debug message output. You must call uffs_InitDebugMessageOutput()
+ *      to initialize debug apart from enable debug feature.
+ */
+#define CONFIG_ENABLE_UFFS_DEBUG_MSG
+
+/**
+ * \def CONFIG_USE_GLOBAL_FS_LOCK
+ * \note use global lock instead of per-device lock.
+ *       this is required if you use fd APIs in multi-thread environment.
+ */
+#define CONFIG_USE_GLOBAL_FS_LOCK
+
+
+/**
+ * \def CONFIG_USE_PER_DEVICE_LOCK
+ * \note use per-device lock.
+ *		 this is required if you use fs APIs in multi-thread environment.
+ */
+//#define CONFIG_USE_PER_DEVICE_LOCK
+
+
+
+/**
+ * \def CONFIG_USE_STATIC_MEMORY_ALLOCATOR
+ * \note uffs will use static memory allocator if this is defined.
+ *       to use static memory allocator, you need to provide memory
+ *       buffer when creating uffs_Device.
+ *
+ *       use UFFS_STATIC_BUFF_SIZE() to calculate memory buffer size.
+ */
+#define CONFIG_USE_STATIC_MEMORY_ALLOCATOR 0
+
+/**
+ * \def CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR
+ * \note  using system platform's 'malloc' and 'free'.
+ */
+#define CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR 1
+
+
+
+/** 
+ * \def CONFIG_FLUSH_BUF_AFTER_WRITE
+ * \note UFFS will write all data directly into flash in 
+ *       each 'write' call if you enable this option.
+ *       (which means lesser data lost when power failure but
+ *		 poorer writing performance).
+ *		 It's not recommended to open this define for normal applications.
+ */
+//#define CONFIG_FLUSH_BUF_AFTER_WRITE
+
+
+/**
+ * \def CONFIG_UFFS_AUTO_LAYOUT_MTD_COMP
+ * \note Use Linux MTD compatiable spare placement for UFFS_LAYOUT_AUTO,
+ *       only valid for page data size 512 or 2048.
+ */
+//#define CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME
+
+
+/** 
+ * \def MAX_OBJECT_HANDLE
+ * maximum number of object handle 
+ */
+#define MAX_OBJECT_HANDLE	50
+#define FD_SIGNATURE_SHIFT	6
+
+
+/**
+ * \def MAX_DIR_HANDLE
+ * maximum number of uffs_DIR
+ */
+#define MAX_DIR_HANDLE	10
+
+/**
+ * \def MINIMUN_ERASED_BLOCK
+ *  UFFS will not allow appending or creating new files when the free/erased block
+ *  is lower then MINIMUN_ERASED_BLOCK.
+ */
+#define MINIMUN_ERASED_BLOCK 2
+
+/**
+ * \def CONFIG_CHANGE_MODIFY_TIME
+ * \note If defined, closing a file which is opened for writing/appending will
+ *       update the file's modify time as well. Disable this feature will save a
+ *       lot of writing activities if you frequently open files for write and close it.
+ */
+//#define CONFIG_CHANGE_MODIFY_TIME
+
+
+/**
+ * \def CONFIG_ENABLE_BAD_BLOCK_VERIFY
+ * \note allow erase and verify block marked as 'bad' when format UFFS partition.
+ *		it's not recommended for most NAND flash.
+ */
+#define CONFIG_ENABLE_BAD_BLOCK_VERIFY
+
+/**
+ * \def CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+ * \note erase block again before mark bad block
+ */
+//#define CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+
+/**
+ * \def CONFIG_PAGE_WRITE_VERIFY
+ * \note verify page data after write, for extra safe data storage.
+ */
+#define CONFIG_PAGE_WRITE_VERIFY
+
+/**
+ * \def CONFIG_BAD_BLOCK_POLICY_STRICT
+ * \note If this is enabled, UFFS will report the block as 'bad' if any bit-flips found;
+ *       otherwise, UFFS report bad block only when ECC failed or reported
+ *		 by low level flash driver.
+ *
+ * \note Enable this will ensure your data always be stored on completely good blocks.
+ */
+#define CONFIG_BAD_BLOCK_POLICY_STRICT
+
+
+/**
+ * \def CONFIG_ENABLE_PAGE_DATA_CRC
+ * \note If this is enabled, UFFS save page data CRC16 sum in mini header,
+ *       it provides extra protection for data integrity.
+ */
+//#define CONFIG_ENABLE_PAGE_DATA_CRC
+
+
+/** micros for calculating buffer sizes */
+
+/**
+ *	\def UFFS_BLOCK_INFO_BUFFER_SIZE
+ *	\brief calculate memory bytes for block info caches
+ */
+#define UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block)	\
+			(											\
+				(										\
+					sizeof(uffs_BlockInfo) +			\
+					sizeof(uffs_PageSpare) * n_pages_per_block \
+				 ) * MAX_CACHED_BLOCK_INFO				\
+			)
+
+/**
+ *	\def UFFS_PAGE_BUFFER_SIZE
+ *	\brief calculate memory bytes for page buffers
+ */
+#define UFFS_PAGE_BUFFER_SIZE(n_page_size)	\
+			(								\
+				(							\
+					sizeof(uffs_Buf) + n_page_size	\
+				) * MAX_PAGE_BUFFERS		\
+			)
+
+/**
+ *	\def UFFS_TREE_BUFFER_SIZE
+ *	\brief calculate memory bytes for tree nodes
+ */
+#define UFFS_TREE_BUFFER_SIZE(n_blocks) (sizeof(TreeNode) * n_blocks)
+
+
+#define UFFS_SPARE_BUFFER_SIZE (MAX_SPARE_BUFFERS * UFFS_MAX_SPARE_SIZE)
+
+
+/**
+ *	\def UFFS_STATIC_BUFF_SIZE
+ *	\brief calculate total memory usage of uffs system
+ */
+#define UFFS_STATIC_BUFF_SIZE(n_pages_per_block, n_page_size, n_blocks) \
+			(		\
+				UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block) + \
+				UFFS_PAGE_BUFFER_SIZE(n_page_size) + \
+				UFFS_TREE_BUFFER_SIZE(n_blocks) + \
+				UFFS_SPARE_BUFFER_SIZE \
+			 )
+
+
+
+/* config check */
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD) < 3
+#error "MAX_PAGE_BUFFERS is too small"
+#endif
+
+#if (MAX_DIRTY_PAGES_IN_A_BLOCK < 2)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should >= 2"
+#endif
+
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1 < MAX_DIRTY_PAGES_IN_A_BLOCK)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should < (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD)"
+#endif
+
+#if defined(CONFIG_PAGE_WRITE_VERIFY) && (CLONE_BUFFERS_THRESHOLD < 2)
+#error "CLONE_BUFFERS_THRESHOLD should >= 2 when CONFIG_PAGE_WRITE_VERIFY is enabled."
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 1
+#error "Please enable ONLY one memory allocator"
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR == 0
+#error "Please enable ONE of memory allocators"
+#endif
+
+#if defined(CONFIG_USE_GLOBAL_FS_LOCK) && defined(CONFIG_USE_PER_DEVICE_LOCK)
+#error "enable either CONFIG_USE_GLOBAL_FS_LOCK or CONFIG_USE_PER_DEVICE_LOCK, not both"
+#endif
+
+#if (MAX_OBJECT_HANDLE > (1 << FD_SIGNATURE_SHIFT))
+#error "Please increase FD_SIGNATURE_SHIFT !"
+#endif
+
+#ifdef WIN32
+# pragma warning(disable : 4996)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4214)
+# pragma warning(disable : 4127)
+# pragma warning(disable : 4389)
+# pragma warning(disable : 4100)
+#endif
+
+#endif

+ 148 - 0
src/platform/win32/uffs_os.c

@@ -0,0 +1,148 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_os.c
+ * \brief Emulation on win32 host
+ * \author Ricky Zheng
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include <memory.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <windows.h>
+
+#define PFX "os  : "
+
+int uffs_SemCreate(OSSEM *sem)
+{
+	HANDLE mutex = CreateMutex( 
+        NULL,		// default security attributes
+        FALSE,		// initially not owned
+        NULL);		// unnamed mutex
+
+	if (mutex == NULL) {
+		printf("Create mutex failed !\n");
+		return -1;
+	}
+	else {
+		*sem = (OSSEM)mutex;
+	}
+
+	return 0;
+}
+
+int uffs_SemWait(OSSEM sem)
+{
+	DWORD result;
+
+	result =  WaitForSingleObject( 
+            (HANDLE)sem,    // handle to mutex
+            INFINITE);		// no time-out interval
+
+	return result == WAIT_ABANDONED ? -1 : 0;
+}
+
+int uffs_SemSignal(OSSEM sem)
+{
+	return ReleaseMutex((HANDLE)sem) ? 0 : -1;
+}
+
+int uffs_SemDelete(OSSEM *sem)
+{
+	if (CloseHandle((HANDLE)(*sem))) {
+		*sem = 0;
+		return 0;
+	}
+	else
+		return -1;
+}
+
+int uffs_OSGetTaskId(void)
+{
+	return 0;
+}
+
+unsigned int uffs_GetCurDateTime(void)
+{
+	// FIXME: return system time, please modify this for your platform ! 
+	//			or just return 0 if you don't care about file time.
+	time_t tvalue;
+
+	tvalue = time(NULL);
+	
+	return (unsigned int)tvalue;
+}
+
+#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
+static void * sys_malloc(struct uffs_DeviceSt *dev, unsigned int size)
+{
+	dev = dev;
+	uffs_Perror(UFFS_MSG_NORMAL, "system memory alloc %d bytes", size);
+	return malloc(size);
+}
+
+static URET sys_free(struct uffs_DeviceSt *dev, void *p)
+{
+	dev = dev;
+	free(p);
+	return U_SUCC;
+}
+
+void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator)
+{
+	allocator->malloc = sys_malloc;
+	allocator->free = sys_free;
+}
+#endif
+
+
+/* debug message output throught 'printf' */
+static void output_dbg_msg(const char *msg);
+static struct uffs_DebugMsgOutputSt m_dbg_ops = {
+	output_dbg_msg,
+	NULL,
+};
+
+static void output_dbg_msg(const char *msg)
+{
+	printf("%s", msg);
+}
+
+void uffs_SetupDebugOutput(void)
+{
+	uffs_InitDebugMessageOutput(&m_dbg_ops, UFFS_MSG_NOISY);
+}

+ 59 - 0
src/uffs/CMakeLists.txt

@@ -0,0 +1,59 @@
+SET (libuffs_SRCS 
+		uffs_badblock.c 
+		uffs_blockinfo.c 
+		uffs_buf.c 
+		uffs_debug.c 
+		uffs_device.c 
+		uffs_ecc.c 
+		uffs_fd.c 
+		uffs_fs.c 
+		uffs_init.c 
+		uffs_mem.c 
+		uffs_pool.c 
+		uffs_public.c 
+		uffs_tree.c 
+		uffs_utils.c
+		uffs_mtb.c
+		uffs_find.c
+		uffs_flash.c
+		uffs_version.c
+		uffs_crc.c
+	 )
+
+SET (HDR ${uffs_SOURCE_DIR}/src/inc/uffs)
+
+SET (libuffs_HEADS 
+		${HDR}/uffs.h
+		${HDR}/uffs_badblock.h
+		${HDR}/uffs_blockinfo.h
+		${HDR}/uffs_buf.h 
+		${HDR}/uffs_core.h 
+		${HDR}/uffs_device.h
+		${HDR}/uffs_ecc.h
+		${HDR}/uffs_fd.h
+		${HDR}/uffs_fs.h
+		${HDR}/uffs_mem.h
+        ${HDR}/uffs_os.h
+        ${HDR}/uffs_pool.h
+        ${HDR}/uffs_public.h
+		${HDR}/uffs_tree.h
+		${HDR}/uffs_types.h
+		${HDR}/uffs_utils.h
+		${HDR}/uffs_mtb.h
+		${HDR}/uffs_find.h
+		${HDR}/uffs_flash.h
+		${HDR}/uffs_version.h
+		${HDR}/uffs_crc.h
+   )
+
+IF (UNIX)
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/posix)
+ENDIF()
+
+IF (WIN32)
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/win32)
+ENDIF()
+
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc)
+
+ADD_LIBRARY( uffs STATIC ${libuffs_SRCS} ${libuffs_HEADS} )

+ 249 - 0
src/uffs/uffs_badblock.c

@@ -0,0 +1,249 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_badblock.c
+ * \brief bad block checking and recovering
+ * \author Ricky Zheng, created in 13th Jun, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_ecc.h"
+#include "uffs/uffs_badblock.h"
+#include <string.h>
+
+#define PFX "bbl : "
+
+void uffs_BadBlockInit(uffs_Device *dev)
+{
+	dev->bad.block = UFFS_INVALID_BLOCK;
+}
+
+
+/** 
+ * \brief process bad block: erase bad block, mark it as 'bad'
+ *			and put the node to bad block list.
+ * \param[in] dev uffs device
+ * \param[in] node bad block tree node
+ *			(before the block turn 'bad', it must belong to something ...)
+ */
+void uffs_BadBlockProcess(uffs_Device *dev, TreeNode *node)
+{
+	if (HAVE_BADBLOCK(dev)) {
+		// mark the bad block
+		uffs_FlashMarkBadBlock(dev, dev->bad.block);
+
+		// and put it into bad block list
+		if (node != NULL)
+			uffs_TreeInsertToBadBlockList(dev, node);
+
+		//clear bad block mark.
+		dev->bad.block = UFFS_INVALID_BLOCK;
+
+	}
+}
+
+/** 
+ * \brief process bad block and put the node in 'suspend' list.
+ * \param[in] dev uffs device
+ * \param[in] node bad block tree node
+ */
+void uffs_BadBlockProcessSuspend(uffs_Device *dev, TreeNode *node)
+{
+	if (HAVE_BADBLOCK(dev)) {
+		// mark the bad block
+		uffs_FlashMarkBadBlock(dev, dev->bad.block);
+
+		// and put it into bad block list
+		if (node != NULL)
+			uffs_TreeSuspendAdd(dev, node);
+
+		//clear bad block mark.
+		dev->bad.block = UFFS_INVALID_BLOCK;
+	}
+}
+
+/** 
+ * \brief recover bad block
+ * \param[in] dev uffs device
+ */
+void uffs_BadBlockRecover(uffs_Device *dev)
+{
+	TreeNode *good, *bad;
+	uffs_Buf *buf;
+	u16 i;
+	u16 page;
+	uffs_BlockInfo *bc = NULL;
+	uffs_Tags *tag;
+	uffs_Tags newTag;
+	UBOOL succRecov;
+	UBOOL goodBlockIsDirty = U_FALSE;
+	int ret;
+	int region;
+	u8 type;
+	
+	if (dev->bad.block == UFFS_INVALID_BLOCK)
+		return;
+
+	// pick up an erased good block
+	good = uffs_TreeGetErasedNode(dev);
+	if (good == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "no free block to replace bad block!");
+		return;
+	}
+
+	//recover block
+	bc = uffs_BlockInfoGet(dev, dev->bad.block);
+	
+	if (bc == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "can't get bad block info");
+		return;
+	}
+
+	succRecov = U_TRUE;
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		page = uffs_FindPageInBlockWithPageId(dev, bc, i);
+		if (page == UFFS_INVALID_PAGE) {
+			break;  //end of last valid page, normal break
+		}
+		page = uffs_FindBestPageInBlock(dev, bc, page);
+		if (page == UFFS_INVALID_PAGE) {
+			// got an invalid page ? it's bad block anyway ...
+			uffs_Perror(UFFS_MSG_SERIOUS, "bad block recover (block %d) not finished", bc->block);
+			break;
+		}
+		tag = GET_TAG(bc, page);
+		buf = uffs_BufClone(dev, NULL);
+		if (buf == NULL) {	
+			uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone a new buf!");
+			succRecov = U_FALSE;
+			break;
+		}
+		//NOTE: since this is a bad block, we can't guarantee the data is ECC ok,
+		//		so just load data even ECC is not OK.
+		ret = uffs_LoadPhyDataToBufEccUnCare(dev, buf, bc->block, page);
+		if (ret == U_FAIL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "I/O error ?");
+			uffs_BufFreeClone(dev, buf);
+			succRecov = U_FALSE;
+			break;
+		}
+		buf->data_len = TAG_DATA_LEN(tag);
+		if (buf->data_len > dev->com.pg_data_size) {
+			uffs_Perror(UFFS_MSG_NOISY, "data length over flow!!!");
+			buf->data_len = dev->com.pg_data_size;
+		}
+
+		buf->parent = TAG_PARENT(tag);
+		buf->serial = TAG_SERIAL(tag);
+		buf->type = TAG_TYPE(tag);
+		buf->page_id = TAG_PAGE_ID(tag);
+		
+		// new tag copied from old tag, and increase time-stamp.
+		newTag = *tag;
+		TAG_BLOCK_TS(&newTag) = uffs_GetNextBlockTimeStamp(TAG_BLOCK_TS(tag));
+
+		ret = uffs_FlashWritePageCombine(dev, good->u.list.block, i, buf, &newTag);
+
+		goodBlockIsDirty = U_TRUE;
+		uffs_BufFreeClone(dev, buf);
+
+		if (ret == UFFS_FLASH_IO_ERR) {
+			uffs_Perror(UFFS_MSG_NORMAL, "I/O error ?");
+			succRecov = U_FALSE;
+			break;
+		}
+	}
+
+
+	if (succRecov == U_TRUE) {
+		// successful recover bad block, so need to mark bad block,
+		// and replace with good one
+
+		region = SEARCH_REGION_DIR|SEARCH_REGION_FILE|SEARCH_REGION_DATA;
+		bad = uffs_TreeFindNodeByBlock(dev, dev->bad.block, &region);
+		if (bad != NULL) {
+			switch (region) {
+			case SEARCH_REGION_DIR:
+				bad->u.dir.block = good->u.list.block;
+				type = UFFS_TYPE_DIR;
+				break;
+			case SEARCH_REGION_FILE:
+				bad->u.file.block = good->u.list.block;
+				type = UFFS_TYPE_FILE;
+				break;
+			case SEARCH_REGION_DATA:
+				bad->u.data.block = good->u.list.block;
+				type = UFFS_TYPE_DATA;
+			}
+			
+			//from now, the 'bad' is actually good block :)))
+			uffs_Perror(UFFS_MSG_NOISY,
+						"new bad block %d found, and replaced by %d, type %d!",
+						dev->bad.block, good->u.list.block, type);
+			uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
+			//we reuse the 'good' node as bad block node, and process the bad block.
+			good->u.list.block = dev->bad.block;
+			uffs_BadBlockProcess(dev, good);
+		}
+		else {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+						"can't find the reported bad block(%d) in the tree???",
+						dev->bad.block);
+			if (goodBlockIsDirty == U_TRUE)
+				dev->ops->EraseBlock(dev, good->u.list.block);
+			uffs_TreeInsertToErasedListTail(dev, good);
+		}
+	}
+	else {
+		if (goodBlockIsDirty == U_TRUE)
+			dev->ops->EraseBlock(dev, good->u.list.block);
+		uffs_TreeInsertToErasedListTail(dev, good); //put back to erased list
+	}
+
+	uffs_BlockInfoPut(dev, bc);
+
+}
+
+
+/** put a new block to the bad block waiting list */
+void uffs_BadBlockAdd(uffs_Device *dev, int block)
+{
+	if (dev->bad.block == block)
+		return;
+
+	if (dev->bad.block != UFFS_INVALID_BLOCK)
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't add more then one bad block !");
+	else
+		dev->bad.block = block;
+}
+

+ 413 - 0
src/uffs/uffs_blockinfo.c

@@ -0,0 +1,413 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_blockinfo.c
+ * \brief block information cache system manipulations
+ * \author Ricky Zheng, created 10th May, 2005
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_blockinfo.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_os.h"
+
+#include <string.h>
+
+#define PFX "bc  : "
+
+#define UFFS_CLONE_BLOCK_INFO_NEXT ((uffs_BlockInfo *)(-2))
+
+/**
+ * \brief before block info cache is enable,
+ *			this function should be called to initialize it
+ *
+ * \param[in] dev uffs device
+ * \param[in] maxCachedBlocks maximum cache buffers to be allocated
+ * \return result of initialization
+ *		\retval U_SUCC successful
+ *		\retval U_FAIL failed
+ */
+URET uffs_BlockInfoInitCache(uffs_Device *dev, int maxCachedBlocks)
+{
+	uffs_BlockInfo * blockInfos = NULL;
+	uffs_PageSpare * pageSpares = NULL;
+	void * buf = NULL;
+	uffs_BlockInfo *work = NULL;
+	int size, i, j;
+
+	if (dev->bc.head != NULL) {
+		uffs_Perror(UFFS_MSG_NOISY,
+					"block info cache has been inited already, "
+					"now release it first.");
+		uffs_BlockInfoReleaseCache(dev);
+	}
+
+	size = ( 
+			sizeof(uffs_BlockInfo) +
+			sizeof(uffs_PageSpare) * dev->attr->pages_per_block
+			) * maxCachedBlocks;
+
+	if (dev->mem.blockinfo_pool_size == 0) {
+		if (dev->mem.malloc) {
+			dev->mem.blockinfo_pool_buf = dev->mem.malloc(dev, size);
+			if (dev->mem.blockinfo_pool_buf)
+				dev->mem.blockinfo_pool_size = size;
+		}
+	}
+	if (size > dev->mem.blockinfo_pool_size) {
+		uffs_Perror(UFFS_MSG_DEAD,
+					"Block cache buffer require %d but only %d available.",
+					size, dev->mem.blockinfo_pool_size);
+		return U_FAIL;
+	}
+
+	uffs_Perror(UFFS_MSG_NOISY, "alloc info cache %d bytes.", size);
+
+	buf = dev->mem.blockinfo_pool_buf;
+
+	memset(buf, 0, size);
+
+	dev->bc.mem_pool = buf;
+
+	size = 0;
+	blockInfos = (uffs_BlockInfo *)buf;
+	size += sizeof(uffs_BlockInfo) * maxCachedBlocks;
+
+	pageSpares = (uffs_PageSpare *)((char *)buf + size);
+
+	//initialize block info
+	work = &(blockInfos[0]);
+	dev->bc.head = work;
+	work->ref_count = 0;
+	work->prev = NULL;
+	work->next = &(blockInfos[1]);
+	work->block = UFFS_INVALID_BLOCK;
+
+	for (i = 0; i < maxCachedBlocks - 2; i++) {
+		work = &(blockInfos[i+1]);
+		work->prev = &(blockInfos[i]);
+		work->next = &(blockInfos[i+2]);
+		work->ref_count = 0;
+		work->block = UFFS_INVALID_BLOCK;
+	}
+	//the last node
+	work = &(blockInfos[i+1]);
+	work->prev = &(blockInfos[i]);
+	work->next = NULL;
+	work->block = UFFS_INVALID_BLOCK;
+	work->ref_count = 0;
+	dev->bc.tail = work;
+
+	//initialize spares
+	work = dev->bc.head;
+	for (i = 0; i < maxCachedBlocks; i++) {
+		work->spares = &(pageSpares[i*dev->attr->pages_per_block]);
+		for (j = 0; j < dev->attr->pages_per_block; j++) {
+			work->spares[j].expired = 1;
+		}
+		work->expired_count = dev->attr->pages_per_block;
+		work = work->next;
+	}
+	return U_SUCC;
+}
+
+/**
+ * \brief release all allocated memory of block info cache, 
+ *			this function should be called when unmount file system
+ * \param[in] dev uffs device
+ */
+URET uffs_BlockInfoReleaseCache(uffs_Device *dev)
+{
+	uffs_BlockInfo *work;
+
+	if (dev->bc.head) {
+		for (work = dev->bc.head; work != NULL; work = work->next) {
+			if (work->ref_count != 0) {
+				uffs_Perror(UFFS_MSG_SERIOUS,
+					"There have refed block info cache, release cache fail.");
+				return U_FAIL;
+			}
+		}
+		if (dev->mem.free) {
+			dev->mem.free(dev, dev->bc.mem_pool);
+			dev->mem.blockinfo_pool_size = 0;
+		}
+	}
+
+	dev->bc.head = dev->bc.tail = NULL;
+	dev->bc.mem_pool = NULL;
+
+	return U_SUCC;
+}
+
+static void _BreakBcFromList(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	if (bc->prev)
+		bc->prev->next = bc->next;
+
+	if (bc->next)
+		bc->next->prev = bc->prev;
+
+	if (dev->bc.head == bc)
+		dev->bc.head = bc->next;
+
+	if (dev->bc.tail == bc)
+		dev->bc.tail = bc->prev;
+}
+
+static void _InsertToBcListTail(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	bc->next = NULL;
+	bc->prev = dev->bc.tail;
+	bc->prev->next = bc;
+	dev->bc.tail = bc;
+}
+
+static void _MoveBcToTail(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	_BreakBcFromList(dev, bc);
+	_InsertToBcListTail(dev, bc);
+}
+
+
+/** 
+ * \brief load page spare data to given block info structure
+ *			with given page number
+ * \param[in] dev uffs device
+ * \param[in] work given block info to be filled with
+ * \param[in] page given page number to be read from,
+ *			  if #UFFS_ALL_PAGES is presented, it will read
+ *			  all pages, otherwise it will read only one given page.
+ * \return load result
+ * \retval U_SUCC successful
+ * \retval U_FAIL fail to load
+ * \note work->block must be set before load block info
+ */
+URET uffs_BlockInfoLoad(uffs_Device *dev, uffs_BlockInfo *work, int page)
+{
+	int i, ret;
+	uffs_PageSpare *spare;
+
+	if (page == UFFS_ALL_PAGES) {
+		for (i = 0; i < dev->attr->pages_per_block; i++) {
+			spare = &(work->spares[i]);
+			if (spare->expired == 0)
+				continue;
+			
+			ret = uffs_FlashReadPageTag(dev, work->block, i,
+											&(spare->tag));
+			if (UFFS_FLASH_HAVE_ERR(ret)) {
+				uffs_Perror(UFFS_MSG_SERIOUS,
+							"load block %d page %d spare fail.",
+							work->block, i);
+				return U_FAIL;
+			}
+			spare->expired = 0;
+			work->expired_count--;
+		}
+	}
+	else {
+		if (page < 0 || page >= dev->attr->pages_per_block) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "page out of range !");
+			return U_FAIL;
+		}
+		spare = &(work->spares[page]);
+		if (spare->expired != 0) {
+			ret = uffs_FlashReadPageTag(dev, work->block, page,
+											&(spare->tag));
+			if (UFFS_FLASH_HAVE_ERR(ret)) {
+				uffs_Perror(UFFS_MSG_SERIOUS,
+							"load block %d page %d spare fail.",
+							work->block, page);
+				return U_FAIL;
+			}
+			spare->expired = 0;
+			work->expired_count--;
+		}
+	}
+	return U_SUCC;
+}
+
+
+/** 
+ * \brief find a block cache with given block number
+ * \param[in] dev uffs device
+ * \param[in] block block number
+ * \return found block cache
+ * \retval NULL cache not found
+ * \retval non-NULL found cache pointer
+ */
+uffs_BlockInfo * uffs_BlockInfoFindInCache(uffs_Device *dev, int block)
+{
+	uffs_BlockInfo *work;
+	
+	//search cached block
+	for (work = dev->bc.head; work != NULL; work = work->next) {
+		if (work->block == block) {
+			work->ref_count++;
+			return work;
+		}
+	}
+	return NULL;
+}
+
+
+/** 
+ * \brief Find a cached block in cache pool,
+ *			if the cached block exist then return the pointer,
+ *			if the block does not cached already, find a non-used cache.
+ *			if all of cached are used out, return NULL.
+ * \param[in] dev uffs device
+ * \param[in] block block number to be found
+ * \return found block cache buffer
+ * \retval NULL caches used out
+ * \retval non-NULL buffer pointer of given block
+ */
+uffs_BlockInfo * uffs_BlockInfoGet(uffs_Device *dev, int block)
+{
+	uffs_BlockInfo *work;
+	int i;
+
+	//search cached block
+	if ((work = uffs_BlockInfoFindInCache(dev, block)) != NULL) {
+		_MoveBcToTail(dev, work);
+		return work;
+	}
+
+	//can't find block from cache, need to find a free(unlocked) cache
+	for (work = dev->bc.head; work != NULL; work = work->next) {
+		if(work->ref_count == 0) break;
+	}
+	if (work == NULL) {
+		//caches used out !
+		uffs_Perror(UFFS_MSG_SERIOUS,  "insufficient block info cache");
+		return NULL;
+	}
+
+	work->block = block;
+	work->expired_count = dev->attr->pages_per_block;
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		work->spares[i].expired = 1;
+
+		// TODO: init tag
+	}
+
+	work->ref_count = 1;
+
+	_MoveBcToTail(dev, work);
+
+	return work;
+}
+
+/** 
+ * \brief put block info buffer back to pool,
+ *			 should be called with #uffs_BlockInfoGet in pairs.
+ * \param[in] dev uffs device
+ * \param[in] p pointer of block info buffer
+ */
+void uffs_BlockInfoPut(uffs_Device *dev, uffs_BlockInfo *p)
+{
+	dev = dev;
+	if (p)
+	{
+		if (p->ref_count == 0) {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+				"Put an unused block info cache back ?");
+		}
+		else {
+			p->ref_count--;
+		}
+	}
+}
+
+
+/** 
+ * \brief make the given pages expired in given block info buffer
+ * \param[in] dev uffs device
+ * \param[in] p pointer of block info buffer
+ * \param[in] page given page number.
+ *	if #UFFS_ALL_PAGES presented, all pages in the block should be made expired.
+ */
+void uffs_BlockInfoExpire(uffs_Device *dev, uffs_BlockInfo *p, int page)
+{
+	int i;
+	uffs_PageSpare *spare;
+
+	if (page == UFFS_ALL_PAGES) {
+		for (i = 0; i < dev->attr->pages_per_block; i++) {
+			spare = &(p->spares[i]);
+			if (spare->expired == 0) {
+				spare->expired = 1;
+				p->expired_count++;
+			}
+		}
+	}
+	else {
+		if (page >= 0 && page < dev->attr->pages_per_block) {
+			spare = &(p->spares[page]);
+			if (spare->expired == 0) {
+				spare->expired = 1;
+				p->expired_count++;
+			}
+		}
+	}
+}
+
+/** 
+ * Is all blcok info cache free (not referenced) ?
+ */
+UBOOL uffs_BlockInfoIsAllFree(uffs_Device *dev)
+{
+	uffs_BlockInfo *work;
+
+	work = dev->bc.head;
+	while (work) {
+		if (work->ref_count != 0)
+			return U_FALSE;
+		work = work->next;
+	}
+
+	return U_TRUE;
+}
+
+void uffs_BlockInfoExpireAll(uffs_Device *dev)
+{
+	uffs_BlockInfo *bc;
+
+	bc = dev->bc.head;
+	while (bc) {
+		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
+		bc = bc->next;
+	}
+	return;
+}

+ 1799 - 0
src/uffs/uffs_buf.c

@@ -0,0 +1,1799 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/** 
+ * \file uffs_buf.c
+ * \brief uffs page buffers manipulations
+ * \author Ricky Zheng
+ * \note Created in 11th May, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_buf.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_pool.h"
+#include "uffs/uffs_ecc.h"
+#include "uffs/uffs_badblock.h"
+#include <string.h>
+
+#define PFX "pbuf: "
+
+
+URET _BufFlush(struct uffs_DeviceSt *dev, UBOOL force_block_recover, int slot);
+
+
+/**
+ * \brief inspect (print) uffs page buffers.
+ * \param[in] dev uffs device to be inspected.
+ */
+void uffs_BufInspect(uffs_Device *dev)
+{
+	struct uffs_PageBufDescSt *pb = &dev->buf;
+	uffs_Buf *buf;
+
+	uffs_PerrorRaw(UFFS_MSG_NORMAL,
+					"------------- page buffer inspect ---------" TENDSTR);
+	uffs_PerrorRaw(UFFS_MSG_NORMAL, "all buffers: " TENDSTR);
+	for (buf = pb->head; buf; buf = buf->next) {
+		if (buf->mark != 0) {
+			uffs_PerrorRaw(UFFS_MSG_NORMAL,
+				"\tF:%04x S:%04x P:%02d R:%02d D:%03d M:%c EM:%d" TENDSTR,
+				buf->parent, buf->serial,
+				buf->page_id, buf->ref_count,
+				buf->data_len, buf->mark == UFFS_BUF_VALID ? 'V' : 'D',
+				buf->ext_mark);
+		}
+	}
+	uffs_PerrorRaw(UFFS_MSG_NORMAL,
+					"--------------------------------------------"  TENDSTR);
+}
+
+/**
+ * \brief initialize page buffers for device
+ * in UFFS, each device has one buffer pool
+ * \param[in] dev uffs device
+ * \param[in] buf_max maximum buffer number, normally use #MAX_PAGE_BUFFERS
+ * \param[in] dirty_buf_max maximum dirty buffer allowed,
+ *				if the dirty buffer over this number,
+ *				than need to be flush to flash
+ */
+URET uffs_BufInit(uffs_Device *dev, int buf_max, int dirty_buf_max)
+{
+	void *pool;
+	u8 *data;
+	uffs_Buf *buf;
+	int size;
+	int i, slot;
+
+	if (!dev)
+		return U_FAIL;
+
+	//init device common parameters, which are needed by page buffers
+	dev->com.pg_size = dev->attr->page_data_size;  // we use the whole page.
+	dev->com.header_size = sizeof(struct uffs_MiniHeaderSt); // mini header
+	dev->com.pg_data_size = dev->com.pg_size - dev->com.header_size;
+
+	if (dev->buf.pool != NULL) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"buf.pool is not NULL, buf already inited ?");
+		return U_FAIL;
+	}
+	
+	size = (sizeof(uffs_Buf) + dev->com.pg_size) * buf_max;
+	if (dev->mem.pagebuf_pool_size == 0) {
+		if (dev->mem.malloc) {
+			dev->mem.pagebuf_pool_buf = dev->mem.malloc(dev, size);
+			if (dev->mem.pagebuf_pool_buf)
+				dev->mem.pagebuf_pool_size = size;
+		}
+	}
+	if (size > dev->mem.pagebuf_pool_size) {
+		uffs_Perror(UFFS_MSG_DEAD,
+					"page buffers require %d but only %d available.",
+				size, dev->mem.pagebuf_pool_size);
+		return U_FAIL;
+	}
+	pool = dev->mem.pagebuf_pool_buf;
+
+	uffs_Perror(UFFS_MSG_NOISY, "alloc %d bytes.", size);
+	dev->buf.pool = pool;
+
+	for (i = 0; i < buf_max; i++) {
+		buf = (uffs_Buf *)((u8 *)pool + (sizeof(uffs_Buf) * i));
+		memset(buf, 0, sizeof(uffs_Buf));
+		data = (u8 *)pool + (sizeof(uffs_Buf) * buf_max) + (dev->com.pg_size * i);
+		buf->header = data;
+		buf->data = data + dev->com.header_size;
+		buf->mark = UFFS_BUF_EMPTY;
+		memset(buf->header, 0, dev->com.pg_size);
+		if (i == 0) {
+			buf->prev = NULL;
+			dev->buf.head = buf;
+		}
+		else {
+			buf->prev = (uffs_Buf *)((u8 *)buf - sizeof(uffs_Buf));
+		}
+
+		if (i == (buf_max - 1)) {
+			buf->next = NULL;
+			dev->buf.tail = buf;
+		}
+		else {
+			buf->next = (uffs_Buf *)((u8 *)buf + sizeof(uffs_Buf));
+		}
+	}
+
+	dev->buf.buf_max = buf_max;
+	dev->buf.dirty_buf_max = (dirty_buf_max > dev->attr->pages_per_block ?
+								dev->attr->pages_per_block : dirty_buf_max);
+
+	for (slot = 0; slot < dev->cfg.dirty_groups; slot++) {
+		dev->buf.dirtyGroup[slot].dirty = NULL;
+		dev->buf.dirtyGroup[slot].count = 0;
+	}
+	return U_SUCC;
+}
+
+/**
+ * \brief flush all buffers
+ */
+URET uffs_BufFlushAll(struct uffs_DeviceSt *dev)
+{
+	int slot;
+	for (slot = 0; slot < dev->cfg.dirty_groups; slot++) {
+		if(_BufFlush(dev, FALSE, slot) != U_SUCC) {
+			uffs_Perror(UFFS_MSG_NORMAL,
+						"fail to flush buffer(slot %d)", slot);
+			return U_FAIL;
+		}
+	}
+	return U_SUCC;
+}
+
+/** 
+ * \brief release all page buffer, this function should be called 
+			when unmounting a uffs device
+ * \param[in] dev uffs device
+ * \note if there are page buffers in used, it may cause fail to release
+ */
+URET uffs_BufReleaseAll(uffs_Device *dev)
+{
+	uffs_Buf *p;
+
+	if (!dev)
+		return U_FAIL;
+
+	//now release all buffer
+	p = dev->buf.head;
+	while (p) {
+		if (p->ref_count != 0) {
+			uffs_Perror(UFFS_MSG_NORMAL, PFX
+						"can't release buffers, parent:%d, serial:%d, \
+						page_id:%d still in used.\n",
+						p->parent, p->serial, p->page_id);
+			return U_FAIL;
+		}
+		p = p->next;
+	}
+
+	if (uffs_BufFlushAll(dev) != U_SUCC) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"can't release buf, fail to flush buffer");
+		return U_FAIL;
+	}
+
+	if (dev->mem.free) {
+		dev->mem.free(dev, dev->buf.pool);
+		dev->mem.pagebuf_pool_size = 0;
+	}
+
+	dev->buf.pool = NULL;
+	dev->buf.head = dev->buf.tail = NULL;
+
+	return U_SUCC;
+}
+
+
+static void _BreakFromBufList(uffs_Device *dev, uffs_Buf *buf)
+{
+	if(buf->next)
+		buf->next->prev = buf->prev;
+
+	if(buf->prev)
+		buf->prev->next = buf->next;
+
+	if(dev->buf.head == buf)
+		dev->buf.head = buf->next;
+
+	if(dev->buf.tail == buf)
+		dev->buf.tail = buf->prev;
+
+}
+
+static void _LinkToBufListHead(uffs_Device *dev, uffs_Buf *buf)
+{
+	if (buf == dev->buf.head)
+		return;
+
+	buf->prev = NULL;
+	buf->next = dev->buf.head;
+
+	if (dev->buf.head)
+		dev->buf.head->prev = buf;
+
+	if (dev->buf.tail == NULL)
+		dev->buf.tail = buf;
+
+	dev->buf.head = buf;
+}
+
+static void _LinkToBufListTail(uffs_Device *dev, uffs_Buf *buf)
+{
+	if (dev->buf.tail == buf)
+		return;
+
+	buf->prev = dev->buf.tail;
+	buf->next = NULL;
+
+	if (dev->buf.tail)
+		dev->buf.tail->next = buf;
+
+	if (dev->buf.head == NULL)
+		dev->buf.head = buf;
+
+	dev->buf.tail = buf;
+}
+
+//move a node which linked in the list to the head of list
+static void _MoveNodeToHead(uffs_Device *dev, uffs_Buf *p)
+{
+	if (p == dev->buf.head)
+		return;
+
+	//break from list
+	_BreakFromBufList(dev, p);
+
+	//link to head
+	_LinkToBufListHead(dev, p);
+}
+
+// check if the buf is already in dirty list
+static UBOOL _IsBufInInDirtyList(uffs_Device *dev, int slot, uffs_Buf *buf)
+{
+	uffs_Buf *work;
+	work = dev->buf.dirtyGroup[slot].dirty;
+	while (work) {
+		if (work == buf) 
+			return U_TRUE;
+		work = work->next_dirty;
+	}
+
+	return U_FALSE;
+}
+
+static void _LinkToDirtyList(uffs_Device *dev, int slot, uffs_Buf *buf)
+{
+
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"Try to insert a NULL node into dirty list ?");
+		return;
+	}
+
+	buf->mark = UFFS_BUF_DIRTY;
+	buf->prev_dirty = NULL;
+	buf->next_dirty = dev->buf.dirtyGroup[slot].dirty;
+
+	if (dev->buf.dirtyGroup[slot].dirty) 
+		dev->buf.dirtyGroup[slot].dirty->prev_dirty = buf;
+
+	dev->buf.dirtyGroup[slot].dirty = buf;
+	dev->buf.dirtyGroup[slot].count++;
+}
+
+static int CountFreeBuf(uffs_Device *dev)
+{
+	int count = 0;
+
+	uffs_Buf *buf = dev->buf.head;
+
+	while (buf) {
+
+		if (buf->ref_count == 0 && 
+			buf->mark != UFFS_BUF_DIRTY)
+			count++;
+
+		buf = buf->next;
+	}
+
+	return count;
+}
+
+static uffs_Buf * _FindFreeBufEx(uffs_Device *dev, int clone)
+{
+	uffs_Buf *buf;
+
+	if (!clone && CountFreeBuf(dev) <= CLONE_BUFFERS_THRESHOLD)
+		return NULL;
+
+#if 0
+	buf = dev->buf.head;
+	while (buf) {
+
+		if (buf->ref_count == 0 && 
+			buf->mark != UFFS_BUF_DIRTY)
+			return buf;
+
+		buf = buf->next;
+	}
+#else
+	buf = dev->buf.tail;
+	while (buf) {
+
+		if(buf->ref_count == 0 &&
+			buf->mark != UFFS_BUF_DIRTY) 
+			return buf;
+
+		buf = buf->prev;
+	}
+#endif
+
+	return buf;
+}
+
+static uffs_Buf * _FindFreeBuf(uffs_Device *dev)
+{
+	return _FindFreeBufEx(dev, 0);
+}
+
+
+/** 
+ * load psychical page data into buf and do ecc check 
+ * \param[in] dev uffs device
+ * \param[in] buf buf to be load in
+ * \param[in] block psychical block number
+ * \param[in] page psychical page number
+ * \return return U_SUCC if no error,
+ *	 return U_FAIL if I/O error or ecc check fail
+ */
+URET uffs_BufLoadPhyData(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page)
+{
+	int ret;
+
+	ret = uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
+
+	if (UFFS_FLASH_HAVE_ERR(ret)) {
+		buf->mark = UFFS_BUF_EMPTY;
+		return U_FAIL;
+	}
+	else {
+		buf->mark = UFFS_BUF_VALID;
+		return U_SUCC;
+	}
+}
+
+/** 
+ * \brief load psychical page data into buf and ignore ECC result
+ *
+ * \param[in] dev uffs device
+ * \param[in] buf buf to be load in
+ * \param[in] block psychical block number
+ * \param[in] page psychical page number
+ *
+ * \return return U_SUCC if no error, return U_FAIL if I/O error
+ * \note this function should be only used when doing bad block recover.
+ */
+URET uffs_LoadPhyDataToBufEccUnCare(uffs_Device *dev,
+									uffs_Buf *buf, u32 block, u32 page)
+{
+	int ret;
+
+	ret = uffs_FlashReadPage(dev, block, page, buf, U_TRUE);
+
+	if (ret == UFFS_FLASH_IO_ERR) {
+		buf->mark = UFFS_BUF_EMPTY;
+		return U_FAIL;
+	}
+	else {
+		buf->mark = UFFS_BUF_VALID;
+		return U_SUCC;
+	}
+}
+
+/** 
+ * find a buffer in the pool
+ * \param[in] dev uffs device
+ * \param[in] start buf to search from
+ * \param[in] parent parent serial num
+ * \param[in] serial serial num
+ * \param[in] page_id page_id (if page_id == UFFS_ALL_PAGES then any page would match)
+ * \return return found buffer, return NULL if buffer not found
+ */
+uffs_Buf * uffs_BufFindFrom(uffs_Device *dev, uffs_Buf *start,
+						u16 parent, u16 serial, u16 page_id)
+{
+	uffs_Buf *p = start;
+
+	while (p) {
+		if(	p->parent == parent &&
+			p->serial == serial &&
+			(page_id == UFFS_ALL_PAGES || p->page_id == page_id) &&
+			p->mark != UFFS_BUF_EMPTY) 
+		{
+			//they have match one
+			return p;
+		}
+		p = p->next;
+	}
+
+	return NULL; //buffer not found
+}
+
+/** 
+ * find a buffer in the pool
+ * \param[in] dev uffs device
+ * \param[in] parent parent serial num
+ * \param[in] serial serial num
+ * \param[in] page_id page_id (if page_id == UFFS_ALL_PAGES then any page would match)
+ * \return return found buffer, return NULL if buffer not found
+ */
+uffs_Buf * uffs_BufFind(uffs_Device *dev,
+						u16 parent, u16 serial, u16 page_id)
+{
+	uffs_Buf *p = dev->buf.head;
+
+	return uffs_BufFindFrom(dev, p, parent, serial, page_id);
+}
+
+
+static uffs_Buf * _FindBufInDirtyList(uffs_Buf *dirty, u16 page_id)
+{
+	while(dirty) {
+		if (dirty->page_id == page_id) 
+			return dirty;
+		dirty = dirty->next_dirty;
+	}
+	return NULL;
+}
+
+static URET _BreakFromDirty(uffs_Device *dev, uffs_Buf *dirtyBuf)
+{
+	int slot = -1;
+
+	if (dirtyBuf->mark != UFFS_BUF_DIRTY) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+						"try to break a non-dirty buf from dirty list ?");
+		return U_FAIL;
+	}
+
+	slot = uffs_BufFindGroupSlot(dev, dirtyBuf->parent, dirtyBuf->serial);
+	if (slot < 0) {
+		uffs_Perror(UFFS_MSG_NORMAL, "no dirty list exit ?");
+		return U_FAIL;
+	}
+
+	// break from the link
+	if (dirtyBuf->next_dirty) {
+		dirtyBuf->next_dirty->prev_dirty = dirtyBuf->prev_dirty;
+	}
+
+	if (dirtyBuf->prev_dirty) {
+		dirtyBuf->prev_dirty->next_dirty = dirtyBuf->next_dirty;
+	}
+
+	// check if it's the link head ...
+	if (dev->buf.dirtyGroup[slot].dirty == dirtyBuf) {
+		dev->buf.dirtyGroup[slot].dirty = dirtyBuf->next_dirty;
+	}
+
+	dirtyBuf->next_dirty = dirtyBuf->prev_dirty = NULL; // clear dirty link
+
+	dev->buf.dirtyGroup[slot].count--;
+
+	return U_SUCC;
+}
+
+static u16 _GetDirOrFileNameSum(uffs_Device *dev, uffs_Buf *buf)
+{
+	u16 data_sum = 0; //default: 0
+	uffs_FileInfo *fi;
+	
+	dev = dev;
+	//FIXME: We use the same schema for both dir and file.
+	if (buf->type == UFFS_TYPE_FILE || buf->type == UFFS_TYPE_DIR) {
+		if (buf->page_id == 0) {
+			fi = (uffs_FileInfo *)(buf->data);
+			data_sum = uffs_MakeSum16(fi->name, fi->name_len);
+		}
+	}
+
+	return data_sum;
+}
+
+
+static URET _CheckDirtyList(uffs_Buf *dirty)
+{
+	u16 parent;
+	u16 serial;
+
+	if (dirty == NULL) {
+		return U_SUCC;
+	}
+
+	parent = dirty->parent;
+	serial = dirty->serial;
+	dirty = dirty->next_dirty;
+
+	while (dirty) {
+		if (parent != dirty->parent ||
+			serial != dirty->serial) {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+					"parent or serial in dirty pages buffer are not the same ?");
+			return U_FAIL;
+		}
+		if (dirty->mark != UFFS_BUF_DIRTY) {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+					"non-dirty page buffer in dirty buffer list ?");
+			return U_FAIL;
+		}
+		dirty = dirty->next_dirty;
+	}
+	return U_SUCC;
+}
+
+/** find a page in dirty list, which has minimum page_id */
+uffs_Buf * _FindMinimunPageIdFromDirtyList(uffs_Buf *dirtyList)
+{
+	uffs_Buf * work = dirtyList;
+	uffs_Buf * buf = dirtyList;
+
+	if (buf) {
+		work = work->next_dirty;
+		while (work) {
+			if (work->page_id < buf->page_id)
+				buf = work;
+			work = work->next_dirty;
+		}
+
+		uffs_Assert(buf->mark == UFFS_BUF_DIRTY, 
+					"buf (serial = %d, parent = %d, page_id = %d, type = %d) in dirty list but mark is 0x%x ?",
+					buf->serial, buf->parent, buf->page_id, buf->type, buf->mark);
+	}
+
+	return buf;
+}
+
+
+/** 
+ * \brief flush buffer with block recover
+ *
+ * Scenario: 
+ *	1. get a free (erased) block --> newNode <br>
+ *	2. copy from old block ---> oldNode, or copy from dirty list, <br>
+ *		sorted by page_id, to new block. Skips the invalid pages when copy pages.<br>
+ *	3. erased old block. set new info to oldNode, set newNode->block = old block,<br>
+ *		and put newNode to erased list.<br>
+ *	\note IT'S IMPORTANT TO KEEP OLD NODE IN THE LIST,
+ *		 so you don't need to update the obj->node :-)
+ */
+static URET uffs_BufFlush_Exist_With_BlockCover(
+			uffs_Device *dev,
+			int slot,			//!< dirty group slot
+			TreeNode *node,		//!< old data node on tree
+			uffs_BlockInfo *bc	//!< old data block info
+			)
+{
+	u16 i;
+	u8 type, timeStamp;
+	u16 page, parent, serial;
+	uffs_Buf *buf;
+	TreeNode *newNode;
+	uffs_BlockInfo *newBc;
+	uffs_Tags *tag, *oldTag;
+	int x;
+	u16 newBlock;
+	UBOOL succRecover;			//U_TRUE: recover successful, erase old block,
+								//U_FALSE: fail to recover, erase new block
+	UBOOL flash_op_err;
+	u16 data_sum = 0xFFFF;
+
+	UBOOL useCloneBuf;
+
+	type = dev->buf.dirtyGroup[slot].dirty->type;
+	parent = dev->buf.dirtyGroup[slot].dirty->parent;
+	serial = dev->buf.dirtyGroup[slot].dirty->serial;
+
+retry:
+	uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
+
+	flash_op_err = UFFS_FLASH_NO_ERR;
+	succRecover = U_FALSE;
+
+	newNode = uffs_TreeGetErasedNode(dev);
+	if (newNode == NULL) {
+		uffs_Perror(UFFS_MSG_NOISY, "no enough erased block!");
+		goto ext;
+	}
+	newBlock = newNode->u.list.block;
+	newBc = uffs_BlockInfoGet(dev, newBlock);
+	if (newBc == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail!");
+		uffs_InsertToErasedListHead(dev, newNode);  //put node back to erased list
+											//because it doesn't use, so put to head
+		goto ext;
+	}
+
+	//uffs_Perror(UFFS_MSG_NOISY, "flush buffer with block cover to %d", newBlock);
+
+#if 0
+	// this assert seems not necessary ...
+	if (!uffs_Assert(newBc->expired_count == dev->attr->pages_per_block,
+			"We have block cache for erased block ? expired_count = %d, block = %d\n",
+			newBc->expired_count, newBc->block)) {
+		uffs_BlockInfoExpire(dev, newBc, UFFS_ALL_PAGES);
+	}
+#endif
+
+	uffs_BlockInfoLoad(dev, newBc, UFFS_ALL_PAGES);
+	timeStamp = uffs_GetNextBlockTimeStamp(uffs_GetBlockTimeStamp(dev, bc));
+
+//	uffs_Perror(UFFS_MSG_NOISY, "Flush buffers with Block Recover, from %d to %d", 
+//					bc->block, newBc->block);
+
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		tag = GET_TAG(newBc, i);
+		TAG_DIRTY_BIT(tag) = TAG_DIRTY;
+		TAG_VALID_BIT(tag) = TAG_VALID;
+		TAG_BLOCK_TS(tag) = timeStamp;
+		TAG_PARENT(tag) = parent;
+		TAG_SERIAL(tag) = serial;
+		TAG_TYPE(tag) = type;
+		TAG_PAGE_ID(tag) = (u8)(i & 0xFF);	// now, page_id = page.
+									// FIX ME!! if more than 256 pages in a block
+
+		SEAL_TAG(tag);
+		
+		buf = _FindBufInDirtyList(dev->buf.dirtyGroup[slot].dirty, i);
+		if (buf != NULL) {
+			if (i == 0)
+				data_sum = _GetDirOrFileNameSum(dev, buf);
+
+			TAG_DATA_LEN(tag) = buf->data_len;
+
+			if (buf->data_len == 0 || (buf->ext_mark & UFFS_BUF_EXT_MARK_TRUNC_TAIL)) { // this only happen when truncating a file
+
+				// when truncating a file, the last dirty buf will be
+				// set as UFFS_BUF_EXT_MARK_TAIL. so that we don't do page recovery
+				// for the rest pages in the block. (file is ended at this page)
+
+				if (!uffs_Assert((buf->ext_mark & UFFS_BUF_EXT_MARK_TRUNC_TAIL) != 0,
+					"buf->data == 0 but not the last page of truncating ? block = %d, page_id = %d",
+					bc->block, i)) {
+
+					// We can't do more about it for now ...
+				}
+
+				if (buf->data_len > 0) {
+					flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
+				}
+				else {
+					// data_len == 0, no I/O needed.
+					flash_op_err = UFFS_FLASH_NO_ERR;
+				}
+				succRecover = U_TRUE;
+				break;
+			}
+			else
+				flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
+
+			if (flash_op_err != UFFS_FLASH_NO_ERR) {
+				if (flash_op_err == UFFS_FLASH_BAD_BLK) {
+					uffs_Perror(UFFS_MSG_NORMAL,
+								"new bad block %d discovered.", newBlock);
+					break;
+				}
+				else if (flash_op_err == UFFS_FLASH_IO_ERR) {
+					uffs_Perror(UFFS_MSG_NORMAL,
+								"writing to block %d page %d, I/O error ?", 
+								(int)newBlock, (int)i);
+					break;
+				}
+				else {
+					uffs_Perror(UFFS_MSG_SERIOUS, "Unhandled flash op result: %d", flash_op_err);
+					break;
+				}
+			}
+		}
+		else {
+			page = uffs_FindPageInBlockWithPageId(dev, bc, i);
+			if (page == UFFS_INVALID_PAGE) {
+				succRecover = U_TRUE;
+				break;  //end of last page, normal break
+			}
+			page = uffs_FindBestPageInBlock(dev, bc, page);
+
+			if (!uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page ?\n"))
+				break;
+
+			oldTag = GET_TAG(bc, page);
+
+			// First, try to find existing cached buffer.
+			// Note: do not call uffs_BufGetEx() as it may trigger buf flush and result in infinite loop
+			buf = uffs_BufGet(dev, parent, serial, i);
+
+			if (buf == NULL) {  // no cached page buffer, use clone buffer.
+				useCloneBuf = U_TRUE;
+				buf = uffs_BufClone(dev, NULL);
+				if (buf == NULL) {
+					uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone a new buf!");
+					break;
+				}
+				x = uffs_BufLoadPhyData(dev, buf, bc->block, page);
+				if (x == U_FAIL) {
+					if (HAVE_BADBLOCK(dev) && dev->bad.block == bc->block) {
+						// the old block is a bad block, we'll process it later.
+						uffs_Perror(UFFS_MSG_SERIOUS,
+									"the old block %d is a bad block, \
+									but ignore it for now.",
+									bc->block);
+					}
+					else {
+						uffs_Perror(UFFS_MSG_SERIOUS, "I/O error ?");
+						uffs_BufFreeClone(dev, buf);
+						flash_op_err = UFFS_FLASH_IO_ERR;
+						break;
+					}
+				}
+
+				buf->type = type;
+				buf->parent = parent;
+				buf->serial = serial;
+				buf->page_id = TAG_PAGE_ID(oldTag);
+				buf->data_len = TAG_DATA_LEN(oldTag);
+
+			}
+			else {
+				useCloneBuf = U_FALSE;
+
+				uffs_Assert(buf->page_id == TAG_PAGE_ID(oldTag), "buf->page_id = %d, tag page id: %d", buf->page_id, TAG_PAGE_ID(oldTag));
+				uffs_Assert(buf->data_len == TAG_DATA_LEN(oldTag), "buf->data_len = %d, tag data len: %d", buf->data_len, TAG_DATA_LEN(oldTag));
+			}
+
+			if (buf->data_len > dev->com.pg_data_size) {
+				uffs_Perror(UFFS_MSG_NOISY, "data length over flow, truncated !");
+				buf->data_len = dev->com.pg_data_size;
+			}
+
+			if (!uffs_Assert(buf->data_len != 0, "data_len == 0 ? block %d, page %d, serial %d, parent %d",
+				bc->block, page, buf->serial, buf->parent)) {
+				// this could be some error on flash ? we can't do more about it for now ...
+			}
+
+			TAG_DATA_LEN(tag) = buf->data_len;
+
+			if (i == 0)
+				data_sum = _GetDirOrFileNameSum(dev, buf);
+
+			flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag);
+
+			if (buf) {
+				if (useCloneBuf)
+					uffs_BufFreeClone(dev, buf);
+				else
+					uffs_BufPut(dev, buf);
+			}
+
+			if (flash_op_err == UFFS_FLASH_BAD_BLK) {
+				uffs_Perror(UFFS_MSG_NORMAL,
+							"new bad block %d discovered.", newBlock);
+				break;
+			}
+			else if (flash_op_err == UFFS_FLASH_IO_ERR) {
+				uffs_Perror(UFFS_MSG_NORMAL, "I/O error ?", newBlock);
+				break;
+			}
+		}
+	} //end of for
+
+	if (i == dev->attr->pages_per_block)
+		succRecover = U_TRUE;
+	else {
+		// expire last page info cache in case the 'tag' is not written.
+		uffs_BlockInfoExpire(dev, newBc, i);
+	}
+
+	if (flash_op_err == UFFS_FLASH_BAD_BLK) {
+		uffs_BlockInfoExpire(dev, newBc, UFFS_ALL_PAGES);
+		uffs_BlockInfoPut(dev, newBc);
+		if (newNode->u.list.block == dev->bad.block) {
+			// the recovered block is a BAD block (buy me a lotto, please :-), we need to 
+			// deal with it immediately (mark it as 'bad' and put into bad block list).
+			uffs_BadBlockProcess(dev, newNode);
+		}
+
+		uffs_Perror(UFFS_MSG_NORMAL, "Retry block cover ...");
+
+		goto retry; // retry on a new erased block ...
+	}
+
+	if (succRecover == U_TRUE) {
+		// now it's time to clean the dirty buffers
+		for (i = 0; i < dev->attr->pages_per_block; i++) {
+			buf = _FindBufInDirtyList(dev->buf.dirtyGroup[slot].dirty, i);
+			if (buf) {
+				if (_BreakFromDirty(dev, buf) == U_SUCC) {
+					buf->mark = UFFS_BUF_VALID;
+					buf->ext_mark &= ~UFFS_BUF_EXT_MARK_TRUNC_TAIL;
+					_MoveNodeToHead(dev, buf);
+				}
+			}
+		}
+
+		// swap the old block node and new block node.
+		// it's important that we 'swap' the block and keep the node unchanged
+		// so that allowing someone hold the node pointer unawared.
+		switch (type) {
+		case UFFS_TYPE_DIR:
+			node->u.dir.parent = parent;
+			node->u.dir.serial = serial;
+			node->u.dir.block = newBlock;
+			node->u.dir.checksum = data_sum;
+			break;
+		case UFFS_TYPE_FILE:
+			node->u.file.parent = parent;
+			node->u.file.serial = serial;
+			node->u.file.block = newBlock;
+			node->u.file.checksum = data_sum;
+			break;
+		case UFFS_TYPE_DATA:
+			node->u.data.parent = parent;
+			node->u.data.serial = serial;
+			node->u.data.block = newBlock;
+			break;
+		default:
+			uffs_Perror(UFFS_MSG_SERIOUS, "UNKNOW TYPE");
+			break;
+		}
+
+		newNode->u.list.block = bc->block;
+
+		// if the recovered block is a bad block, it's time to process it.
+		if (HAVE_BADBLOCK(dev) && dev->bad.block == newNode->u.list.block) {
+			//uffs_Perror(UFFS_MSG_SERIOUS, "Still have bad block ?");
+			uffs_BadBlockProcess(dev, newNode);
+		}
+		else {
+			// erase recovered block, put it back to erased block list.
+			if (uffs_IsThisBlockUsed(dev, bc)) {
+				uffs_FlashEraseBlock(dev, bc->block);
+			}
+			if (HAVE_BADBLOCK(dev))
+				uffs_BadBlockProcess(dev, newNode);
+			else
+				uffs_TreeInsertToErasedListTail(dev, newNode);
+		}
+	}
+	else {
+
+		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);  // FIXME: this might not be necessary ...
+
+		uffs_FlashEraseBlock(dev, newBlock);
+		newNode->u.list.block = newBlock;
+		if (HAVE_BADBLOCK(dev))
+			uffs_BadBlockProcess(dev, newNode);
+		else
+			uffs_TreeInsertToErasedListTail(dev, newNode);
+	}
+
+	if (dev->buf.dirtyGroup[slot].dirty != NULL ||
+			dev->buf.dirtyGroup[slot].count != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL, "still have dirty buffer ?");
+	}
+
+	uffs_BlockInfoPut(dev, newBc);
+
+ext:
+	return (succRecover == U_TRUE ? U_SUCC : U_FAIL);
+
+}
+
+
+
+/** 
+ * \brief flush buffer to a new block which is not registered in tree
+ *
+ * Scenario:
+ *		1. get a new block
+ *		2. write pages in dirty list to new block, sorted by page_id
+ *		3. insert new block to tree
+ */
+static URET _BufFlush_NewBlock(uffs_Device *dev, int slot)
+{
+	u8 type;
+	TreeNode *node;
+	uffs_BlockInfo *bc;
+	URET ret;
+
+	ret = U_FAIL;
+
+	node = uffs_TreeGetErasedNode(dev);
+	if (node == NULL) {
+		uffs_Perror(UFFS_MSG_NOISY, "no erased block!");
+		goto ext;
+	}
+	bc = uffs_BlockInfoGet(dev, node->u.list.block);
+	if (bc == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail!");
+		uffs_InsertToErasedListHead(dev, node); //put node back to erased list
+		goto ext;
+	}
+
+	type = dev->buf.dirtyGroup[slot].dirty->type;
+	
+	ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
+
+	if (ret == U_SUCC)
+		uffs_InsertNodeToTree(dev, type, node);
+	else {
+		uffs_FlashEraseBlock(dev, bc->block);
+		uffs_TreeInsertToErasedListTail(dev, node);
+	}		
+
+	uffs_BlockInfoPut(dev, bc);
+ext:
+	return ret;
+}
+
+
+/** 
+ * \brief flush buffer to a block with enough free pages 
+ *  
+ *  pages in dirty list must be sorted by page_id to write to flash
+ */
+static
+URET
+ uffs_BufFlush_Exist_With_Enough_FreePage(
+		uffs_Device *dev,
+		int slot,			//!< dirty group slot
+		TreeNode *node,		//!< tree node
+		uffs_BlockInfo *bc	//!< block info (Source, also destination)
+		)		
+{
+	u16 page;
+	uffs_Buf *buf;
+	uffs_Tags *tag;
+	URET ret = U_FAIL;
+	int x;
+
+//	uffs_Perror(UFFS_MSG_NOISY,
+//					"Flush buffers with Enough Free Page to block %d",
+//					bc->block);
+
+	for (page = 1;	// page 0 won't be a free page, so we start from 1.
+			page < dev->attr->pages_per_block &&
+			dev->buf.dirtyGroup[slot].count > 0;		//still has dirty pages?
+			page++) {
+
+		// locate to the free page (make sure the page is a erased page, so an unclean page won't sneak in)
+		for (; page < dev->attr->pages_per_block; page++) {
+			if (uffs_IsPageErased(dev, bc, page))
+				break;
+		}
+
+		if (!uffs_Assert(page < dev->attr->pages_per_block, "no free page? buf flush not finished."))
+			break;
+
+		buf = _FindMinimunPageIdFromDirtyList(dev->buf.dirtyGroup[slot].dirty);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+						"count > 0, but no dirty pages in list ?");
+			goto ext;
+		}
+
+		//write the dirty page (id: buf->page_id) to page (free page)
+		tag = GET_TAG(bc, page);
+		TAG_DIRTY_BIT(tag) = TAG_DIRTY;
+		TAG_VALID_BIT(tag) = TAG_VALID;
+		TAG_BLOCK_TS(tag) = uffs_GetBlockTimeStamp(dev, bc);
+		TAG_DATA_LEN(tag) = buf->data_len;
+		TAG_TYPE(tag) = buf->type;
+		TAG_PARENT(tag) = buf->parent;
+		TAG_SERIAL(tag) = buf->serial;
+		TAG_PAGE_ID(tag) = (u8)(buf->page_id);
+
+		SEAL_TAG(tag);
+
+		x = uffs_FlashWritePageCombine(dev, bc->block, page, buf, tag);
+		if (x == UFFS_FLASH_IO_ERR) {
+			uffs_Perror(UFFS_MSG_NORMAL, "I/O error <1>?");
+			goto ext;
+		}
+		else if (x == UFFS_FLASH_BAD_BLK) {
+			uffs_Perror(UFFS_MSG_NORMAL, "Bad blcok found, start block cover ...");
+			ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
+			goto ext;
+		}
+		else {
+			if(_BreakFromDirty(dev, buf) == U_SUCC) {
+				buf->mark = UFFS_BUF_VALID;
+				_MoveNodeToHead(dev, buf);
+			}
+		}
+	} //end of for
+	
+	if (dev->buf.dirtyGroup[slot].dirty != NULL ||
+			dev->buf.dirtyGroup[slot].count != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL, "still has dirty buffer ?");
+	}
+	else {
+		ret = U_SUCC;
+	}
+
+ext:
+	return ret;
+}
+
+
+URET _BufFlush(struct uffs_DeviceSt *dev,
+			   UBOOL force_block_recover, int slot)
+{
+	uffs_Buf *dirty;
+	TreeNode *node;
+	uffs_BlockInfo *bc;
+	u16 n;
+	URET ret;
+	u8 type;
+	u16 parent;
+	u16 serial;
+	int block;
+	
+	if (dev->buf.dirtyGroup[slot].count == 0) {
+		return U_SUCC;
+	}
+
+	dirty = dev->buf.dirtyGroup[slot].dirty;
+
+	if (_CheckDirtyList(dirty) == U_FAIL)
+		return U_FAIL;
+
+	type = dirty->type;
+	parent = dirty->parent;
+	serial = dirty->serial;
+
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		node = uffs_TreeFindDirNode(dev, serial);
+		break;
+	case UFFS_TYPE_FILE:
+		node = uffs_TreeFindFileNode(dev, serial);
+		break;
+	case UFFS_TYPE_DATA:
+		node = uffs_TreeFindDataNode(dev, parent, serial);
+		break;
+	default:
+		uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
+		return U_FAIL;
+	}
+
+	if (node == NULL) {
+		//not found in the tree, need to generate a new block
+		ret = _BufFlush_NewBlock(dev, slot);
+	}
+	else {
+		switch (type) {
+		case UFFS_TYPE_DIR:
+			block = node->u.dir.block;
+			break;
+		case UFFS_TYPE_FILE:
+			block = node->u.file.block;
+			break;
+		case UFFS_TYPE_DATA:
+			block = node->u.data.block;
+			break;
+		default:
+			uffs_Perror(UFFS_MSG_SERIOUS, "unknown type.");
+			return U_FAIL;
+		}
+		bc = uffs_BlockInfoGet(dev, block);
+		if(bc == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "get block info fail.");
+			return U_FAIL;
+		}
+
+		ret = uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
+		if (ret == U_SUCC) {
+
+			n = uffs_GetFreePagesCount(dev, bc);
+
+			if (n >= dev->buf.dirtyGroup[slot].count && !force_block_recover) {
+				//The free pages are enough for the dirty pages
+				ret = uffs_BufFlush_Exist_With_Enough_FreePage(dev,	slot, node, bc);
+			}
+			else {
+				ret = uffs_BufFlush_Exist_With_BlockCover(dev, slot, node, bc);
+			}
+		}
+		uffs_BlockInfoPut(dev, bc);
+	}
+
+	return ret;
+}
+
+static int _FindMostDirtyGroup(struct uffs_DeviceSt *dev)
+{
+	int i, slot = -1;
+	int max_count = 0;
+
+	for (i = 0; i < dev->cfg.dirty_groups; i++) {
+		if (dev->buf.dirtyGroup[i].dirty &&
+				dev->buf.dirtyGroup[i].lock == 0) {
+			if (dev->buf.dirtyGroup[i].count > max_count) {
+				max_count = dev->buf.dirtyGroup[i].count;
+				slot = i;
+			}
+		}
+	}
+
+	return slot;
+}
+
+/** lock dirty group */
+URET uffs_BufLockGroup(struct uffs_DeviceSt *dev, int slot)
+{
+	URET ret = U_FAIL;
+	if (slot >= 0 && slot < dev->cfg.dirty_groups) {
+		dev->buf.dirtyGroup[slot].lock++;
+		ret = U_SUCC;
+	}	
+	return ret;
+}
+
+/** unlock dirty group */
+URET uffs_BufUnLockGroup(struct uffs_DeviceSt *dev, int slot)
+{
+	URET ret = U_FAIL;
+
+	if (slot >= 0 && slot < dev->cfg.dirty_groups) {
+		if (dev->buf.dirtyGroup[slot].lock > 0)
+			dev->buf.dirtyGroup[slot].lock--;
+		else {
+			uffs_Perror(UFFS_MSG_SERIOUS, "Try to unlock an unlocked group ?");
+		}
+		ret = U_SUCC;
+	}	
+	return ret;
+}
+
+
+/** 
+ * flush buffers to flash.
+ * this will flush all dirty groups.
+ * \param[in] dev uffs device
+ */
+URET uffs_BufFlush(struct uffs_DeviceSt *dev)
+{
+	int slot;
+
+	slot = uffs_BufFindFreeGroupSlot(dev);
+	if (slot >= 0)
+		return U_SUCC;	// do nothing if there is free slot
+	else
+		return uffs_BufFlushMostDirtyGroup(dev);
+}
+
+/** 
+ * flush most dirty group
+ * \param[in] dev uffs device
+ */
+URET uffs_BufFlushMostDirtyGroup(struct uffs_DeviceSt *dev)
+{
+	int slot;
+
+	slot = _FindMostDirtyGroup(dev);
+	if (slot >= 0) {
+		return _BufFlush(dev, U_FALSE, slot);
+	}
+	return U_SUCC;
+}
+
+/** 
+ * flush buffers to flash
+ * this will pick up a most dirty group,
+ * and flush it if there is no free dirty group slot.
+ *
+ * \param[in] dev uffs device
+ * \param[in] force_block_recover #U_TRUE: force a block recover
+ *				 even there are enough free pages
+ */
+URET uffs_BufFlushEx(struct uffs_DeviceSt *dev, UBOOL force_block_recover)
+{
+	int slot;
+
+	slot = uffs_BufFindFreeGroupSlot(dev);
+	if (slot >= 0) {
+		return U_SUCC;  //there is free slot, do nothing.
+	}
+	else {
+		slot = _FindMostDirtyGroup(dev);
+		return _BufFlush(dev, force_block_recover, slot);
+	}
+}
+
+/**
+ * flush buffer group with given parent/serial num.
+ *
+ * \param[in] dev uffs device
+ * \param[in] parent parent num of the group
+ * \param[in] serial serial num of the group
+ */
+URET uffs_BufFlushGroup(struct uffs_DeviceSt *dev, u16 parent, u16 serial)
+{
+	int slot;
+
+	slot = uffs_BufFindGroupSlot(dev, parent, serial);
+	if (slot >= 0) {
+		return _BufFlush(dev, U_FALSE, slot);
+	}
+
+	return U_SUCC;
+}
+
+/**
+ * flush buffer group with given parent/serial num
+ * and force_block_recover indicator.
+ *
+ * \param[in] dev uffs device
+ * \param[in] parent parent num of the group
+ * \param[in] serial serial num of group
+ * \param[in] force_block_recover indicator
+ */
+URET uffs_BufFlushGroupEx(struct uffs_DeviceSt *dev,
+						  u16 parent, u16 serial, UBOOL force_block_recover)
+{
+	int slot;
+
+	slot = uffs_BufFindGroupSlot(dev, parent, serial);
+	if (slot >= 0) {
+		return _BufFlush(dev, force_block_recover, slot);
+	}
+
+	return U_SUCC;
+}
+
+
+/**
+ * flush buffer group/groups which match given parent num.
+ *
+ * \param[in] dev uffs device
+ * \param[in] parent parent num of the group
+ * \param[in] serial serial num of group
+ * \param[in] force_block_recover indicator
+ */
+URET uffs_BufFlushGroupMatchParent(struct uffs_DeviceSt *dev, u16 parent)
+{
+	int slot;
+	uffs_Buf *buf;
+	URET ret = U_SUCC;
+
+	for (slot = 0; slot < dev->cfg.dirty_groups && ret == U_SUCC; slot++) {
+		if (dev->buf.dirtyGroup[slot].dirty) {
+			buf = dev->buf.dirtyGroup[slot].dirty;
+			if (buf->parent == parent) {
+				ret = _BufFlush(dev, U_FALSE, slot);
+			}
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * find a free dirty group slot
+ *
+ * \param[in] dev uffs device
+ * \return slot index (0 to MAX_DIRTY_BUF_GROUPS - 1) if found one,
+ *			 otherwise return -1.
+ */
+int uffs_BufFindFreeGroupSlot(struct uffs_DeviceSt *dev)
+{
+	int i, slot = -1;
+
+	for (i = 0; i < dev->cfg.dirty_groups; i++) {
+		if (dev->buf.dirtyGroup[i].dirty == NULL) {
+			slot = i;
+			break;
+		}
+	}
+	return slot;
+}
+
+/**
+ * find a dirty group slot with given parent/serial num.
+ *
+ * \param[in] dev uffs device
+ * \param[in] parent parent num of the group
+ * \param[in] serial serial num of group
+ * \return slot index (0 to MAX_DIRTY_BUF_GROUPS - 1) if found one,
+ *			otherwise return -1.
+ */
+int uffs_BufFindGroupSlot(struct uffs_DeviceSt *dev, u16 parent, u16 serial)
+{
+	uffs_Buf *buf;
+	int i, slot = -1;
+
+	for (i = 0; i < dev->cfg.dirty_groups; i++) {
+		if (dev->buf.dirtyGroup[i].dirty) {
+			buf = dev->buf.dirtyGroup[i].dirty;
+			if (buf->parent == parent && buf->serial == serial) {
+				slot = i;
+				break;
+			}
+		}
+	}
+	return slot;
+}
+
+/** 
+ * \brief get a page buffer
+ * \param[in] dev uffs device
+ * \param[in] parent parent serial num
+ * \param[in] serial serial num
+ * \param[in] page_id page_id
+ * \return return the buffer found in buffer list, if not found, return NULL.
+ */
+uffs_Buf * uffs_BufGet(struct uffs_DeviceSt *dev,
+					   u16 parent, u16 serial, u16 page_id)
+{
+	uffs_Buf *p;
+
+	//first, check whether the buffer exist in buf list ?
+	p = uffs_BufFind(dev, parent, serial, page_id);
+
+	if (p) {
+		p->ref_count++;
+		_MoveNodeToHead(dev, p);
+	}
+
+	return p;
+}
+
+/** 
+ * New generate a buffer
+ */
+uffs_Buf *uffs_BufNew(struct uffs_DeviceSt *dev,
+					  u8 type, u16 parent, u16 serial, u16 page_id)
+{
+	uffs_Buf *buf;
+
+	buf = uffs_BufGet(dev, parent, serial, page_id);
+	if (buf) {
+		if (buf->ref_count > 1) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "When create new buf, \
+						an exist buffer has ref count %d, possibly bug!",
+						buf->ref_count);
+		}
+		else {
+			buf->data_len = 0;
+		}
+		_MoveNodeToHead(dev, buf);
+		return buf;
+	}
+
+	buf = _FindFreeBuf(dev);
+	if (buf == NULL) {
+		uffs_BufFlushMostDirtyGroup(dev);
+		buf = _FindFreeBuf(dev);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "no free page buf!");
+			return NULL;
+		}
+	}
+
+	buf->mark = UFFS_BUF_EMPTY;
+	buf->type = type;
+	buf->parent = parent;
+	buf->serial = serial;
+	buf->page_id = page_id;
+	buf->data_len = 0;
+	buf->ref_count++;
+	memset(buf->data, 0xff, dev->com.pg_data_size);
+
+	_MoveNodeToHead(dev, buf);
+	
+	return buf;	
+}
+
+
+
+/** 
+ * get a page buffer
+ * \param[in] dev uffs device
+ * \param[in] type dir, file or data ?
+ * \param[in] node node on the tree
+ * \param[in] page_id page_id
+ * \param[in] oflag the open flag of current file/dir object
+ * \return return the buffer if found in buffer list, if not found in 
+ *		buffer list, it will get a free buffer, and load data from flash.
+ *		return NULL if not free buffer.
+ */
+uffs_Buf *uffs_BufGetEx(struct uffs_DeviceSt *dev,
+						u8 type, TreeNode *node, u16 page_id, int oflag)
+{
+	uffs_Buf *buf;
+	u16 parent, serial, block, page;
+	uffs_BlockInfo *bc;
+
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		parent = node->u.dir.parent;
+		serial = node->u.dir.serial;
+		block = node->u.dir.block;
+		break;
+	case UFFS_TYPE_FILE:
+		parent = node->u.file.parent;
+		serial = node->u.file.serial;
+		block = node->u.file.block;
+		break;
+	case UFFS_TYPE_DATA:
+		parent = node->u.data.parent;
+		serial = node->u.data.serial;
+		block = node->u.data.block;
+		break;
+	default:
+		uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
+		return NULL;
+	}
+
+	buf = uffs_BufFind(dev, parent, serial, page_id);
+	if (buf) {
+		buf->ref_count++;
+		return buf;
+	}
+
+	buf = _FindFreeBuf(dev);
+	if (buf == NULL) {
+		uffs_BufFlushMostDirtyGroup(dev);
+		buf = _FindFreeBuf(dev);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "no free page buf!");
+			return NULL;
+		}
+
+		/* Note: if uffs_BufFlushMostDirtyGroup() flush the same block as we'll write to,
+		 *	the block will be changed to a new one! (and the content of 'node' is changed).
+		 *	So here we need to update block number from the new 'node'.
+		 */
+		switch (type) {
+		case UFFS_TYPE_DIR:
+			block = node->u.dir.block;
+			break;
+		case UFFS_TYPE_FILE:
+			block = node->u.file.block;
+			break;
+		case UFFS_TYPE_DATA:
+			block = node->u.data.block;
+			break;
+		default:
+			uffs_Perror(UFFS_MSG_SERIOUS, "unknown type");
+			return NULL;
+		}
+	}
+
+	bc = uffs_BlockInfoGet(dev, block);
+	if (bc == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't get block info!");
+		return NULL;
+	}
+	
+	page = uffs_FindPageInBlockWithPageId(dev, bc, page_id);
+	if (page == UFFS_INVALID_PAGE) {
+		uffs_BlockInfoPut(dev, bc);
+		uffs_Perror(UFFS_MSG_SERIOUS, "can't find right page ? block %d page_id %d", bc->block, page_id);
+		return NULL;
+	}
+	page = uffs_FindBestPageInBlock(dev, bc, page);
+	if (!uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page?\n"))
+		return NULL;
+
+	uffs_BlockInfoPut(dev, bc);
+
+	buf->mark = UFFS_BUF_EMPTY;
+	buf->type = type;
+	buf->parent = parent;
+	buf->serial = serial;
+	buf->page_id = page_id;
+
+	if (UFFS_FLASH_HAVE_ERR(uffs_FlashReadPage(dev, block, page, buf, oflag & UO_NOECC ? U_TRUE : U_FALSE))) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "can't load page from flash !");
+		return NULL;
+	}
+
+	buf->data_len = TAG_DATA_LEN(GET_TAG(bc, page));
+	buf->mark = UFFS_BUF_VALID;
+	buf->ref_count++;
+
+	_MoveNodeToHead(dev, buf);
+	
+	return buf;
+
+}
+
+/** 
+ * \brief Put back a page buffer, make reference count decrease by one
+ * \param[in] dev uffs device
+ * \param[in] buf buffer to be put back
+ */
+URET uffs_BufPut(uffs_Device *dev, uffs_Buf *buf)
+{
+	URET ret = U_FAIL;
+
+	dev = dev;
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_NORMAL,  "Can't put an NULL buffer!");
+	}
+	else if (buf->ref_count == 0) {
+		uffs_Perror(UFFS_MSG_NORMAL,  "Putting an unused page buffer ? ");
+	}
+	else if (buf->ref_count == CLONE_BUF_MARK) {
+		uffs_Perror(UFFS_MSG_NORMAL, "Putting an cloned page buffer ? ");
+		ret = uffs_BufFreeClone(dev, buf);
+	}
+	else {
+		buf->ref_count--;
+		ret = U_SUCC;
+	}
+
+	return ret;
+}
+
+
+/** 
+ * \brief clone from an exist buffer.
+		allocate memory for new buffer, and copy data from original buffer if 
+		original buffer is not NULL. 
+ * \param[in] dev uffs device
+ * \param[in] buf page buffer to be clone from.
+ *				if NULL presented here, data copy will not be processed
+ * \return return the cloned page buffer, all data copied from source
+ * \note the cloned buffer is not linked in page buffer list in uffs device,
+ *			so you should use #uffs_BufFreeClone instead of #uffs_BufPut
+ *			when you put back or release buffer
+ */
+uffs_Buf * uffs_BufClone(uffs_Device *dev, uffs_Buf *buf)
+{
+	uffs_Buf *p;
+
+	p = _FindFreeBufEx(dev, 1);
+	if (p == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"no enough free pages for clone! " \
+					"Please increase Clone Buffer Count threshold.");
+	}
+	else {
+		_BreakFromBufList(dev, p);
+
+		if (buf) {
+			p->parent = buf->parent;
+			p->type = buf->type;
+			p->serial = buf->serial;
+			p->page_id = buf->page_id;
+			
+			p->data_len = buf->data_len;
+			//athough the valid data length is .data_len,
+			//but we still need copy the whole buffer, include header
+			memcpy(p->header, buf->header, dev->com.pg_size);
+		}
+		p->next = p->prev = NULL; // the cloned one is not linked to device buffer
+		p->next_dirty = p->prev_dirty = NULL;
+		p->ref_count = CLONE_BUF_MARK;	// CLONE_BUF_MARK indicates that
+										// this is an cloned buffer
+	}
+
+	return p;
+}
+
+/** 
+ * \brief release cloned buffer
+ * \param[in] dev uffs device
+ * \param[in] buf cloned buffer
+ */
+URET uffs_BufFreeClone(uffs_Device *dev, uffs_Buf *buf)
+{
+	dev = dev; //make compiler happy
+	if (!buf)
+		return U_FAIL;
+
+	if (buf->ref_count != CLONE_BUF_MARK) {
+		/* a cloned buffer must have a ref_count of CLONE_BUF_MARK */
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"Try to release a non-cloned page buffer ?");
+		return U_FAIL;
+	}
+
+	buf->ref_count = 0;
+	buf->mark = UFFS_BUF_EMPTY;
+	_LinkToBufListTail(dev, buf);
+
+	return U_SUCC;
+}
+
+
+
+UBOOL uffs_BufIsAllFree(struct uffs_DeviceSt *dev)
+{
+	uffs_Buf *buf = dev->buf.head;
+
+	while (buf) {
+		if(buf->ref_count != 0) return U_FALSE;
+		buf = buf->next;
+	}
+
+	return U_TRUE;
+}
+
+UBOOL uffs_BufIsAllEmpty(struct uffs_DeviceSt *dev)
+{
+	uffs_Buf *buf = dev->buf.head;
+
+	while (buf) {
+		if(buf->mark != UFFS_BUF_EMPTY) return U_FALSE;
+		buf = buf->next;
+	}
+
+	return U_TRUE;
+}
+
+
+URET uffs_BufSetAllEmpty(struct uffs_DeviceSt *dev)
+{
+	uffs_Buf *buf = dev->buf.head;
+
+	while (buf) {
+		buf->mark = UFFS_BUF_EMPTY;
+		buf = buf->next;
+	}
+	return U_SUCC;
+}
+
+
+void uffs_BufIncRef(uffs_Buf *buf)
+{
+	buf->ref_count++;
+}
+
+void uffs_BufDecRef(uffs_Buf *buf)
+{
+	if (buf->ref_count > 0)
+		buf->ref_count--;
+}
+
+/** mark buffer as #UFFS_BUF_EMPTY if ref_count == 0,
+	and discard all data it holds */
+void uffs_BufMarkEmpty(uffs_Device *dev, uffs_Buf *buf)
+{
+	if (buf->mark != UFFS_BUF_EMPTY) {
+		if (buf->ref_count == 0) {
+			if (buf->mark == UFFS_BUF_DIRTY)
+				_BreakFromDirty(dev, buf);
+			buf->mark = UFFS_BUF_EMPTY;
+		}
+	}
+}
+
+#if 0
+static UBOOL _IsBufInDirtyList(struct uffs_DeviceSt *dev, uffs_Buf *buf)
+{
+	uffs_Buf *p = dev->buf.dirtyGroup[slot].dirty;
+
+	while (p) {
+		if(p == buf) return U_TRUE;
+		p = p->next_dirty;
+	}
+
+	return U_FALSE;
+}
+#endif
+
+URET uffs_BufWrite(struct uffs_DeviceSt *dev,
+				   uffs_Buf *buf, void *data, u32 ofs, u32 len)
+{
+	int slot;
+
+	if(ofs + len > dev->com.pg_data_size) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"data length out of range! %d+%d", ofs, len);
+		return U_FAIL;
+	}
+
+	slot = uffs_BufFindGroupSlot(dev, buf->parent, buf->serial);
+
+	if (slot < 0) {
+		// need to take a free slot
+		slot = uffs_BufFindFreeGroupSlot(dev);
+		if (slot < 0) {
+			// no free slot ? flush buffer
+			if (uffs_BufFlushMostDirtyGroup(dev) != U_SUCC)
+				return U_FAIL;
+
+			slot = uffs_BufFindFreeGroupSlot(dev);
+			if (slot < 0) {
+				// still no free slot ??
+				uffs_Perror(UFFS_MSG_SERIOUS, "no free slot ?");
+				return U_FAIL;
+			}
+		}
+	}
+
+	if (data)
+		memcpy(buf->data + ofs, data, len);
+	else
+		memset(buf->data + ofs, 0, len);	// if data == NULL, then fill all '\0'.
+
+	if (ofs + len > buf->data_len) 
+		buf->data_len = ofs + len;
+	
+	if (_IsBufInInDirtyList(dev, slot, buf) == U_FALSE) {
+		_LinkToDirtyList(dev, slot, buf);
+	}
+
+	if (dev->buf.dirtyGroup[slot].count >= dev->buf.dirty_buf_max) {
+		if (uffs_BufFlushGroup(dev, buf->parent, buf->serial) != U_SUCC) {
+			return U_FAIL;
+		}
+	}
+
+	return U_SUCC;
+}
+
+URET uffs_BufRead(struct uffs_DeviceSt *dev,
+				  uffs_Buf *buf, void *data, u32 ofs, u32 len)
+{
+	u32 readSize;
+	u32 pg_data_size = dev->com.pg_data_size;
+
+	readSize = (ofs >= pg_data_size ? 
+					0 : (ofs + len >= pg_data_size ? pg_data_size - ofs : len)
+				);
+
+	if (readSize > 0) 
+		memcpy(data, buf->data + ofs, readSize);
+
+	return U_SUCC;
+}
+
+
+
+
+
+
+

+ 93 - 0
src/uffs/uffs_crc.c

@@ -0,0 +1,93 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+/** 
+ * \file uffs_crc.c
+ * \brief simple CRC functions
+ * \author Ricky Zheng
+ * \note Created in 23 Nov, 2011
+ */
+
+#include "uffs/uffs_crc.h"
+
+/* CRC16 Table */
+static const u16 CRC16_TBL[256] = {
+  0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+  0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+  0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+  0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+  0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+  0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+  0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+  0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+  0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+  0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+  0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+  0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+  0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+  0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+  0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+  0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+  0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+  0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+  0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+  0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+  0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+  0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+  0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+  0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+  0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+  0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+  0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+  0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+  0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+  0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+  0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+  0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+#define CRC16(v, x) v = ((v) >> 8) ^ CRC16_TBL[((v) ^ (x)) & 0x00ff]
+
+u16 uffs_crc16update(const void *data, int length, u16 crc)
+{
+	int i;
+	const u8 *p = (const u8 *)data;
+	for (i = 0; i < length; i++, p++) {
+		CRC16(crc, *p);
+	}
+
+	return crc;
+}
+
+u16 uffs_crc16sum(const void *data, int length)
+{
+	return uffs_crc16update(data, length, 0xFFFF);
+}

+ 188 - 0
src/uffs/uffs_debug.c

@@ -0,0 +1,188 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_debug.c
+ * \brief output debug messages
+ * \author Ricky Zheng, created 10th May, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+
+#if !defined(RT_THREAD)
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define MAX_DEBUG_MSG_LINE_LENGTH	128
+
+static struct uffs_DebugMsgOutputSt * m_ops = NULL;
+static int m_msg_level = UFFS_MSG_NORMAL;
+
+static void uffs_vprintf(const char *fmt, va_list args)
+{
+	char line_buf[MAX_DEBUG_MSG_LINE_LENGTH];
+
+	if (m_ops && m_ops->output) {
+#ifdef _COMPILER_CLIB_DO_NOT_HAVE_VSNPRINTF_
+		vsprintf(line_buf, fmt, args);
+#else
+		vsnprintf(line_buf, MAX_DEBUG_MSG_LINE_LENGTH, fmt, args);
+#endif
+		m_ops->output(line_buf);
+	}
+}
+
+/**
+ * \brief Initialize debug message output functions
+ */
+URET uffs_InitDebugMessageOutput(struct uffs_DebugMsgOutputSt *ops, int msg_level)
+{
+	m_ops = ops;
+
+	if (m_ops == NULL || m_ops->output == NULL) {
+		m_ops = NULL;
+		return U_FAIL;
+	}
+	else if (m_ops->vprintf == NULL)
+		m_ops->vprintf = uffs_vprintf;
+
+	m_msg_level = msg_level;
+
+	return U_SUCC;
+}
+
+void uffs_DebugSetMessageLevel(int msg_level)
+{
+	m_msg_level = msg_level;
+}
+#endif
+
+#ifdef CONFIG_ENABLE_UFFS_DEBUG_MSG
+
+/**
+ * \brief The main debug message output function
+ */
+#if !defined(RT_THREAD)
+void uffs_DebugMessage(int level, const char *prefix,
+					   const char *suffix, const char *errFmt, ...)
+{
+	va_list arg;
+
+	if (m_ops && level >= m_msg_level) {
+		if (prefix)
+			m_ops->output(prefix);
+
+		va_start(arg, errFmt);
+		m_ops->vprintf(errFmt, arg);
+		va_end(arg);
+
+		if (suffix)
+			m_ops->output(suffix);
+	}
+}
+
+/**
+ * \brief Called when an assert occurred.
+ * This method is called when an assert occurred and should stop the
+ * application from running, as this there is a severe error condition.
+ * \param[in] file Source filename
+ * \param[in] line Source line of code
+ * \param[in] msg Assert message
+ */
+void uffs_AssertCall(const char *file, int line, const char *msg, ...)
+{
+	va_list args;
+	char buf[32];
+
+	if (m_ops && m_msg_level < UFFS_MSG_NOMSG) {
+		m_ops->output("ASSERT ");
+		m_ops->output(file);
+		sprintf(buf, ":%d - :", line);
+		m_ops->output(buf);
+		va_start(args, msg);
+		m_ops->vprintf(msg, args);
+		va_end(args);
+		m_ops->output(TENDSTR);
+	}
+}
+#endif
+
+#ifdef _COMPILER_DO_NOT_SUPPORT_MACRO_VALIST_REPLACE_
+void uffs_Perror(int level, const char *fmt, ...)
+{
+	va_list args;
+	if (m_ops && level >= m_msg_level) {
+		va_start(args, fmt);
+		m_ops->vprintf(fmt, args);
+		va_end(args);
+		m_ops->output(TENDSTR);
+	}
+}
+
+void uffs_PerrorRaw(int level, const char *fmt, ...)
+{
+	va_list args;
+	if (m_ops && level >= m_msg_level) {
+		va_start(args, fmt);
+		m_ops->vprintf(fmt, args);
+		va_end(args);
+	}
+}
+
+UBOOL uffs_Assert(UBOOL expr, const char *fmt, ...)
+{
+	va_list args;
+	if (m_ops && !expr && m_msg_level < UFFS_MSG_NOMSG) {
+		m_ops->output("ASSERT FAILED: ");
+		va_start(args, fmt);
+		m_ops->vprintf(fmt, args);
+		va_end(args);
+		m_ops->output(TENDSTR);
+	}
+	return expr ? U_TRUE : U_FALSE;
+}
+#endif
+
+#else
+
+void uffs_DebugMessage(int level, const char *prefix, const char *suffix, const char *errFmt, ...) {};
+void uffs_AssertCall(const char *file, int line, const char *msg, ...) {};
+
+#ifdef _COMPILER_DO_NOT_SUPPORT_MACRO_VALIST_REPLACE_
+void uffs_Perror(int level, const char *fmt, ...) {}
+void uffs_PerrorRaw(int level, const char *fmt, ...) {}
+UBOOL uffs_Assert(UBOOL expr, const char *fmt, ...) { return expr ? U_TRUE : U_FALSE; }
+#endif
+
+#endif

+ 93 - 0
src/uffs/uffs_device.c

@@ -0,0 +1,93 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_device.c
+ * \brief uffs device operation
+ * \author Ricky Zheng, created 10th May, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_mtb.h"
+#include <string.h>
+
+#define PFX "dev : "
+
+
+#ifdef CONFIG_USE_PER_DEVICE_LOCK
+void uffs_DeviceInitLock(uffs_Device *dev)
+{
+	uffs_SemCreate(&dev->lock.sem);
+	dev->lock.task_id = UFFS_TASK_ID_NOT_EXIST;
+	dev->lock.counter = 0;
+}
+
+void uffs_DeviceReleaseLock(uffs_Device *dev)
+{
+	uffs_SemDelete(&dev->lock.sem);
+}
+
+void uffs_DeviceLock(uffs_Device *dev)
+{
+	uffs_SemWait(dev->lock.sem);
+	
+	if (dev->lock.counter != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"Lock device, counter %d NOT zero?!", dev->lock.counter);
+	}
+
+	dev->lock.counter++;
+}
+
+void uffs_DeviceUnLock(uffs_Device *dev)
+{
+	dev->lock.counter--;
+
+	if (dev->lock.counter != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"Unlock device, counter %d NOT zero?!", dev->lock.counter);
+	}
+	
+	uffs_SemSignal(dev->lock.sem);
+}
+
+#else
+
+/* dummy stubs */
+void uffs_DeviceInitLock(uffs_Device *dev) {}
+void uffs_DeviceReleaseLock(uffs_Device *dev) {}
+void uffs_DeviceLock(uffs_Device *dev) {}
+void uffs_DeviceUnLock(uffs_Device *dev) {}
+
+#endif

+ 384 - 0
src/uffs/uffs_ecc.c

@@ -0,0 +1,384 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_ecc.c
+ * \brief ecc maker and correct
+ * \author Ricky Zheng, created in 12th Jun, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_fs.h"
+#include <string.h>
+
+#define PFX "ecc : "
+
+static const u8 bits_tbl[256] = {
+	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+	2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+	3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+static const u8 line_parity_tbl[16] = {
+	0x00, 0x02, 0x08, 0x0a, 0x20, 0x22, 0x28, 0x2a,
+	0x80, 0x82, 0x88, 0x8a, 0xa0, 0xa2, 0xa8, 0xaa
+};
+
+static const u8 line_parity_prime_tbl[16] = {
+	0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
+	0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55
+};
+
+static const u8 column_parity_tbl[256] = {
+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
+};
+
+/**
+ * calculate 3 bytes ECC for 256 bytes data.
+ *
+ * \param[in] data input data
+ * \param[out] ecc output ecc
+ * \param[in] length of data in bytes
+ */
+static void uffs_EccMakeChunk256(const void *data, void *ecc, u16 len)
+{
+	u8 *pecc = (u8 *)ecc;
+	const u8 *p = (const u8 *)data;
+	u8 b, col_parity = 0, line_parity = 0, line_parity_prime = 0;
+	u16 i;
+
+	for (i = 0; i < len; i++) {
+		b = column_parity_tbl[*p++];
+		col_parity ^= b;
+		if (b & 0x01) { // odd number of bits in the byte
+			line_parity ^= i;
+			line_parity_prime ^= ~i;
+		}
+	}
+
+	// ECC layout:
+	// Byte[0]  P64   | P64'   | P32  | P32'  | P16  | P16'  | P8   | P8'
+	// Byte[1]  P1024 | P1024' | P512 | P512' | P256 | P256' | P128 | P128'
+	// Byte[2]  P4    | P4'    | P2   | P2'   | P1   | P1'   | 1    | 1
+	pecc[0] = ~(line_parity_tbl[line_parity & 0xf] |
+				line_parity_prime_tbl[line_parity_prime & 0xf]);
+	pecc[1] = ~(line_parity_tbl[line_parity >> 4] |
+				line_parity_prime_tbl[line_parity_prime >> 4]);
+	pecc[2] = (~col_parity) | 0x03;
+
+}
+
+
+/**
+ * calculate ECC. (3 bytes ECC per 256 data)
+ *
+ * \param[in] data input data
+ * \param[in] data_len length of data in byte
+ * \param[out] ecc output ecc
+ *
+ * \return length of ECC in byte. (3 bytes ECC per 256 data) 
+ */
+int uffs_EccMake(const void *data, int data_len, void *ecc)
+{
+	const u8 *p_data = (const u8 *)data;
+	u8 *p_ecc = (u8 *)ecc;
+	int len;
+
+	if (data == NULL || ecc == NULL)
+		return 0;
+
+	while (data_len > 0) {
+		len = data_len > 256 ? 256 : data_len;
+		uffs_EccMakeChunk256(p_data, p_ecc, len);
+		data_len -= len;
+		p_data += len;
+		p_ecc += 3;
+	}
+
+	return p_ecc - (u8 *)ecc;
+}
+
+/**
+ * perform ECC error correct for 256 bytes data chunk.
+ *
+ * \param[in|out] data input data to be corrected
+ * \param[in] read_ecc 3 bytes ECC read from storage
+ * \param[in] test_ecc 3 bytes ECC calculated from data
+ * \param[in] errtop top position of error
+ *
+ * \return:  0 -- no error
+ *			-1 -- can not be corrected
+ *			>0 -- how many bits corrected
+ */
+static int uffs_EccCorrectChunk256(void *data, void *read_ecc,
+								   const void *test_ecc, int errtop)
+{
+	u8 d0, d1, d2;		/* deltas */
+	u8 *p = (u8 *)data;
+	u8 *pread_ecc = (u8 *)read_ecc, *ptest_ecc = (u8 *)test_ecc;
+
+	d0 = pread_ecc[0] ^ ptest_ecc[0];
+	d1 = pread_ecc[1] ^ ptest_ecc[1];
+	d2 = pread_ecc[2] ^ ptest_ecc[2];
+	
+	if ((d0 | d1 | d2) == 0)
+		return 0;
+	
+	if( ((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
+	    ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
+	    ((d2 ^ (d2 >> 1)) & 0x54) == 0x54)
+	{
+		// Single bit (recoverable) error in data
+
+		u8 b;
+		u8 bit;
+		
+		bit = b = 0;		
+		
+		if(d1 & 0x80) b |= 0x80;
+		if(d1 & 0x20) b |= 0x40;
+		if(d1 & 0x08) b |= 0x20;
+		if(d1 & 0x02) b |= 0x10;
+		if(d0 & 0x80) b |= 0x08;
+		if(d0 & 0x20) b |= 0x04;
+		if(d0 & 0x08) b |= 0x02;
+		if(d0 & 0x02) b |= 0x01;
+
+		if(d2 & 0x80) bit |= 0x04;
+		if(d2 & 0x20) bit |= 0x02;
+		if(d2 & 0x08) bit |= 0x01;
+
+		if (b >= errtop) return -1;
+
+		p[b] ^= (1 << bit);
+		
+		return 1;
+	}
+	
+	if ((bits_tbl[d0] + bits_tbl[d1] + bits_tbl[d2]) == 1) {
+		// error in ecc, no action need		
+		return 1;
+	}
+	
+	// Unrecoverable error
+	return -1;
+}
+
+/**
+ * perform ECC error correct
+ *
+ * \param[in|out] data input data to be corrected
+ * \param[in] data_len length of data in byte
+ * \param[in] read_ecc ECC read from storage
+ * \param[in] test_ecc ECC calculated from data
+ *
+ * \return:  0 -- no error
+ *			-1 -- can not be corrected
+ *			>0 -- how many bits corrected
+ */
+
+int uffs_EccCorrect(void *data, int data_len,
+					void *read_ecc, const void *test_ecc)
+{
+	u8 *p_data = (u8 *)data;
+	u8 *p_read_ecc = (u8 *)read_ecc;
+	u8 *p_test_ecc = (u8 *)test_ecc;
+	int total = 0, ret, len;
+
+	if (data == NULL || read_ecc == NULL || test_ecc == NULL)
+		return -1;
+
+	while (data_len > 0) {
+		len = (data_len > 256 ? 256 : data_len);
+		ret = uffs_EccCorrectChunk256(p_data, p_read_ecc, p_test_ecc, len);
+		if (ret < 0) {
+			total = ret;
+			break;
+		}
+		else
+			total += ret;
+
+		p_data += len;
+		p_read_ecc += 3;
+		p_test_ecc += 3;
+		data_len -= len;
+	}
+
+	return total;
+
+}
+
+/** 
+ * generate 12 bit ecc for 8 bytes data. 
+ *	(use 0xFF padding if the data length is less then 8 bytes)
+ *
+ * \param[in] data input data
+ * \param[in] data_len length of data in byte
+ *
+ * \return 12 bits ECC data (lower 12 bits).
+ */
+u16 uffs_EccMake8(void *data, int data_len)
+{
+	u8 *p = (u8 *)data;
+	u8 b, col_parity = 0, line_parity = 0, line_parity_prime = 0;
+	u8 i;
+	u16 ecc = 0;
+
+
+	data_len = (data_len > 8 ? 8 : data_len);
+
+	for (i = 0; i < data_len; i++) {
+		b = column_parity_tbl[*p++];
+		col_parity ^= b;
+		if (b & 0x01) { // odd number of bits in the byte
+			line_parity ^= i;
+			line_parity_prime ^= ~i;
+		}
+	}
+
+	// ECC layout:
+	// row:         (1) | (1)  | P32 | P32' | P16 | P16' | P8  | P8'
+	// column:      P4  | P4'  | P2  | P2'  | P1  | P1'  | (1) | (1)
+	// 12-bit ecc:  P32 | P32' | P16 | P16' | P8  | P8'  | P4  | P4' | P2 | P2' | P1 | P1' |
+	ecc = (~(
+			line_parity_tbl[line_parity & 0xf] |
+			line_parity_prime_tbl[line_parity_prime & 0xf]
+			)) << 6;
+	ecc |= (((~col_parity) >> 2) & 0x3f);
+
+	return ecc & 0xfff;
+}
+
+/**
+ * correct 8 bytes data from 12 bits ECC
+ *
+ * \param[in|out] data input data
+ * \param[in] read_ecc ecc read from storage
+ * \param[in] test_ecc ecc calculated from data
+ * \param[in] errtop top position of error.
+ *
+ * \return:  0 -- no error
+ *			-1 -- can not be corrected
+ *			>0 -- how many bits corrected
+ */
+int uffs_EccCorrect8(void *data, u16 read_ecc, u16 test_ecc, int errtop)
+{
+	u8 d0, d1;			/* deltas */
+	u8 *p = (u8 *)data;
+
+	read_ecc &= 0xfff;
+	test_ecc &= 0xfff;
+
+	d0 = (read_ecc >> 6) ^ (test_ecc >> 6);
+	d1 = (read_ecc & 0x3f) ^ (test_ecc & 0x3f);
+
+	if ((d0 | d1) == 0)
+		return 0;
+	
+	if( ((d0 ^ (d0 >> 1)) & 0x15) == 0x15 &&
+	    ((d1 ^ (d1 >> 1)) & 0x15) == 0x15)
+	{
+		// Single bit (recoverable) error in data
+
+		u8 b;
+		u8 bit;
+		
+		bit = b = 0;		
+		
+		if(d0 & 0x20) b |= 0x04;
+		if(d0 & 0x08) b |= 0x02;
+		if(d0 & 0x02) b |= 0x01;
+
+		if(d1 & 0x20) bit |= 0x04;
+		if(d1 & 0x08) bit |= 0x02;
+		if(d1 & 0x02) bit |= 0x01;
+
+		if (b >= (u8)errtop) return -1;
+		if (bit >= 8) return -1;
+
+		p[b] ^= (1 << bit);
+		
+		return 1;
+	}
+	
+	if ((bits_tbl[d0] + bits_tbl[d1]) == 1) {
+		// error in ecc, no action need		
+		return 1;
+	}
+	
+	// Unrecoverable error
+	return -1;
+}
+

+ 746 - 0
src/uffs/uffs_fd.c

@@ -0,0 +1,746 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fd.c
+ * \brief POSIX like, hight level file operations
+ * \author Ricky Zheng, created 8th Jun, 2005
+ */
+
+#include <string.h>
+#include "uffs_config.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_fd.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_version.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_find.h"
+
+#define PFX "fd  : "
+
+/**
+ * \brief POSIX DIR
+ */
+struct uffs_dirSt {
+    struct uffs_ObjectSt   *obj;		/* dir object */
+    struct uffs_FindInfoSt f;			/* find info */
+    struct uffs_ObjectInfoSt info;		/* object info */
+    struct uffs_dirent dirent;			/* dir entry */
+};
+
+
+#define FD_OFFSET		3	//!< just make file handler more like POSIX (0, 1, 2 for stdin/stdout/stderr)
+
+#define OBJ2FD(obj)	\
+	( \
+		( \
+			uffs_PoolGetIndex(uffs_GetObjectPool(), obj) | \
+			(_fd_signature << FD_SIGNATURE_SHIFT) \
+		) \
+		+ FD_OFFSET \
+	)
+
+/**
+ * check #fd signature, convert #fd to #obj
+ * if success, hold global file system lock, otherwise return with #ret
+ */
+#define CHK_OBJ_LOCK(fd, obj, ret)	\
+	do { \
+		uffs_GlobalFsLockLock(); \
+		fd -= FD_OFFSET; \
+		if ( (fd >> FD_SIGNATURE_SHIFT) != _fd_signature ) { \
+			uffs_set_error(-UEBADF); \
+			uffs_Perror(UFFS_MSG_NOISY, "invalid fd: %d (sig: %d, expect: %d)", \
+					fd + FD_OFFSET, fd >> FD_SIGNATURE_SHIFT, _fd_signature); \
+			uffs_GlobalFsLockUnlock(); \
+			return (ret); \
+		} \
+		fd = fd & ((1 << FD_SIGNATURE_SHIFT) - 1); \
+		obj = (uffs_Object *)uffs_PoolGetBufByIndex(uffs_GetObjectPool(), fd); \
+		if ((obj) == NULL || \
+				uffs_PoolVerify(uffs_GetObjectPool(), (obj)) == U_FALSE || \
+				uffs_PoolCheckFreeList(uffs_GetObjectPool(), (obj)) == U_TRUE) { \
+			uffs_set_error(-UEBADF); \
+			uffs_Perror(UFFS_MSG_NOISY, "invalid obj"); \
+			uffs_GlobalFsLockUnlock(); \
+			return (ret); \
+		} \
+	} while(0)
+
+/**
+ * check #dirp signature,
+ * if success, hold global file system lock,
+ * otherwise return with #ret
+ */
+#define CHK_DIR_LOCK(dirp, ret)	\
+	do { \
+		uffs_GlobalFsLockLock(); \
+		if ((dirp) == NULL || \
+				uffs_PoolVerify(&_dir_pool, (dirp)) == U_FALSE || \
+				uffs_PoolCheckFreeList(&_dir_pool, (dirp)) == U_TRUE) { \
+			uffs_set_error(-UEBADF); \
+			uffs_Perror(UFFS_MSG_NOISY, "invalid dirp"); \
+			uffs_GlobalFsLockUnlock(); \
+			return (ret); \
+		} \
+	} while(0)
+
+/**
+ * check #dirp signature,
+ * if success, hold global file system lock,
+ * otherwise return void
+ */
+#define CHK_DIR_VOID_LOCK(dirp)	\
+	do { \
+		uffs_GlobalFsLockLock(); \
+		if ((dirp) == NULL || \
+				uffs_PoolVerify(&_dir_pool, (dirp)) == U_FALSE || \
+				uffs_PoolCheckFreeList(&_dir_pool, (dirp)) == U_TRUE) { \
+			uffs_set_error(-UEBADF); \
+			uffs_Perror(UFFS_MSG_NOISY, "invalid dirp"); \
+			uffs_GlobalFsLockUnlock(); \
+			return; \
+		} \
+	} while(0)
+
+
+static int _dir_pool_data[sizeof(uffs_DIR) * MAX_DIR_HANDLE / sizeof(int)];
+static uffs_Pool _dir_pool;
+static int _uffs_errno = 0;
+
+
+//
+// What is fd signature ? fd signature is for detecting file system get formated by other party.
+//   A thread open a file, read()...sleep()...write()...sleep()...
+//   B thread format UFFS partition, increase fd signature.
+//   A thread ...sleep()...read() --> Opps, fd signature changed ! read() return error(expected).
+//
+#define MAX_FD_SIGNATURE_ROUND  (100)
+static int _fd_signature = 0;
+
+//
+// only get called when formating UFFS partition
+//
+void uffs_FdSignatureIncrease(void)
+{
+	if (_fd_signature++ > MAX_FD_SIGNATURE_ROUND)
+		_fd_signature = 0;
+}
+
+/**
+ * initialise uffs_DIR buffers, called by UFFS internal
+ */
+URET uffs_DirEntryBufInit(void)
+{
+	return uffs_PoolInit(&_dir_pool, _dir_pool_data,
+							sizeof(_dir_pool_data),
+							sizeof(uffs_DIR), MAX_DIR_HANDLE);
+}
+
+/**
+ * Release uffs_DIR buffers, called by UFFS internal
+ */
+URET uffs_DirEntryBufRelease(void)
+{
+	return uffs_PoolRelease(&_dir_pool);
+}
+
+/**
+ * Put all dir entry buf match dev
+ */
+int uffs_DirEntryBufPutAll(uffs_Device *dev)
+{
+	int count = 0;
+	uffs_DIR *dirp = NULL;
+
+	do {
+		dirp = (uffs_DIR *) uffs_PoolFindNextAllocated(&_dir_pool, dirp);
+		if (dirp && dirp->obj && dirp->obj->dev &&
+				dirp->obj->dev->dev_num == dev->dev_num) {
+			uffs_PoolPut(&_dir_pool, dirp);
+			count++;
+		}
+	} while (dirp);
+
+	return count;
+}
+
+
+uffs_Pool * uffs_DirEntryBufGetPool(void)
+{
+	return &_dir_pool;
+}
+
+static uffs_DIR * GetDirEntry(void)
+{
+	uffs_DIR *dirp = (uffs_DIR *) uffs_PoolGet(&_dir_pool);
+
+	if (dirp)
+		memset(dirp, 0, sizeof(uffs_DIR));
+
+	return dirp;
+}
+
+static void PutDirEntry(uffs_DIR *p)
+{
+	uffs_PoolPut(&_dir_pool, p);
+}
+
+
+/** get global errno
+ */
+int uffs_get_error(void)
+{
+	return _uffs_errno;
+}
+
+/** set global errno
+ */
+int uffs_set_error(int err)
+{
+	return (_uffs_errno = err);
+}
+
+/* POSIX compliant file system APIs */
+
+int uffs_open(const char *name, int oflag, ...)
+{
+	uffs_Object *obj;
+	int ret = 0;
+
+	uffs_GlobalFsLockLock();
+
+	obj = uffs_GetObject();
+	if (obj == NULL) {
+		uffs_set_error(-UEMFILE);
+		ret = -1;
+	}
+	else {
+		if (uffs_OpenObject(obj, name, oflag) == U_FAIL) {
+			uffs_set_error(-uffs_GetObjectErr(obj));
+			uffs_PutObject(obj);
+			ret = -1;
+		}
+		else {
+			ret = OBJ2FD(obj);
+		}
+	}
+
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_close(int fd)
+{
+	int ret = 0;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+
+	uffs_ClearObjectErr(obj);
+	if (uffs_CloseObject(obj) == U_FAIL) {
+		uffs_set_error(-uffs_GetObjectErr(obj));
+		ret = -1;
+	}
+	else {
+		uffs_PutObject(obj);
+		ret = 0;
+	}
+
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_read(int fd, void *data, int len)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = uffs_ReadObject(obj, data, len);
+	uffs_set_error(-uffs_GetObjectErr(obj));
+
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_write(int fd, const void *data, int len)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = uffs_WriteObject(obj, data, len);
+	uffs_set_error(-uffs_GetObjectErr(obj));
+
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+long uffs_seek(int fd, long offset, int origin)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = uffs_SeekObject(obj, offset, origin);
+	uffs_set_error(-uffs_GetObjectErr(obj));
+	
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+long uffs_tell(int fd)
+{
+	long ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = (long) uffs_GetCurOffset(obj);
+	uffs_set_error(-uffs_GetObjectErr(obj));
+	
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_eof(int fd)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = uffs_EndOfFile(obj);
+	uffs_set_error(-uffs_GetObjectErr(obj));
+	
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_flush(int fd)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = (uffs_FlushObject(obj) == U_SUCC) ? 0 : -1;
+	uffs_set_error(-uffs_GetObjectErr(obj));
+	
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_rename(const char *old_name, const char *new_name)
+{
+	int err = 0;
+	int ret = 0;
+
+	uffs_GlobalFsLockLock();
+	ret = (uffs_RenameObject(old_name, new_name, &err) == U_SUCC) ? 0 : -1;
+	uffs_set_error(-err);
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_remove(const char *name)
+{
+	int err = 0;
+	int ret = 0;
+	struct uffs_stat st;
+
+	if (uffs_stat(name, &st) < 0) {
+		err = UENOENT;
+		ret = -1;
+	}
+	else if (st.st_mode & US_IFDIR) {
+		err = UEISDIR;
+		ret = -1;
+	}
+	else {
+		uffs_GlobalFsLockLock();
+		if (uffs_DeleteObject(name, &err) == U_SUCC) {
+			ret = 0;
+		}
+		else {
+			ret = -1;
+		}
+		uffs_GlobalFsLockUnlock();
+	}
+
+	uffs_set_error(-err);
+	return ret;
+}
+
+int uffs_ftruncate(int fd, long remain)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+	uffs_ClearObjectErr(obj);
+	ret = (uffs_TruncateObject(obj, remain) == U_SUCC) ? 0 : -1;
+	uffs_set_error(-uffs_GetObjectErr(obj));
+	uffs_GlobalFsLockUnlock();
+	
+	return ret;
+}
+
+static int do_stat(uffs_Object *obj, struct uffs_stat *buf)
+{
+	uffs_ObjectInfo info;
+	int ret = 0;
+	int err = 0;
+
+	if (uffs_GetObjectInfo(obj, &info, &err) == U_FAIL) {
+		ret = -1;
+	}
+	else {
+		buf->st_dev = obj->dev->dev_num;
+		buf->st_ino = info.serial;
+		buf->st_nlink = 0;
+		buf->st_uid = 0;
+		buf->st_gid = 0;
+		buf->st_rdev = 0;
+		buf->st_size = info.len;
+		buf->st_blksize = obj->dev->com.pg_data_size;
+		buf->st_blocks = 0;
+		buf->st_atime = info.info.last_modify;
+		buf->st_mtime = info.info.last_modify;
+		buf->st_ctime = info.info.create_time;
+		buf->st_mode = (info.info.attr & FILE_ATTR_DIR ? US_IFDIR : US_IFREG);
+		if (info.info.attr & FILE_ATTR_WRITE)
+			buf->st_mode |= US_IRWXU;
+	}
+
+	uffs_set_error(-err);
+	return ret;
+}
+
+int uffs_stat(const char *name, struct uffs_stat *buf)
+{
+	uffs_Object *obj;
+	int ret = 0;
+	int err = 0;
+	URET result;
+
+	uffs_GlobalFsLockLock();
+
+	obj = uffs_GetObject();
+	if (obj) {
+		if (*name && name[strlen(name) - 1] == '/') {
+			result = uffs_OpenObject(obj, name, UO_RDONLY | UO_DIR);
+		}
+		else {
+			if ((result = uffs_OpenObject(obj, name, UO_RDONLY)) != U_SUCC)	// try file
+				result = uffs_OpenObject(obj, name, UO_RDONLY | UO_DIR);	// then try dir
+		}
+		if (result == U_SUCC) {
+			ret = do_stat(obj, buf);
+			uffs_CloseObject(obj);
+		}
+		else {
+			err = uffs_GetObjectErr(obj);
+			ret = -1;
+		}
+		uffs_PutObject(obj);
+	}
+	else {
+		err = UENOMEM;
+		ret = -1;
+	}
+
+	uffs_set_error(-err);
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_lstat(const char *name, struct uffs_stat *buf)
+{
+	return uffs_stat(name, buf);
+}
+
+int uffs_fstat(int fd, struct uffs_stat *buf)
+{
+	int ret;
+	uffs_Object *obj;
+
+	CHK_OBJ_LOCK(fd, obj, -1);
+
+	ret = do_stat(obj, buf);
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_closedir(uffs_DIR *dirp)
+{
+	CHK_DIR_LOCK(dirp, -1);
+
+	uffs_FindObjectClose(&dirp->f);
+	if (dirp->obj) {
+		uffs_CloseObject(dirp->obj);
+		uffs_PutObject(dirp->obj);
+	}
+	PutDirEntry(dirp);
+	uffs_GlobalFsLockUnlock();
+
+	return 0;
+}
+
+uffs_DIR * uffs_opendir(const char *path)
+{
+	int err = 0;
+	uffs_DIR *ret = NULL;
+	uffs_DIR *dirp;
+
+	uffs_GlobalFsLockLock();
+
+	dirp = GetDirEntry();
+
+	if (dirp) {
+		dirp->obj = uffs_GetObject();
+		if (dirp->obj) {
+			if (uffs_OpenObject(dirp->obj, path, UO_RDONLY | UO_DIR) == U_SUCC) {
+				if (uffs_FindObjectOpen(&dirp->f, dirp->obj) == U_SUCC) {
+					ret = dirp;
+					goto ext;
+				}
+				else {
+					uffs_CloseObject(dirp->obj);
+				}
+			}
+			else {
+				err = uffs_GetObjectErr(dirp->obj);
+			}
+			uffs_PutObject(dirp->obj);
+			dirp->obj = NULL;
+		}
+		else {
+			err = UEMFILE;
+		}
+		PutDirEntry(dirp);
+	}
+	else {
+		err = UEMFILE;
+	}
+ext:
+	uffs_set_error(-err);
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+struct uffs_dirent * uffs_readdir(uffs_DIR *dirp)
+{
+	struct uffs_dirent *ent = NULL;
+
+	CHK_DIR_LOCK(dirp, NULL);
+
+	if (uffs_FindObjectNext(&dirp->info, &dirp->f) == U_SUCC) {
+		ent = &dirp->dirent;
+		ent->d_ino = dirp->info.serial;
+		ent->d_namelen = dirp->info.info.name_len < (sizeof(ent->d_name) - 1) ? dirp->info.info.name_len : (sizeof(ent->d_name) - 1);
+		memcpy(ent->d_name, dirp->info.info.name, ent->d_namelen);
+		ent->d_name[ent->d_namelen] = '\0';
+		ent->d_off = dirp->f.pos;
+		ent->d_reclen = sizeof(struct uffs_dirent);
+		ent->d_type = dirp->info.info.attr;
+	}
+	uffs_GlobalFsLockUnlock();
+
+	return ent;
+}
+
+void uffs_rewinddir(uffs_DIR *dirp)
+{
+	CHK_DIR_VOID_LOCK(dirp);
+
+	uffs_FindObjectRewind(&dirp->f);
+
+	uffs_GlobalFsLockUnlock();
+}
+
+
+int uffs_mkdir(const char *name, ...)
+{
+	uffs_Object *obj;
+	int ret = 0;
+	int err = 0;
+
+	uffs_GlobalFsLockLock();
+
+	obj = uffs_GetObject();
+	if (obj) {
+		if (uffs_CreateObject(obj, name, UO_CREATE|UO_DIR) != U_SUCC) {
+			err = obj->err;
+			ret = -1;
+		}
+		else {
+			uffs_CloseObject(obj);
+			ret = 0;
+		}
+		uffs_PutObject(obj);
+	}
+	else {
+		err = UEMFILE;
+		ret = -1;
+	}
+
+	uffs_set_error(-err);
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+int uffs_rmdir(const char *name)
+{
+	int err = 0;
+	int ret = 0;
+	struct uffs_stat st;
+
+	if (uffs_stat(name, &st) < 0) {
+		err = UENOENT;
+		ret = -1;
+	}
+	else if ((st.st_mode & US_IFDIR) == 0) {
+		err = UENOTDIR;
+		ret = -1;
+	}
+	else {
+		uffs_GlobalFsLockLock();
+		if (uffs_DeleteObject(name, &err) == U_SUCC) {
+			ret = 0;
+		}
+		else {
+			ret = -1;
+		}
+		uffs_GlobalFsLockUnlock();
+	}
+	uffs_set_error(-err);
+	return ret;
+}
+
+int uffs_version(void)
+{
+	return uffs_GetVersion();
+}
+
+int uffs_format(const char *mount_point)
+{
+	uffs_Device *dev = NULL;
+	URET ret = U_FAIL;
+
+	dev = uffs_GetDeviceFromMountPoint(mount_point);
+	if (dev) {
+		uffs_GlobalFsLockLock();
+		ret = uffs_FormatDevice(dev, U_TRUE);
+		uffs_GlobalFsLockUnlock();
+	}
+
+	return ret == U_SUCC ? 0 : -1;
+}
+
+long uffs_space_total(const char *mount_point)
+{
+	uffs_Device *dev = NULL;
+	long ret = -1L;
+
+	dev = uffs_GetDeviceFromMountPoint(mount_point);
+	if (dev) {
+		uffs_GlobalFsLockLock();
+		ret = (long) uffs_GetDeviceTotal(dev);
+		uffs_GlobalFsLockUnlock();
+	}
+
+	return ret;
+}
+
+long uffs_space_used(const char *mount_point)
+{
+	uffs_Device *dev = NULL;
+	long ret = -1L;
+
+	dev = uffs_GetDeviceFromMountPoint(mount_point);
+	if (dev) {
+		uffs_GlobalFsLockLock();
+		ret = (long) uffs_GetDeviceUsed(dev);
+		uffs_GlobalFsLockUnlock();
+	}
+
+	return ret;
+}
+
+long uffs_space_free(const char *mount_point)
+{
+	uffs_Device *dev = NULL;
+	long ret = -1L;
+
+	dev = uffs_GetDeviceFromMountPoint(mount_point);
+	if (dev) {
+		uffs_GlobalFsLockLock();
+		ret = (long) uffs_GetDeviceFree(dev);
+		uffs_GlobalFsLockUnlock();
+	}
+
+	return ret;
+}
+
+
+void uffs_flush_all(const char *mount_point)
+{
+	uffs_Device *dev = NULL;
+
+	dev = uffs_GetDeviceFromMountPoint(mount_point);
+	if (dev) {
+		uffs_GlobalFsLockLock();
+		uffs_BufFlushAll(dev);
+		uffs_PutDevice(dev);
+		uffs_GlobalFsLockUnlock();
+	}
+}
+

+ 378 - 0
src/uffs/uffs_find.c

@@ -0,0 +1,378 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_find.c
+ * \brief find objects under dir
+ * \author Ricky Zheng, created 13th July, 2009
+ */
+
+#include <string.h> 
+#include <stdio.h>
+#include "uffs_config.h"
+#include "uffs/uffs_find.h"
+
+#define TPOOL(dev) &((dev)->mem.tree_pool)
+
+static void ResetFindInfo(uffs_FindInfo *f)
+{
+	f->hash = 0;
+	f->work = NULL;
+	f->step = 0;
+	f->pos = 0;
+}
+
+static URET _LoadObjectInfo(uffs_Device *dev,
+							TreeNode *node,
+							uffs_ObjectInfo *info,
+							int type,
+							int *err)
+{
+	uffs_Buf *buf;
+
+	buf = uffs_BufGetEx(dev, (u8)type, node, 0, 0);
+
+	if (buf == NULL) {
+		if (err)
+			*err = UENOMEM;
+		return U_FAIL;
+	}
+
+	memcpy(&(info->info), buf->data, sizeof(uffs_FileInfo));
+
+	if (type == UFFS_TYPE_DIR) {
+		info->len = 0;
+		info->serial = node->u.dir.serial;
+	}
+	else {
+		info->len = node->u.file.len;
+		info->serial = node->u.file.serial;
+	}
+
+	uffs_BufPut(dev, buf);
+
+	return U_SUCC;
+}
+
+/**
+ * get object information
+ *
+ * \param[in] obj the object to be revealed
+ * \param[out] info object information will be loaded to info
+ * \param[out] err return error code if failed
+ *
+ * \return U_SUCC or U_FAIL
+ *
+ * \node the obj should be openned before call this function.
+ */
+URET uffs_GetObjectInfo(uffs_Object *obj, uffs_ObjectInfo *info, int *err)
+{
+	uffs_Device *dev = obj->dev;
+	URET ret = U_FAIL;
+
+	uffs_DeviceLock(dev);
+
+	if (obj && dev && info) {
+		if (obj->parent == PARENT_OF_ROOT) {
+			// this is ROOT. UFFS does not physically has root, just fake it ...
+			memset(info, 0, sizeof(uffs_ObjectInfo));
+			info->serial = obj->serial;
+			info->info.attr |= (FILE_ATTR_DIR | FILE_ATTR_WRITE);
+			if (err)
+				*err = UENOERR;
+			ret = U_SUCC;
+		}
+		else
+			ret = _LoadObjectInfo(dev, obj->node, info, obj->type, err);
+	}
+	else {
+		if (err)
+			*err = UEINVAL;
+	}
+
+	uffs_DeviceUnLock(dev);
+
+	return ret;
+}
+
+
+/**
+ * Open a FindInfo for finding objects under dir
+ *
+ * \param[out] f uffs_FindInfo structure
+ * \param[in] dir an openned dir object (openned by uffs_OpenObject() ). 
+ *
+ * \return U_SUCC if success, U_FAIL if invalid param or the dir
+ *			is not been openned.
+ */
+URET uffs_FindObjectOpen(uffs_FindInfo *f, uffs_Object *dir)
+{
+	if (f == NULL || dir == NULL ||
+			dir->dev == NULL || dir->open_succ != U_TRUE)
+		return U_FAIL;
+
+	f->dev = dir->dev;
+	f->serial = dir->serial;
+	ResetFindInfo(f);
+
+	return U_SUCC;
+}
+
+/**
+ * Open a FindInfo for finding objects under dir
+ *
+ * \param[out] f uffs_FindInfo structure
+ * \param[in] dev uffs device
+ * \param[in] dir serial number of the dir to be searched
+ *
+ * \return U_SUCC if success, U_FAIL if invalid param or the dir
+ *			serial number is not valid.
+ */
+URET uffs_FindObjectOpenEx(uffs_FindInfo *f, uffs_Device *dev, int dir)
+{
+	TreeNode *node;
+
+	if (f == NULL || dev == NULL)
+		return U_FAIL;
+
+	node = uffs_TreeFindDirNode(dev, dir);
+
+	if (node == NULL)
+		return U_FAIL;
+
+	f->serial = dir;
+	f->dev = dev;
+	ResetFindInfo(f);
+
+	return U_SUCC;
+}
+
+
+static URET do_FindObject(uffs_FindInfo *f, uffs_ObjectInfo *info, u16 x)
+{
+	URET ret = U_SUCC;
+	TreeNode *node;
+	uffs_Device *dev = f->dev;
+
+	if (f->step == 0) { //!< working on dirs
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.dir.parent == f->serial) {
+				f->work = node;
+				f->pos++;
+				if (info)
+					ret = _LoadObjectInfo(dev, node, info, UFFS_TYPE_DIR, NULL);
+				goto ext;
+			}
+			x = node->hash_next;
+		}
+
+		f->hash++; //come to next hash entry
+
+		for (; f->hash < DIR_NODE_ENTRY_LEN; f->hash++) {
+			x = dev->tree.dir_entry[f->hash];
+			while (x != EMPTY_NODE) {
+				node = FROM_IDX(x, TPOOL(dev));
+				if (node->u.dir.parent == f->serial) {
+					f->work = node;
+					f->pos++;
+					if (info)
+						ret = _LoadObjectInfo(dev, node, info, UFFS_TYPE_DIR, NULL);
+					goto ext;
+				}
+				x = node->hash_next;
+			}
+		}
+
+		//no subdirs, then lookup files ..
+		f->step++;
+		f->hash = 0;
+		x = dev->tree.file_entry[f->hash];
+	}
+
+	if (f->step == 1) {
+
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.file.parent == f->serial) {
+				f->work = node;
+				f->pos++;
+				if (info)
+					ret = _LoadObjectInfo(dev, node, info, UFFS_TYPE_FILE, NULL);
+				goto ext;
+			}
+			x = node->hash_next;
+		}
+
+		f->hash++; //come to next hash entry
+
+		for (; f->hash < FILE_NODE_ENTRY_LEN; f->hash++) {
+			x = dev->tree.file_entry[f->hash];
+			while (x != EMPTY_NODE) {
+				node = FROM_IDX(x, TPOOL(dev));
+				if (node->u.file.parent == f->serial) {
+					f->work = node;
+					f->pos++;
+					if (info) 
+						ret = _LoadObjectInfo(dev, node, info, UFFS_TYPE_FILE, NULL);
+					goto ext;
+				}
+				x = node->hash_next;
+			}
+		}
+
+		//no any files, stopped.
+		f->step++;
+	}
+
+	ret = U_FAIL;
+ext:
+
+	return ret;
+
+}
+
+
+/**
+ * Find the first object
+ *
+ * \param[out] info the object information will be filled to info.
+ *				if info is NULL, then skip this object.
+ * \param[in] f uffs_FindInfo structure, openned by uffs_FindObjectOpen().
+ *
+ * \return U_SUCC if an object is found, U_FAIL if no object is found.
+ */
+URET uffs_FindObjectFirst(uffs_ObjectInfo * info, uffs_FindInfo * f)
+{
+	uffs_Device *dev = f->dev;
+	URET ret = U_SUCC;
+
+	uffs_DeviceLock(dev);
+	ResetFindInfo(f);
+	ret = do_FindObject(f, info, dev->tree.dir_entry[0]);
+	uffs_DeviceUnLock(dev);
+
+	return ret;
+}
+
+/**
+ * Find the next object.
+ *
+ * \param[out] info the object information will be filled to info.
+ *				if info is NULL, then skip this object.
+ * \param[in] f uffs_FindInfo structure, openned by uffs_FindObjectOpen().
+ *
+ * \return U_SUCC if an object is found, U_FAIL if no object is found.
+ *
+ * \note uffs_FindObjectFirst() should be called before uffs_FindObjectNext().
+ */
+URET uffs_FindObjectNext(uffs_ObjectInfo *info, uffs_FindInfo * f)
+{
+	uffs_Device *dev = f->dev;
+	URET ret = U_SUCC;
+
+	if (dev == NULL || f->step > 1) 
+		return U_FAIL;
+
+	if (f->work == NULL)
+		return uffs_FindObjectFirst(info, f);
+
+	uffs_DeviceLock(dev);
+	ret = do_FindObject(f, info, f->work->hash_next);
+	uffs_DeviceUnLock(dev);
+
+	return ret;
+}
+
+/**
+ * Rewind a find object process.
+ *
+ * \note After rewind, you can call uffs_FindObjectFirst() to
+ *			start find object process.
+ */
+URET uffs_FindObjectRewind(uffs_FindInfo *f)
+{
+	if (f == NULL)
+		return U_FAIL;
+
+	ResetFindInfo(f);
+
+	return U_SUCC;
+}
+
+/**
+ * Close Find Object.
+ *
+ * \param[in] f uffs_FindInfo structure, openned by uffs_FindObjectOpen().
+ *
+ * \return U_SUCC if success, U_FAIL if invalid param.
+ */
+URET uffs_FindObjectClose(uffs_FindInfo * f)
+{
+	if (f == NULL)
+		return U_FAIL;
+
+	f->dev = NULL;
+	ResetFindInfo(f);
+
+	return U_SUCC;
+}
+
+/**
+ * Count objects
+ *
+ * \param[in] f uffs_FindInfo structure, openned by uffs_FindObjectOpen().
+ *
+ * \return object counts
+ * \note after call this function, you need to call
+ *		 uffs_FindObjectRewind() to start finding process.
+ */
+int uffs_FindObjectCount(uffs_FindInfo *f)
+{
+	if (uffs_FindObjectFirst(NULL, f) == U_SUCC) {
+		while (uffs_FindObjectNext(NULL, f) == U_SUCC) { };
+	}
+	return f->pos;
+}
+
+/**
+ * Return current finding position
+ *
+ * \param[in] f uffs_FindInfo structure, openned by uffs_FindObjectOpen().
+ *
+ * \return current finding position
+ */
+int uffs_FindObjectTell(uffs_FindInfo *f)
+{
+	return f->pos;
+}
+

+ 1034 - 0
src/uffs/uffs_flash.c

@@ -0,0 +1,1034 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_flash.c
+ * \brief UFFS flash interface
+ * \author Ricky Zheng, created 17th July, 2009
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_ecc.h"
+#include "uffs/uffs_flash.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_badblock.h"
+#include "uffs/uffs_crc.h"
+#include <string.h>
+
+#define PFX "flsh: "
+
+#define SPOOL(dev) &((dev)->mem.spare_pool)
+#define HEADER(buf) ((struct uffs_MiniHeaderSt *)(buf)->header)
+
+#define ECC_SIZE(dev)	((dev)->attr->ecc_size)
+#define NOMINAL_ECC_SIZE(dev) (3 * ((((dev)->attr->page_data_size - 1) / 256) + 1))
+
+#define TAG_STORE_SIZE	(sizeof(struct uffs_TagStoreSt))
+
+#define SEAL_BYTE(dev, spare)  spare[(dev)->mem.spare_data_size - 1]	// seal byte is the last byte of spare data
+
+#if defined(CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME)
+/** Linux MTD spare layout for 512 and 2K page size */
+static const u8 MTD512_LAYOUT_ECC[] =	{0, 4, 6, 2, 0xFF, 0};
+static const u8 MTD512_LAYOUT_DATA[] = {8, 8, 0xFF, 0};
+static const u8 MTD2K_LAYOUT_ECC[] = {40, 24, 0xFF, 0};
+static const u8 MTD2K_LAYOUT_DATA[] = {2, 38, 0xFF, 0};
+#endif
+
+static void TagMakeEcc(struct uffs_TagStoreSt *ts)
+{
+	ts->tag_ecc = 0xFFF;
+	ts->tag_ecc = uffs_EccMake8(ts, sizeof(struct uffs_TagStoreSt));
+}
+
+static int TagEccCorrect(struct uffs_TagStoreSt *ts)
+{
+	u16 ecc_store, ecc_read;
+	int ret;
+
+	ecc_store = ts->tag_ecc;
+	ts->tag_ecc = 0xFFF;
+	ecc_read = uffs_EccMake8(ts, sizeof(struct uffs_TagStoreSt));
+	ret = uffs_EccCorrect8(ts, ecc_read, ecc_store, sizeof(struct uffs_TagStoreSt));
+	ts->tag_ecc = ecc_store;	// restore tag ecc
+
+	return ret;
+
+}
+
+/** setup UFFS spare data & ecc layout */
+static void InitSpareLayout(uffs_Device *dev)
+{
+	u8 s; // status byte offset
+	u8 *p;
+
+	s = dev->attr->block_status_offs;
+
+	if (s < TAG_STORE_SIZE) {	/* status byte is within 0 ~ TAG_STORE_SIZE-1 */
+
+		/* spare data layout */
+		p = dev->attr->_uffs_data_layout;
+		if (s > 0) {
+			*p++ = 0;
+			*p++ = s;
+		}
+		*p++ = s + 1;
+		*p++ = TAG_STORE_SIZE - s;
+		*p++ = 0xFF;
+		*p++ = 0;
+
+		/* spare ecc layout */
+		p = dev->attr->_uffs_ecc_layout;
+		if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
+			*p++ = TAG_STORE_SIZE + 1;
+			*p++ = ECC_SIZE(dev);
+		}
+		*p++ = 0xFF;
+		*p++ = 0;
+	}
+	else {	/* status byte > TAG_STORE_SIZE-1 */
+
+		/* spare data layout */
+		p = dev->attr->_uffs_data_layout;
+		*p++ = 0;
+		*p++ = TAG_STORE_SIZE;
+		*p++ = 0xFF;
+		*p++ = 0;
+
+		/* spare ecc layout */
+		p = dev->attr->_uffs_ecc_layout;
+		if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
+			if (s < TAG_STORE_SIZE + ECC_SIZE(dev)) {
+				if (s > TAG_STORE_SIZE) {
+					*p++ = TAG_STORE_SIZE;
+					*p++ = s - TAG_STORE_SIZE;
+				}
+				*p++ = s + 1;
+				*p++ = TAG_STORE_SIZE + ECC_SIZE(dev) - s;
+			}
+			else {
+				*p++ = TAG_STORE_SIZE;
+				*p++ = ECC_SIZE(dev);
+			}
+		}
+		*p++ = 0xFF;
+		*p++ = 0;
+	}
+
+	dev->attr->data_layout = dev->attr->_uffs_data_layout;
+	dev->attr->ecc_layout = dev->attr->_uffs_ecc_layout;
+}
+
+static int CalculateSpareDataSize(uffs_Device *dev)
+{
+	const u8 *p;
+	int ecc_last = 0, tag_last = 0;
+	int ecc_size, tag_size;
+	int n;
+
+	ecc_size = (dev->attr->ecc_opt == UFFS_ECC_NONE ? 0 : ECC_SIZE(dev));
+	
+	p = dev->attr->ecc_layout;
+	if (p) {
+		while (*p != 0xFF && ecc_size > 0) {
+			n = (p[1] > ecc_size ? ecc_size : p[1]);
+			ecc_last = p[0] + n;
+			ecc_size -= n;
+			p += 2;
+		}
+	}
+
+	tag_size = TAG_STORE_SIZE;
+	p = dev->attr->data_layout;
+	if (p) {
+		while (*p != 0xFF && tag_size > 0) {
+			n = (p[1] > tag_size ? tag_size : p[1]);
+			tag_last = p[0] + n;
+			tag_size -= n;
+			p += 2;
+		}
+	}
+
+	n = (ecc_last > tag_last ? ecc_last : tag_last);
+	n = (n > dev->attr->block_status_offs + 1 ?
+			n : dev->attr->block_status_offs + 1);
+
+	return n + 1;		// plus one seal byte.
+}
+
+
+/**
+ * Initialize UFFS flash interface
+ */
+URET uffs_FlashInterfaceInit(uffs_Device *dev)
+{
+	URET ret = U_FAIL;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	uffs_Pool *pool = SPOOL(dev);
+
+	if (dev->mem.spare_pool_size == 0) {
+		if (dev->mem.malloc) {
+			dev->mem.spare_pool_buf = dev->mem.malloc(dev, UFFS_SPARE_BUFFER_SIZE);
+			if (dev->mem.spare_pool_buf)
+				dev->mem.spare_pool_size = UFFS_SPARE_BUFFER_SIZE;
+		}
+	}
+
+	if (UFFS_SPARE_BUFFER_SIZE > dev->mem.spare_pool_size) {
+		uffs_Perror(UFFS_MSG_DEAD,
+					"Spare buffer require %d but only %d available.",
+					UFFS_SPARE_BUFFER_SIZE, dev->mem.spare_pool_size);
+		memset(pool, 0, sizeof(uffs_Pool));
+		goto ext;
+	}
+
+	uffs_Perror(UFFS_MSG_NOISY,
+					"alloc spare buffers %d bytes.",
+					UFFS_SPARE_BUFFER_SIZE);
+	uffs_PoolInit(pool, dev->mem.spare_pool_buf,
+					dev->mem.spare_pool_size,
+					UFFS_MAX_SPARE_SIZE, MAX_SPARE_BUFFERS);
+
+	// init flash driver
+	if (dev->ops->InitFlash) {
+		if (dev->ops->InitFlash(dev) < 0)
+			goto ext;
+	}
+
+	if (dev->ops->WritePage == NULL && dev->ops->WritePageWithLayout == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Flash driver must provide 'WritePage' or 'WritePageWithLayout' function!");
+		goto ext;
+	}
+
+	if (dev->ops->ReadPage == NULL && dev->ops->ReadPageWithLayout == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Flash driver must provide 'ReadPage' or 'ReadPageWithLayout' function!");
+		goto ext;
+	}
+
+	if (dev->attr->layout_opt == UFFS_LAYOUT_UFFS) {
+		/* sanity check */
+
+		if (dev->attr->ecc_size == 0 && dev->attr->ecc_opt != UFFS_ECC_NONE) {
+			dev->attr->ecc_size = NOMINAL_ECC_SIZE(dev);
+		}
+
+		uffs_Perror(UFFS_MSG_NORMAL, "ECC size %d", dev->attr->ecc_size);
+
+		if ((dev->attr->data_layout && !dev->attr->ecc_layout) ||
+			(!dev->attr->data_layout && dev->attr->ecc_layout)) {
+			uffs_Perror(UFFS_MSG_SERIOUS,
+						"Please setup data_layout and ecc_layout, "
+						"or leave them all NULL !");
+			goto ext;
+		}
+
+		if (!attr->data_layout && !attr->ecc_layout) {
+#if defined(CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME)
+			switch(attr->page_data_size) {
+			case 512:
+				attr->ecc_layout = MTD512_LAYOUT_ECC;
+				attr->data_layout = MTD512_LAYOUT_DATA;
+				break;
+			case 2048:
+				attr->ecc_layout = MTD2K_LAYOUT_ECC;
+				attr->data_layout = MTD2K_LAYOUT_DATA;
+				break;
+			default:
+				InitSpareLayout(dev);
+				break;
+			}
+#else
+			InitSpareLayout(dev);
+#endif
+		}
+	}
+	else if (dev->attr->layout_opt == UFFS_LAYOUT_FLASH) {
+		if (dev->ops->WritePageWithLayout == NULL || dev->ops->ReadPageWithLayout == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "When using UFFS_LAYOUT_FLASH option, "
+				"flash driver must provide 'WritePageWithLayout' and 'ReadPageWithLayout' function!");
+			goto ext;
+		}
+	}
+	else {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Invalid layout_opt: %d", dev->attr->layout_opt);
+		goto ext;
+	}
+
+	if (dev->ops->EraseBlock == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Flash driver MUST implement 'EraseBlock()' function !");
+		goto ext;
+	}
+
+	dev->mem.spare_data_size = CalculateSpareDataSize(dev);
+	uffs_Perror(UFFS_MSG_NORMAL, "UFFS consume spare data size %d", dev->mem.spare_data_size);
+
+	if (dev->mem.spare_data_size > dev->attr->spare_size) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "NAND spare(%dB) can't hold UFFS spare data(%dB) !",
+						dev->attr->spare_size, dev->mem.spare_data_size);
+		goto ext;
+	}
+
+	ret = U_SUCC;
+ext:
+	return ret;
+}
+
+/**
+ * Release UFFS flash interface
+ */
+URET uffs_FlashInterfaceRelease(uffs_Device *dev)
+{
+	uffs_Pool *pool;
+
+	pool = SPOOL(dev);
+	if (pool->mem && dev->mem.free) {
+		dev->mem.free(dev, pool->mem);
+		pool->mem = NULL;
+		dev->mem.spare_pool_size = 0;
+	}
+	uffs_PoolRelease(pool);
+	memset(pool, 0, sizeof(uffs_Pool));
+
+	// release flash driver
+	if (dev->ops->ReleaseFlash) {
+		if (dev->ops->ReleaseFlash(dev) < 0)
+			return U_FAIL;
+	}
+
+	return U_SUCC;
+}
+
+/**
+ * unload spare to tag and ecc.
+ */
+void uffs_FlashUnloadSpare(uffs_Device *dev,
+						const u8 *spare, struct uffs_TagStoreSt *ts, u8 *ecc)
+{
+	u8 *p_tag = (u8 *)ts;
+	int tag_size = TAG_STORE_SIZE;
+	int ecc_size = dev->attr->ecc_size;
+	int n;
+	const u8 *p;
+
+	// unload ecc
+	p = dev->attr->ecc_layout;
+	if (p && ecc) {
+		while (*p != 0xFF && ecc_size > 0) {
+			n = (p[1] > ecc_size ? ecc_size : p[1]);
+			memcpy(ecc, spare + p[0], n);
+			ecc_size -= n;
+			ecc += n;
+			p += 2;
+		}
+	}
+
+	// unload tag
+	if (ts) {
+		p = dev->attr->data_layout;
+		while (*p != 0xFF && tag_size > 0) {
+			n = (p[1] > tag_size ? tag_size : p[1]);
+			memcpy(p_tag, spare + p[0], n);
+			tag_size -= n;
+			p_tag += n;
+			p += 2;
+		}
+	}
+}
+
+/**
+ * Read tag from page spare
+ *
+ * \param[in] dev uffs device
+ * \param[in] block flash block num
+ * \param[in] page flash page num
+ * \param[out] tag tag to be filled
+ *
+ * \return	#UFFS_FLASH_NO_ERR: success and/or has no flip bits.
+ *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+ *			#UFFS_FLASH_ECC_FAIL: spare data has flip bits and ecc correct failed.
+ *			#UFFS_FLASH_ECC_OK: spare data has flip bits and corrected by ecc.
+*/
+int uffs_FlashReadPageTag(uffs_Device *dev,
+							int block, int page, uffs_Tags *tag)
+{
+	uffs_FlashOps *ops = dev->ops;
+	u8 * spare_buf;
+	int ret = UFFS_FLASH_UNKNOWN_ERR;
+	int tmp_ret;
+	UBOOL is_bad = U_FALSE;
+
+	spare_buf = (u8 *) uffs_PoolGet(SPOOL(dev));
+	if (spare_buf == NULL)
+		goto ext;
+
+	if (ops->ReadPageWithLayout) {
+		ret = ops->ReadPageWithLayout(dev, block, page, NULL, 0, NULL, tag ? &tag->s : NULL, NULL);
+		if (tag)
+			tag->seal_byte = (ret == UFFS_FLASH_NOT_SEALED ? 0xFF : 0);
+	}
+	else {
+		ret = ops->ReadPage(dev, block, page, NULL, 0, NULL,
+									spare_buf, dev->mem.spare_data_size);
+
+		if (tag) {
+			tag->seal_byte = SEAL_BYTE(dev, spare_buf);
+
+			if (!UFFS_FLASH_HAVE_ERR(ret))
+				uffs_FlashUnloadSpare(dev, spare_buf, &tag->s, NULL);
+		}
+	}
+
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		is_bad = U_TRUE;
+
+	if (UFFS_FLASH_HAVE_ERR(ret))
+		goto ext;
+
+	if (tag) {
+		if (!TAG_IS_SEALED(tag))	// not sealed ? don't try tag ECC correction
+			goto ext;
+
+		if (!TAG_IS_VALID(tag)) {
+			if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
+				/*
+				 * There could be a special case if:
+				 *  a) tag is sealed (so we are here), and
+				 *  b) s.valid == 1 and this bit is a 'bad' bit, and
+				 *  c) after tag ECC (corrected by tag ECC) s.valid == 0.
+				 *
+				 * So we need to try tag ECC (don't treat it as bad block if ECC failed)
+				 */
+
+				struct uffs_TagStoreSt s;
+
+				memcpy(&s, &tag->s, sizeof(s));
+				tmp_ret = TagEccCorrect(&s);
+
+				if (tmp_ret <= 0 || !TAG_IS_VALID(tag))	// can not corrected by ECC.
+					goto ext;
+			}
+			else {
+				goto ext;
+			}				
+		}
+
+		// do tag ecc correction
+		if (dev->attr->ecc_opt != UFFS_ECC_NONE) {
+			ret = TagEccCorrect(&tag->s);
+			ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
+					(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));
+
+			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+				is_bad = U_TRUE;
+
+			if (UFFS_FLASH_HAVE_ERR(ret))
+				goto ext;
+		}
+	}
+
+ext:
+	if (is_bad) {
+		uffs_BadBlockAdd(dev, block);
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"A new bad block (%d) is detected.", block);
+	}
+
+	if (spare_buf)
+		uffs_PoolPut(SPOOL(dev), spare_buf);
+
+	return ret;
+}
+
+/**
+ * Read page data to buf (do ECC error correction if needed)
+ * \param[in] dev uffs device
+ * \param[in] block flash block num
+ * \param[in] page flash page num of the block
+ * \param[out] buf holding the read out data
+ * \param[in] skip_ecc skip ecc when reading data from flash
+ *
+ * \return	#UFFS_FLASH_NO_ERR: success and/or has no flip bits.
+ *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+ *			#UFFS_FLASH_ECC_FAIL: spare data has flip bits and ecc correct failed.
+ *			#UFFS_FLASH_ECC_OK: spare data has flip bits and corrected by ecc.
+ *			#UFFS_FLASH_CRC_ERR: CRC verification failed.
+ *			#UFFS_FLASH_UNKNOWN_ERR:
+ *
+ * \note if skip_ecc is U_TRUE, skip CRC as well.
+ */
+int uffs_FlashReadPage(uffs_Device *dev, int block, int page, uffs_Buf *buf, UBOOL skip_ecc)
+{
+	uffs_FlashOps *ops = dev->ops;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	int size = dev->com.pg_size;
+	u8 ecc_buf[UFFS_MAX_ECC_SIZE];
+	u8 ecc_store[UFFS_MAX_ECC_SIZE];
+	UBOOL is_bad = U_FALSE;
+#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
+	UBOOL crc_ok = U_TRUE;
+#endif
+	u8 * spare;
+
+	int ret = UFFS_FLASH_UNKNOWN_ERR;
+
+	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
+	if (spare == NULL)
+		goto ext;
+
+	if (ops->ReadPageWithLayout) {
+		if (skip_ecc)
+			ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, NULL, NULL, NULL);
+		else
+			ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, ecc_buf, NULL, ecc_store);
+	}
+	else {
+		if (skip_ecc)
+			ret = ops->ReadPage(dev, block, page, buf->header, size, NULL, NULL, 0);
+		else
+			ret = ops->ReadPage(dev, block, page, buf->header, size, ecc_buf, spare, dev->mem.spare_data_size);
+	}
+
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		is_bad = U_TRUE;
+
+	if (UFFS_FLASH_HAVE_ERR(ret))
+		goto ext;
+
+#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
+	if (!skip_ecc) {
+		crc_ok = (HEADER(buf)->crc == uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt)) ? U_TRUE : U_FALSE);
+
+		if (crc_ok)
+			goto ext;	// CRC is matched, no need to do ECC correction.
+		else {
+			if (dev->attr->ecc_opt == UFFS_ECC_NONE || dev->attr->ecc_opt == UFFS_ECC_HW_AUTO) {
+				// ECC is not enabled or ecc correction already done, error return immediately,
+				// otherwise, we try CRC check again after ecc correction.
+				ret = UFFS_FLASH_CRC_ERR;
+				goto ext;
+			}
+		}
+	}
+#endif
+
+	// make ECC for UFFS_ECC_SOFT
+	if (attr->ecc_opt == UFFS_ECC_SOFT && !skip_ecc)
+		uffs_EccMake(buf->header, size, ecc_buf);
+
+	// unload ecc_store if driver doesn't do the layout
+	if (ops->ReadPageWithLayout == NULL) {
+		if (!skip_ecc && (attr->ecc_opt == UFFS_ECC_SOFT || attr->ecc_opt == UFFS_ECC_HW))
+			uffs_FlashUnloadSpare(dev, spare, NULL, ecc_store);
+	}
+
+	// check page data ecc
+	if (!skip_ecc && (dev->attr->ecc_opt == UFFS_ECC_SOFT || dev->attr->ecc_opt == UFFS_ECC_HW)) {
+
+		ret = uffs_EccCorrect(buf->header, size, ecc_store, ecc_buf);
+		ret = (ret < 0 ? UFFS_FLASH_ECC_FAIL :
+				(ret > 0 ? UFFS_FLASH_ECC_OK : UFFS_FLASH_NO_ERR));
+
+		if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+			is_bad = U_TRUE;
+
+		if (UFFS_FLASH_HAVE_ERR(ret))
+			goto ext;
+	}
+
+#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
+	if (!skip_ecc && !UFFS_FLASH_HAVE_ERR(ret)) {
+		// Everything seems ok, do CRC check again.
+		if (HEADER(buf)->crc == uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt))) {
+			ret = UFFS_FLASH_CRC_ERR;
+			goto ext;
+		}
+	}
+#endif
+
+ext:
+	switch(ret) {
+		case UFFS_FLASH_IO_ERR:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d I/O error", block, page);
+			break;
+		case UFFS_FLASH_ECC_FAIL:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d ECC failed", block, page);
+			ret = UFFS_FLASH_BAD_BLK;	// treat ECC FAIL as BAD BLOCK
+			is_bad = U_TRUE;
+			break;
+		case UFFS_FLASH_ECC_OK:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d bit flip corrected by ECC", block, page);
+			break;
+		case UFFS_FLASH_BAD_BLK:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d BAD BLOCK found", block, page);
+			break;
+		case UFFS_FLASH_UNKNOWN_ERR:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d UNKNOWN error!", block, page);
+			break;
+		case UFFS_FLASH_CRC_ERR:
+			uffs_Perror(UFFS_MSG_NORMAL, "Read block %d page %d CRC failed", block, page);
+			break;
+		default:
+			break;
+	}
+
+	if (is_bad)
+		uffs_BadBlockAdd(dev, block);
+
+	if (spare)
+		uffs_PoolPut(SPOOL(dev), spare);
+
+
+	return ret;
+}
+
+/**
+ * make spare from tag and ecc
+ *
+ * \param[in] dev uffs dev
+ * \param[in] ts uffs tag store, NULL if don't pack tag store
+ * \param[in] ecc ecc of data, NULL if don't pack ecc
+ * \param[out] spare output buffer
+ * \note spare buffer size: dev->mem.spare_data_size,
+ *		 all unpacked bytes will be inited 0xFF
+ */
+void uffs_FlashMakeSpare(uffs_Device *dev,
+						 const uffs_TagStore *ts, const u8 *ecc, u8* spare)
+{
+	u8 *p_ts = (u8 *)ts;
+	int ts_size = TAG_STORE_SIZE;
+	int ecc_size = ECC_SIZE(dev);
+	int n;
+	const u8 *p;
+
+	if (!uffs_Assert(spare != NULL, "invalid param"))
+		return;
+
+	memset(spare, 0xFF, dev->mem.spare_data_size);	// initialize as 0xFF.
+	SEAL_BYTE(dev, spare) = 0;						// set seal byte = 0.
+
+	// load ecc
+	p = dev->attr->ecc_layout;
+	if (p && ecc) {
+		while (*p != 0xFF && ecc_size > 0) {
+			n = (p[1] > ecc_size ? ecc_size : p[1]);
+			memcpy(spare + p[0], ecc, n);
+			ecc_size -= n;
+			ecc += n;
+			p += 2;
+		}
+	}
+
+	p = dev->attr->data_layout;
+	while (*p != 0xFF && ts_size > 0) {
+		n = (p[1] > ts_size ? ts_size : p[1]);
+		memcpy(spare + p[0], p_ts, n);
+		ts_size -= n;
+		p_ts += n;
+		p += 2;
+	}
+
+	uffs_Assert(SEAL_BYTE(dev, spare) == 0, "Make spare fail!");
+}
+
+/**
+ * write the whole page, include data and tag
+ *
+ * \param[in] dev uffs device
+ * \param[in] block
+ * \param[in] page
+ * \param[in] buf contains data to be wrote
+ * \param[in] tag tag to be wrote
+ *
+ * \return	#UFFS_FLASH_NO_ERR: success.
+ *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+ *			#UFFS_FLASH_BAD_BLK: a new bad block detected.
+ */
+int uffs_FlashWritePageCombine(uffs_Device *dev,
+							   int block, int page,
+							   uffs_Buf *buf, uffs_Tags *tag)
+{
+	uffs_FlashOps *ops = dev->ops;
+	int size = dev->com.pg_size;
+	u8 ecc_buf[UFFS_MAX_ECC_SIZE];
+	u8 *ecc = NULL;
+	u8 *spare;
+	struct uffs_MiniHeaderSt *header;
+	int ret = UFFS_FLASH_UNKNOWN_ERR;
+	UBOOL is_bad = U_FALSE;
+	uffs_Buf *verify_buf;
+#ifdef CONFIG_PAGE_WRITE_VERIFY
+	uffs_Tags chk_tag;
+#endif
+	
+	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
+	if (spare == NULL)
+		goto ext;
+
+	// setup header
+	header = HEADER(buf);
+	memset(header, 0xFF, sizeof(struct uffs_MiniHeaderSt));
+	header->status = 0;
+#ifdef CONFIG_ENABLE_PAGE_DATA_CRC
+	header->crc = uffs_crc16sum(buf->data, size - sizeof(struct uffs_MiniHeaderSt));
+#endif
+
+	// setup tag
+	TAG_DIRTY_BIT(tag) = TAG_DIRTY;		//!< set dirty bit
+	TAG_VALID_BIT(tag) = TAG_VALID;		//!< set valid bit
+	SEAL_TAG(tag);						//!< seal tag (the real seal byte will be set in uffs_FlashMakeSpare())
+
+	if (dev->attr->ecc_opt != UFFS_ECC_NONE)
+		TagMakeEcc(&tag->s);
+	else
+		tag->s.tag_ecc = TAG_ECC_DEFAULT;
+	
+	if (dev->attr->ecc_opt == UFFS_ECC_SOFT) {
+		uffs_EccMake(buf->header, size, ecc_buf);
+		ecc = ecc_buf;
+	}
+	else if (dev->attr->ecc_opt == UFFS_ECC_HW) {
+		ecc = ecc_buf;
+	}
+
+	if (ops->WritePageWithLayout) {
+		ret = ops->WritePageWithLayout(dev, block, page,
+							buf->header, size, ecc, &tag->s);
+	}
+	else {
+
+		if (!uffs_Assert(!(dev->attr->layout_opt == UFFS_LAYOUT_FLASH ||
+					dev->attr->ecc_opt == UFFS_ECC_HW ||
+					dev->attr->ecc_opt == UFFS_ECC_HW_AUTO), "WritePageWithLayout() not implemented ?")) {
+			ret = UFFS_FLASH_IO_ERR;
+			goto ext;
+		}
+
+		uffs_FlashMakeSpare(dev, &tag->s, ecc, spare);
+
+		ret = ops->WritePage(dev, block, page, buf->header, size, spare, dev->mem.spare_data_size);
+
+	}
+	
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		is_bad = U_TRUE;
+
+	if (UFFS_FLASH_HAVE_ERR(ret))
+		goto ext;
+
+#ifdef CONFIG_PAGE_WRITE_VERIFY
+	verify_buf = uffs_BufClone(dev, NULL);
+	if (verify_buf) {
+		ret = uffs_FlashReadPage(dev, block, page, verify_buf, U_FALSE);
+		if (!UFFS_FLASH_HAVE_ERR(ret)) {
+			if (memcmp(buf->header, verify_buf->header, size) != 0) {
+				uffs_Perror(UFFS_MSG_NORMAL,
+							"Page write verify failed (block %d page %d)",
+							block, page);
+				ret = UFFS_FLASH_BAD_BLK;
+			}
+		}
+
+		uffs_BufFreeClone(dev, verify_buf);
+	}
+	else {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Insufficient buf, clone buf failed.");
+	}
+
+	ret = uffs_FlashReadPageTag(dev, block, page, &chk_tag);
+	if (UFFS_FLASH_HAVE_ERR(ret))
+		goto ext;
+	
+	if (memcmp(&tag->s, &chk_tag.s, sizeof(uffs_TagStore)) != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL, "Page tag write verify failed (block %d page %d)",
+					block, page);
+		ret = UFFS_FLASH_BAD_BLK;
+	}
+
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		is_bad = U_TRUE;
+	
+#endif
+ext:
+	if (is_bad)
+		uffs_BadBlockAdd(dev, block);
+
+	if (spare)
+		uffs_PoolPut(SPOOL(dev), spare);
+
+	return ret;
+}
+
+/**
+ * mark a clean page tag as 'dirty' and 'invalid'.
+ *
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \param[in] page
+ *
+ * \return	#UFFS_FLASH_NO_ERR: success.
+ *			#UFFS_FLASH_IO_ERR: I/O error, expect retry ?
+ *			#UFFS_FLASH_BAD_BLK: a new bad block detected.
+ */
+int uffs_FlashMarkDirtyPage(uffs_Device *dev, uffs_BlockInfo *bc, int page)
+{
+	u8 *spare;
+	uffs_FlashOps *ops = dev->ops;
+	UBOOL is_bad = U_FALSE;
+	int ret = UFFS_FLASH_UNKNOWN_ERR;
+	int block = bc->block;
+	uffs_Tags *tag = GET_TAG(bc, page);
+	struct uffs_TagStoreSt *ts = &tag->s;
+
+	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
+	if (spare == NULL)
+		goto ext;
+
+	memset(ts, 0xFF, sizeof(struct uffs_TagStoreSt));
+	ts->dirty = TAG_DIRTY;  // set only 'dirty' bit, leave 'valid' bit to 1 (invalid).
+	
+	if (dev->attr->ecc_opt != UFFS_ECC_NONE)
+		TagMakeEcc(ts);
+
+	if (ops->WritePageWithLayout) {
+		ret = ops->WritePageWithLayout(dev, block, page, NULL, 0, NULL, ts);
+	}
+	else {
+		uffs_FlashMakeSpare(dev, ts, NULL, spare);
+		ret = ops->WritePage(dev, block, page, NULL, 0, spare, dev->mem.spare_data_size);
+	}
+
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		is_bad = U_TRUE;
+
+ext:
+	if (is_bad)
+		uffs_BadBlockAdd(dev, block);
+
+	if (spare)
+		uffs_PoolPut(SPOOL(dev), spare);
+
+	return ret;
+}
+
+/** Mark this block as bad block */
+URET uffs_FlashMarkBadBlock(uffs_Device *dev, int block)
+{
+	int ret;
+	uffs_BlockInfo *bc;
+
+	uffs_Perror(UFFS_MSG_NORMAL, "Mark bad block: %d", block);
+
+	bc = uffs_BlockInfoGet(dev, block);
+	if (bc) {
+		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);	// expire this block, just in case it's been cached before
+		uffs_BlockInfoPut(dev, bc);
+	}
+
+	if (dev->ops->MarkBadBlock)
+		return dev->ops->MarkBadBlock(dev, block) == 0 ? U_SUCC : U_FAIL;
+
+#ifdef CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+	ret = dev->ops->EraseBlock(dev, block);
+	if (ret != UFFS_FLASH_IO_ERR) {
+		// note: even EraseBlock return UFFS_FLASH_BAD_BLK,
+		//			we still process it ... not recommended for most NAND flash.
+#endif
+	if (dev->ops->WritePageWithLayout)
+		ret = dev->ops->WritePageWithLayout(dev, block, 0, NULL, 0, NULL, NULL);
+	else
+		ret = dev->ops->WritePage(dev, block, 0, NULL, 0, NULL, 0);
+
+#ifdef CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+	}
+#endif
+
+	return ret == UFFS_FLASH_NO_ERR ? U_SUCC : U_FAIL;
+}
+
+/** Is this block a bad block ? */
+UBOOL uffs_FlashIsBadBlock(uffs_Device *dev, int block)
+{
+	struct uffs_FlashOpsSt *ops = dev->ops;
+	UBOOL ret = U_FALSE;
+
+	if (ops->IsBadBlock) {
+		/* if flash driver provide 'IsBadBlock' function, call it */
+		ret = (ops->IsBadBlock(dev, block) == 0 ? U_FALSE : U_TRUE);
+	}
+	else {
+		/* otherwise we call ReadPage[WithLayout]() to get bad block status byte */
+		/* check the first page */
+		if (ops->ReadPageWithLayout) {
+			ret = (ops->ReadPageWithLayout(dev, block, 0, NULL, 0, NULL, NULL, NULL)
+												== UFFS_FLASH_BAD_BLK ? U_TRUE : U_FALSE);
+		}
+		else {
+			ret = (ops->ReadPage(dev, block, 0, NULL, 0, NULL, NULL, 0)
+												== UFFS_FLASH_BAD_BLK ? U_TRUE : U_FALSE);
+		}
+
+		if (ret == U_FALSE) {
+			/* check the second page */
+			if (ops->ReadPageWithLayout) {
+				ret = (ops->ReadPageWithLayout(dev, block, 0, NULL, 0, NULL, NULL, NULL) 
+													== UFFS_FLASH_BAD_BLK ? U_TRUE : U_FALSE);
+			}
+			else {
+				ret = (ops->ReadPage(dev, block, 0, NULL, 0, NULL, NULL, 0)
+													== UFFS_FLASH_BAD_BLK ? U_TRUE : U_FALSE);
+			}
+		}
+	}
+
+	//uffs_Perror(UFFS_MSG_NOISY, "Block %d is %s", block, ret ? "BAD" : "GOOD");
+
+	return ret;
+}
+
+/** Erase flash block */
+URET uffs_FlashEraseBlock(uffs_Device *dev, int block)
+{
+	int ret;
+	uffs_BlockInfo *bc;
+
+	ret = dev->ops->EraseBlock(dev, block);
+
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		uffs_BadBlockAdd(dev, block);
+
+	bc = uffs_BlockInfoGet(dev, block);
+	if (bc) {
+		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
+		uffs_BlockInfoPut(dev, bc);
+	}
+	return UFFS_FLASH_HAVE_ERR(ret) ? U_FAIL : U_SUCC;
+}
+
+/**
+ * Check the block by reading all pages.
+ *
+ * \return U_SUCC - all pages are clean,
+ *         U_FAIL - block is not clean
+ */
+URET uffs_FlashCheckErasedBlock(uffs_Device *dev, int block)
+{
+	u8 *spare = NULL;
+	uffs_FlashOps *ops = dev->ops;
+	int ret = U_SUCC;
+	int page;
+	int flash_ret;
+	u8 ecc_store[UFFS_MAX_ECC_SIZE];
+	uffs_TagStore ts;
+	uffs_Buf *buf = NULL;
+	int size = dev->com.pg_size;
+	int i;
+	u8 *p;
+	
+	spare = (u8 *) uffs_PoolGet(SPOOL(dev));
+	
+	if (spare == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't allocate spare buf.");
+		goto ext;
+	}
+	
+	buf = uffs_BufClone(dev, NULL);
+	
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't clone buf.");
+		goto ext;
+	}
+	
+	for (page = 0; page < dev->attr->pages_per_block; page++) {
+		if (ops->ReadPageWithLayout) {
+			
+			flash_ret = ops->ReadPageWithLayout(dev, block, page, buf->header, size, NULL, &ts, ecc_store);
+			
+			if (flash_ret != UFFS_FLASH_IO_ERR) {
+				// check page tag, should be all 0xFF
+				for (i = 0, p = (u8 *)(&ts); i < sizeof(ts); i++, p++) {
+					if (*p != 0xFF) {
+						ret = U_FAIL;
+						goto ext;
+					}
+				}
+				
+				// for hw or soft ecc, check stored ecc, should be all 0xFF
+				if (dev->attr->ecc_opt == UFFS_ECC_HW ||
+					dev->attr->ecc_opt == UFFS_ECC_SOFT)
+				{
+					for (i = 0, p = ecc_store; i < ECC_SIZE(dev); i++, p++) {
+						if (*p != 0xFF) {
+							ret = U_FAIL;
+							goto ext;
+						}
+					}
+				}
+			}
+		}
+		else {
+			
+			flash_ret = ops->ReadPage(dev, block, page, buf->header, size, NULL, spare, dev->attr->spare_size);
+			if (flash_ret != UFFS_FLASH_IO_ERR) {
+				// check spare data, should be all 0xFF
+				for (i = 0, p = spare; i < dev->attr->spare_size; i++, p++) {
+					if (*p != 0xFF) {
+						ret = U_FAIL;
+						goto ext;
+					}
+				}
+			}
+		}
+		
+		if (flash_ret != UFFS_FLASH_IO_ERR) {
+			// check page data, should be all 0xFF
+			for (i = 0, p = buf->header; i < size; i++, p++) {
+				if (*p != 0xFF) {
+					ret = U_FAIL;
+					goto ext;
+				}
+			}
+		}
+		
+	}
+	
+
+ext:
+	if (spare)
+		uffs_PoolPut(SPOOL(dev), spare);
+	
+	if (buf)
+		uffs_BufFreeClone(dev, buf);
+	
+	
+	return ret;
+}
+

+ 1944 - 0
src/uffs/uffs_fs.c

@@ -0,0 +1,1944 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_fs.c
+ * \brief basic file operations
+ * \author Ricky Zheng, created 12th May, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_pool.h"
+#include "uffs/uffs_ecc.h"
+#include "uffs/uffs_badblock.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_utils.h"
+#include <string.h> 
+#include <stdio.h>
+
+#define PFX "fs  : "
+
+#define GET_OBJ_NODE_SERIAL(obj) \
+	( \
+		(obj)->type == UFFS_TYPE_DIR ? \
+			(obj)->node->u.dir.serial :	(obj)->node->u.file.serial \
+	 )
+
+#define GET_OBJ_NODE_FATHER(obj) \
+	( \
+		(obj)->type == UFFS_TYPE_DIR ? \
+			(obj)->node->u.dir.parent :	(obj)->node->u.file.parent \
+	 )
+
+#define GET_SERIAL_FROM_OBJECT(obj) \
+			((obj)->node ? GET_OBJ_NODE_SERIAL(obj) : obj->serial)
+
+#define GET_FATHER_FROM_OBJECT(obj) \
+			((obj)->node ? GET_OBJ_NODE_FATHER(obj) : obj->parent)
+
+
+#define GET_BLOCK_FROM_NODE(obj) \
+	( \
+		(obj)->type == UFFS_TYPE_DIR ? \
+			(obj)->node->u.dir.block : (obj)->node->u.file.block \
+	 )
+
+typedef enum {
+	eDRY_RUN = 0,
+	eREAL_RUN,
+} RunOptionE;
+
+
+static void do_ReleaseObjectResource(uffs_Object *obj);
+static URET do_TruncateObject(uffs_Object *obj, u32 remain, RunOptionE run_opt);
+
+
+static int _object_data[(sizeof(struct uffs_ObjectSt) * MAX_OBJECT_HANDLE) / sizeof(int)];
+
+static uffs_Pool _object_pool;
+
+
+uffs_Pool * uffs_GetObjectPool(void)
+{
+	return &_object_pool;
+}
+
+/**
+ * initialise object buffers, called by UFFS internal
+ */
+URET uffs_InitObjectBuf(void)
+{
+	return uffs_PoolInit(&_object_pool, _object_data, sizeof(_object_data),
+			sizeof(uffs_Object), MAX_OBJECT_HANDLE);
+}
+
+/**
+ * Release object buffers, called by UFFS internal
+ */
+URET uffs_ReleaseObjectBuf(void)
+{
+	return uffs_PoolRelease(&_object_pool);
+}
+
+/**
+ * Get free object handlers
+ */
+int uffs_GetFreeObjectHandlers(void)
+{
+	int count = 0;
+
+	uffs_GlobalFsLockLock();
+	count = uffs_PoolGetFreeCount(&_object_pool);
+	uffs_GlobalFsLockUnlock();
+
+	return count;
+}
+
+/**
+ * Put all object which match dev
+ */
+int uffs_PutAllObjectBuf(uffs_Device *dev)
+{
+	int count = 0;
+	uffs_Object * obj = NULL;
+
+	do {
+		obj = (uffs_Object *) uffs_PoolFindNextAllocated(&_object_pool, (void *)obj);
+		if (obj && obj->dev && obj->dev->dev_num == dev->dev_num) {
+			uffs_PutObject(obj);
+			count++;
+		}
+	} while (obj);
+
+	return count;
+}
+
+/**
+ * alloc a new object structure
+ * \return the new object
+ */
+uffs_Object * uffs_GetObject(void)
+{
+	uffs_Object * obj;
+
+	obj = (uffs_Object *) uffs_PoolGet(&_object_pool);
+	if (obj) {
+		memset(obj, 0, sizeof(uffs_Object));
+		obj->attr_loaded = U_FALSE;
+		obj->open_succ = U_FALSE;
+	}
+
+	return obj;
+}
+
+/**
+ * re-initialize an object.
+ *
+ * \return U_SUCC or U_FAIL if the object is openned.
+ */
+URET uffs_ReInitObject(uffs_Object *obj)
+{
+	if (obj == NULL)
+		return U_FAIL;
+
+	if (obj->open_succ == U_TRUE)
+		return U_FAIL;	// can't re-init an openned object.
+
+	memset(obj, 0, sizeof(uffs_Object));
+	obj->attr_loaded = U_FALSE;
+	obj->open_succ = U_FALSE;
+
+	return U_SUCC;	
+}
+
+/**
+ * put the object struct back to system
+ */
+void uffs_PutObject(uffs_Object *obj)
+{
+	if (obj)
+		uffs_PoolPut(&_object_pool, obj);
+}
+
+/**
+ * \return the internal index num of object
+ */
+int uffs_GetObjectIndex(uffs_Object *obj)
+{
+	return uffs_PoolGetIndex(&_object_pool, obj);
+}
+
+/**
+ * \return the object by the internal index
+ */
+uffs_Object * uffs_GetObjectByIndex(int idx)
+{
+	return (uffs_Object *) uffs_PoolGetBufByIndex(&_object_pool, idx);
+}
+
+#ifdef CONFIG_PER_DEVICE_LOCK
+static void uffs_ObjectDevLock(uffs_Object *obj)
+{
+	if (obj) {
+		if (obj->dev) {
+			uffs_DeviceLock(obj->dev);
+			obj->dev_lock_count++;
+		}
+	}
+}
+
+static void uffs_ObjectDevUnLock(uffs_Object *obj)
+{
+	if (obj) {
+		if (obj->dev) {
+			obj->dev_lock_count--;
+			uffs_DeviceUnLock(obj->dev);
+		}
+	}
+} 
+#else
+#define uffs_ObjectDevLock(obj) do { } while (0)
+#define uffs_ObjectDevUnLock(obj) do { } while (0)
+
+#endif
+
+
+
+/**
+ * create a new object and open it if success
+ */
+URET uffs_CreateObject(uffs_Object *obj, const char *fullname, int oflag)
+{
+	URET ret = U_FAIL;
+
+	oflag |= UO_CREATE;
+
+	if (uffs_ParseObject(obj, fullname) == U_SUCC)
+		uffs_CreateObjectEx(obj, obj->dev, obj->parent,
+								obj->name, obj->name_len, oflag);
+
+	if (obj->err == UENOERR) {
+		ret = U_SUCC;
+	}
+	else {
+		if (obj->dev) {
+			uffs_PutDevice(obj->dev);
+			obj->dev = NULL;
+		}
+		ret = U_FAIL;
+	}
+
+	return ret;
+}
+
+
+
+/**
+ * return the dir length from a path.
+ * for example, path = "abc/def/xyz", return 8 ("abc/def/")
+ */
+static int GetDirLengthFromPath(const char *path, int path_len)
+{
+	const char *p = path;
+
+	if (path_len > 0) {
+		if (path[path_len - 1] == '/')
+			path_len--;		// skip the last '/'
+
+		p = path + path_len - 1;
+		while (p > path && *p != '/')
+			p--; 
+	}
+
+	return p - path;
+}
+
+/**
+ * Create an object under the given dir.
+ *
+ * \param[in|out] obj to be created, obj is returned from uffs_GetObject()
+ * \param[in] dev uffs device
+ * \param[in] dir object parent dir serial NO.
+ * \param[in] name point to the object name
+ * \param[in] name_len object name length
+ * \param[in] oflag open flag. UO_DIR should be passed for an dir object.
+ *
+ * \return U_SUCC or U_FAIL (error code in obj->err).
+ */
+URET uffs_CreateObjectEx(uffs_Object *obj, uffs_Device *dev, 
+						   int dir, const char *name, int name_len, int oflag)
+{
+	uffs_Buf *buf = NULL;
+	uffs_FileInfo fi;
+	TreeNode *node;
+
+	obj->dev = dev;
+	obj->parent = dir;
+	obj->type = (oflag & UO_DIR ? UFFS_TYPE_DIR : UFFS_TYPE_FILE);
+	obj->name = name;
+	obj->name_len = name_len;
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		if (name[obj->name_len - 1] == '/')		// get rid of ending '/' for dir
+			obj->name_len--;
+	}
+	else {
+		if (name[obj->name_len - 1] == '/') {	// file name can't end with '/'
+			obj->err = UENOENT;
+			goto ext;
+		}
+	}
+
+	if (obj->name_len == 0) {	// empty name ?
+		obj->err = UENOENT;
+		goto ext;
+	}
+
+	obj->sum = uffs_MakeSum16(obj->name, obj->name_len);
+
+	uffs_ObjectDevLock(obj);
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		//find out whether have file with the same name
+		node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
+											obj->name_len, obj->sum,
+											obj->parent);
+		if (node != NULL) {
+			obj->err = UEEXIST;	// we can't create a dir has the
+								// same name with exist file.
+			goto ext_1;
+		}
+		obj->node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
+												obj->name_len, obj->sum,
+												obj->parent);
+		if (obj->node != NULL) {
+			obj->err = UEEXIST; // we can't create a dir already exist.
+			goto ext_1;
+		}
+	}
+	else {
+		//find out whether have dir with the same name
+		node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
+											obj->name_len, obj->sum,
+											obj->parent);
+		if (node != NULL) {
+			obj->err = UEEXIST;
+			goto ext_1;
+		}
+		obj->node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
+												obj->name_len, obj->sum,
+												obj->parent);
+		if (obj->node) {
+			/* file already exist, truncate it to zero length */
+			obj->serial = GET_OBJ_NODE_SERIAL(obj);
+			obj->open_succ = U_TRUE; // set open_succ to U_TRUE before
+									 // call do_TruncateObject()
+			if (do_TruncateObject(obj, 0, eDRY_RUN) == U_SUCC)
+				do_TruncateObject(obj, 0, eREAL_RUN);
+			goto ext_1;
+		}
+	}
+
+	/* dir|file does not exist, create a new one */
+	obj->serial = uffs_FindFreeFsnSerial(obj->dev);
+	if (obj->serial == INVALID_UFFS_SERIAL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "No free serial num!");
+		obj->err = UENOMEM;
+		goto ext_1;
+	}
+
+	if (obj->dev->tree.erased_count < obj->dev->cfg.reserved_free_blocks) {
+		uffs_Perror(UFFS_MSG_NOISY,
+					"insufficient block in create obj");
+		obj->err = UENOMEM;
+		goto ext_1;
+	}
+
+	buf = uffs_BufNew(obj->dev, obj->type, obj->parent, obj->serial, 0);
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+						"Can't create new buffer when create obj!");
+		goto ext_1;
+	}
+
+	memset(&fi, 0, sizeof(uffs_FileInfo));
+	fi.name_len = obj->name_len < sizeof(fi.name) ? obj->name_len : sizeof(fi.name) - 1;
+	memcpy(fi.name, obj->name, fi.name_len);
+	fi.name[fi.name_len] = '\0';
+
+	fi.access = 0;
+	fi.attr |= FILE_ATTR_WRITE;
+
+	if (obj->type == UFFS_TYPE_DIR)
+		fi.attr |= FILE_ATTR_DIR;
+
+	fi.create_time = fi.last_modify = uffs_GetCurDateTime();
+
+	uffs_BufWrite(obj->dev, buf, &fi, 0, sizeof(uffs_FileInfo));
+	uffs_BufPut(obj->dev, buf);
+
+	// flush buffer immediately,
+	// so that the new node will be inserted into the tree
+	uffs_BufFlushGroup(obj->dev, obj->parent, obj->serial);
+
+	// update obj->node: after buf flushed,
+	// the NEW node can be found in the tree
+	if (obj->type == UFFS_TYPE_DIR)
+		obj->node = uffs_TreeFindDirNode(obj->dev, obj->serial);
+	else
+		obj->node = uffs_TreeFindFileNode(obj->dev, obj->serial);
+
+	if (obj->node == NULL) {
+		uffs_Perror(UFFS_MSG_NOISY, "Can't find the node in the tree ?");
+		obj->err = UEIOERR;
+		goto ext_1;
+	}
+
+	if (obj->type == UFFS_TYPE_FILE)
+		obj->node->u.file.len = 0;	//init the length to 0
+
+	if (HAVE_BADBLOCK(obj->dev))
+		uffs_BadBlockRecover(obj->dev);
+
+	obj->open_succ = U_TRUE;
+
+ext_1:
+	uffs_ObjectDevUnLock(obj);
+ext:
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+/**
+ * Open object under the given dir.
+ *
+ * \param[in|out] obj to be open, obj is returned from uffs_GetObject()
+ * \param[in] dev uffs device
+ * \param[in] dir object parent dir serial NO.
+ * \param[in] name point to the object name
+ * \param[in] name_len object name length
+ * \param[in] oflag open flag. UO_DIR should be passed for an dir object.
+ *
+ * \return U_SUCC or U_FAIL (error code in obj->err).
+ */
+URET uffs_OpenObjectEx(uffs_Object *obj, uffs_Device *dev, 
+					   int dir, const char *name, int name_len, int oflag)
+{
+
+	obj->err = UENOERR;
+	obj->open_succ = U_FALSE;
+
+	if (dev == NULL) {
+		obj->err = UEINVAL;
+		goto ext;
+	}
+
+	if ((oflag & (UO_WRONLY | UO_RDWR)) == (UO_WRONLY | UO_RDWR)) {
+		/* UO_WRONLY and UO_RDWR can't appear together */
+		uffs_Perror(UFFS_MSG_NOISY,
+					"UO_WRONLY and UO_RDWR can't appear together");
+		obj->err = UEINVAL;
+		goto ext;
+	}
+
+	obj->oflag = oflag;
+	obj->parent = dir;
+	obj->type = (oflag & UO_DIR ? UFFS_TYPE_DIR : UFFS_TYPE_FILE);
+	obj->pos = 0;
+	obj->dev = dev;
+	obj->name = name;
+	obj->name_len = name_len;
+
+	// adjust the name length
+	if (obj->type == UFFS_TYPE_DIR) {
+		if (obj->name_len > 0 && name[obj->name_len - 1] == '/')
+			obj->name_len--;	// truncate the ending '/' for dir
+	}
+
+	obj->sum = (obj->name_len > 0 ? uffs_MakeSum16(name, obj->name_len) : 0);
+	obj->head_pages = obj->dev->attr->pages_per_block - 1;
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		if (obj->name_len == 0) {
+			if (dir != PARENT_OF_ROOT) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "Bad parent for root dir!");
+				obj->err = UEINVAL;
+			}
+			else {
+				obj->serial = ROOT_DIR_SERIAL;
+			}
+			goto ext;
+		}
+	}
+	else {
+		if (obj->name_len == 0 || name[obj->name_len - 1] == '/') {
+			uffs_Perror(UFFS_MSG_SERIOUS, "Bad file name.");
+			obj->err = UEINVAL;
+		}
+	}
+
+
+	uffs_ObjectDevLock(obj);
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		obj->node = uffs_TreeFindDirNodeByName(obj->dev, obj->name,
+												obj->name_len, obj->sum,
+												obj->parent);
+	}
+	else {
+		obj->node = uffs_TreeFindFileNodeByName(obj->dev, obj->name,
+												obj->name_len, obj->sum,
+												obj->parent);
+	}
+
+	if (obj->node == NULL) {			// dir or file not exist
+		if (obj->oflag & UO_CREATE) {	// expect to create a new one
+			uffs_ObjectDevUnLock(obj);
+			if (obj->name == NULL || obj->name_len == 0)
+				obj->err = UEEXIST;
+			else
+				uffs_CreateObjectEx(obj, dev, dir, obj->name, obj->name_len, oflag);
+			goto ext;
+		}
+		else {
+			obj->err = UENOENT;
+			goto ext_1;
+		}
+	}
+
+	if ((obj->oflag & (UO_CREATE | UO_EXCL)) == (UO_CREATE | UO_EXCL)){
+		obj->err = UEEXIST;
+		goto ext_1;
+	}
+
+	obj->serial = GET_OBJ_NODE_SERIAL(obj);
+	obj->open_succ = U_TRUE;
+
+	if (obj->oflag & UO_TRUNC)
+		if (do_TruncateObject(obj, 0, eDRY_RUN) == U_SUCC) {
+			//NOTE: obj->err will be set in do_TruncateObject() if failed.
+			do_TruncateObject(obj, 0, eREAL_RUN);
+		}
+
+ext_1:
+	uffs_ObjectDevUnLock(obj);
+ext:
+	obj->open_succ = (obj->err == UENOERR ? U_TRUE : U_FALSE);
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+
+/**
+ * Parse the full path name, initialize obj.
+ *
+ * \param[out] obj object to be initialize.
+ * \param[in] name full path name.
+ *
+ * \return U_SUCC if the name is parsed correctly,
+ *			 U_FAIL if failed, and obj->err is set.
+ *
+ *	\note the following fields in obj will be initialized:
+ *			obj->dev
+ *			obj->parent
+ *			obj->name
+ *			obj->name_len
+ */
+URET uffs_ParseObject(uffs_Object *obj, const char *name)
+{
+	int len, m_len, d_len;
+	uffs_Device *dev;
+	const char *start, *p, *dname;
+	u16 dir;
+	TreeNode *node;
+	u16 sum;
+
+	if (uffs_ReInitObject(obj) == U_FAIL)
+		return U_FAIL;
+
+	len = strlen(name);
+	m_len = uffs_GetMatchedMountPointSize(name);
+	dev = uffs_GetDeviceFromMountPointEx(name, m_len);
+
+	if (dev) {
+		start = name + m_len;
+		d_len = GetDirLengthFromPath(start, len - m_len);
+		p = start;
+		obj->dev = dev;
+		if (m_len == len) {
+			obj->parent = PARENT_OF_ROOT;
+			obj->name = NULL;
+			obj->name_len = 0;
+		}
+		else {
+			dir = ROOT_DIR_SERIAL;
+			dname = start;
+			while (p - start < d_len) {
+				while (*p != '/') p++;
+				sum = uffs_MakeSum16(dname, p - dname);
+				node = uffs_TreeFindDirNodeByName(dev, dname, p - dname, sum, dir);
+				if (node == NULL) {
+					obj->err = UENOENT;
+					break;
+				}
+				else {
+					dir = node->u.dir.serial;
+					p++; // skip the '/'
+					dname = p;
+				}
+			}
+			obj->parent = dir;
+			obj->name = start + (d_len > 0 ? d_len + 1 : 0);
+			obj->name_len = len - (d_len > 0 ? d_len + 1 : 0) - m_len;
+		}
+
+		if (obj->err != UENOERR) {
+			uffs_PutDevice(obj->dev);
+			obj->dev = NULL;
+		}
+	}
+	else {
+		obj->err = UENOENT;
+	}
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+
+static void do_ReleaseObjectResource(uffs_Object *obj)
+{
+	if (obj) {
+		if (obj->dev) {
+			if (HAVE_BADBLOCK(obj->dev))
+				uffs_BadBlockRecover(obj->dev);
+			if (obj->dev_lock_count > 0) {
+				uffs_ObjectDevUnLock(obj);
+			}
+			uffs_PutDevice(obj->dev);
+			obj->dev = NULL;
+			obj->open_succ = U_FALSE;
+		}
+	}
+}
+
+/**
+ * Open a UFFS object
+ *
+ * \param[in|out] obj the object to be open
+ * \param[in] name the full name of the object
+ * \param[in] oflag open flag
+ *
+ * \return U_SUCC if object is opened successfully,
+ *			 U_FAIL if failed, error code will be set to obj->err.
+ */
+URET uffs_OpenObject(uffs_Object *obj, const char *name, int oflag)
+{
+	URET ret;
+
+	if (obj == NULL)
+		return U_FAIL;
+
+ 	if ((ret = uffs_ParseObject(obj, name)) == U_SUCC) {
+		ret = uffs_OpenObjectEx(obj, obj->dev, obj->parent,
+									obj->name, obj->name_len, oflag);
+ 	}
+ 	if (ret != U_SUCC)
+ 		do_ReleaseObjectResource(obj);
+
+	return ret;
+}
+
+static URET do_FlushObject(uffs_Object *obj)
+{
+	uffs_Device *dev;
+	URET ret = U_SUCC;
+	TreeNode *node = NULL;
+
+	dev = obj->dev;
+	if (obj->node) {
+		node = obj->node;
+		if (obj->type == UFFS_TYPE_DIR)
+			ret = uffs_BufFlushGroup(dev, obj->node->u.dir.parent,
+										obj->node->u.dir.serial);
+		else {
+			ret = (
+				uffs_BufFlushGroupMatchParent(dev, obj->node->u.file.serial) == U_SUCC &&
+				uffs_BufFlushGroup(dev, obj->node->u.file.parent, obj->node->u.file.serial) == U_SUCC
+				) ? U_SUCC : U_FAIL;
+		}
+		uffs_Assert(node == obj->node, "obj->node change!\n");
+	}
+
+	return ret;
+}
+
+/**
+ * Flush object data.
+ *
+ * \param[in] obj object to be flushed
+ * \return U_SUCC or U_FAIL (error code in obj->err).
+ */
+URET uffs_FlushObject(uffs_Object *obj)
+{
+	if(obj->dev == NULL || obj->open_succ != U_TRUE) {
+		obj->err = UEBADF;
+		goto ext;
+	}
+
+	uffs_ObjectDevLock(obj);
+
+	if (do_FlushObject(obj) != U_SUCC)
+		obj->err = UEIOERR;
+
+	uffs_ObjectDevUnLock(obj);
+
+ext:
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+/**
+ * Close an openned object.
+ *
+ * \param[in] obj object to be closed
+ * \return U_SUCC or U_FAIL (error code in obj->err).
+ */
+URET uffs_CloseObject(uffs_Object *obj)
+{
+#ifdef CONFIG_CHANGE_MODIFY_TIME
+	uffs_Device *dev;
+	uffs_Buf *buf;
+	uffs_FileInfo fi;
+#endif
+
+	if(obj->dev == NULL || obj->open_succ != U_TRUE) {
+		obj->err = UEBADF;
+		goto ext;
+	}
+
+
+	uffs_ObjectDevLock(obj);
+
+	if (obj->oflag & (UO_WRONLY|UO_RDWR|UO_APPEND|UO_CREATE|UO_TRUNC)) {
+
+#ifdef CONFIG_CHANGE_MODIFY_TIME
+        dev = obj->dev;
+		if (obj->node) {
+			//need to change the last modify time stamp
+			if (obj->type == UFFS_TYPE_DIR)
+				buf = uffs_BufGetEx(dev, UFFS_TYPE_DIR, obj->node, 0, obj->oflag);
+			else
+				buf = uffs_BufGetEx(dev, UFFS_TYPE_FILE, obj->node, 0, obj->oflag);
+
+			if(buf == NULL) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "can't get file header");
+				do_FlushObject(obj);
+				uffs_ObjectDevUnLock(obj);
+				goto ext;
+			}
+			uffs_BufRead(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
+			fi.last_modify = uffs_GetCurDateTime();
+			uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
+			uffs_BufPut(dev, buf);
+		}
+#endif
+		do_FlushObject(obj);
+	}
+
+	uffs_ObjectDevUnLock(obj);
+
+ext:
+	do_ReleaseObjectResource(obj);
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+static u16 GetFdnByOfs(uffs_Object *obj, u32 ofs)
+{
+	uffs_Device *dev = obj->dev;
+
+	if (ofs < (u32)(obj->head_pages * dev->com.pg_data_size)) {
+		return 0;
+	}
+	else {
+		ofs -= obj->head_pages * dev->com.pg_data_size;
+		return (ofs / (dev->com.pg_data_size * dev->attr->pages_per_block)) + 1;
+	}
+}
+
+
+static u32 GetStartOfDataBlock(uffs_Object *obj, u16 fdn)
+{
+	if (fdn == 0) {
+		return 0;
+	}
+	else {
+		return (obj->head_pages * obj->dev->com.pg_data_size) +
+					(fdn - 1) * (obj->dev->com.pg_data_size	*
+						obj->dev->attr->pages_per_block);
+	}
+}
+
+
+static int do_WriteNewBlock(uffs_Object *obj,
+						  const void *data, u32 len,
+						  u16 parent,
+						  u16 serial)
+{
+	uffs_Device *dev = obj->dev;
+	u16 page_id;
+	int wroteSize = 0;
+	int size;
+	uffs_Buf *buf;
+	URET ret;
+
+	for (page_id = 0; page_id < dev->attr->pages_per_block; page_id++) {
+		size = (len - wroteSize) > dev->com.pg_data_size ?
+					dev->com.pg_data_size : len - wroteSize;
+		if (size <= 0)
+			break;
+
+		buf = uffs_BufNew(dev, UFFS_TYPE_DATA, parent, serial, page_id);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "can't create a new page ?");
+			break;
+		}
+		// Note: if data == NULL, we will fill '\0'
+		ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, 0, size);
+		uffs_BufPut(dev, buf);
+
+		if (ret != U_SUCC) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "write data fail!");
+			break;
+		}
+		wroteSize += size;
+		obj->node->u.file.len += size;
+	}
+
+	return wroteSize;
+}
+
+static int do_WriteInternalBlock(uffs_Object *obj,
+							   TreeNode *node,
+							   u16 fdn,
+							   const void *data,
+							   u32 len,
+							   u32 blockOfs)
+{
+	uffs_Device *dev = obj->dev;
+	u16 maxPageID;
+	u16 page_id;
+	u32 size;
+	u32 pageOfs;
+	u32 wroteSize = 0;
+	URET ret;
+	uffs_Buf *buf;
+	u32 block_start;
+	u8 type;
+	u16 parent, serial;
+
+	block_start = GetStartOfDataBlock(obj, fdn);
+
+	if (fdn == 0) {
+		type = UFFS_TYPE_FILE;
+		parent = node->u.file.parent;
+		serial = node->u.file.serial;
+	}
+	else {
+		type = UFFS_TYPE_DATA;
+		parent = node->u.data.parent;
+		serial = fdn;
+	}
+
+	if (fdn == 0)
+		maxPageID = obj->head_pages;
+	else
+		maxPageID = dev->attr->pages_per_block - 1;
+
+
+	while (wroteSize < len) {
+		page_id = blockOfs / dev->com.pg_data_size;
+		if (fdn == 0)
+			page_id++; //in file header, page_id start from 1, not 0.
+		if (page_id > maxPageID) 
+			break;
+
+		pageOfs = blockOfs % dev->com.pg_data_size;
+		size = (len - wroteSize + pageOfs) > dev->com.pg_data_size ?
+					(dev->com.pg_data_size - pageOfs) : (len - wroteSize);
+
+		if ((obj->node->u.file.len % dev->com.pg_data_size) == 0 &&
+			(blockOfs + block_start) == obj->node->u.file.len) {
+
+			buf = uffs_BufNew(dev, type, parent, serial, page_id);
+
+			if(buf == NULL) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "can create a new buf!");
+				break;
+			}
+		}
+		else {
+			buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag);
+			if (buf == NULL) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer ?");
+				break;
+			}
+		}
+
+		// Note: if data == NULL, then we will fill '\0'
+		ret = uffs_BufWrite(dev, buf, data == NULL ? NULL : (u8 *)data + wroteSize, pageOfs, size);
+
+		uffs_BufPut(dev, buf);
+
+		if (ret == U_FAIL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "write inter data fail!");
+			break;
+		}
+
+		wroteSize += size;
+		blockOfs += size;
+
+		if (block_start + blockOfs > obj->node->u.file.len)
+			obj->node->u.file.len = block_start + blockOfs;
+
+	}
+
+	return wroteSize;
+}
+
+
+/**
+ * write data to obj, return remain data (0 if all data been written).
+ */
+static int do_WriteObject(uffs_Object *obj, const void *data, int len)
+{
+	uffs_Device *dev = obj->dev;
+	TreeNode *fnode = obj->node;
+	int remain = len;
+	u16 fdn;
+	u32 write_start;
+	TreeNode *dnode;
+	u32 size;
+
+	while (remain > 0) {
+		write_start = obj->pos + len - remain;
+		if (write_start > fnode->u.file.len) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "write point out of file ?");
+			break;
+		}
+
+		fdn = GetFdnByOfs(obj, write_start);
+
+		if (write_start == fnode->u.file.len && fdn > 0 &&
+			write_start == GetStartOfDataBlock(obj, fdn)) {
+			if (dev->tree.erased_count < dev->cfg.reserved_free_blocks) {
+				uffs_Perror(UFFS_MSG_NOISY, "insufficient block in write obj, new block");
+				break;
+			}
+			size = do_WriteNewBlock(obj, data ? (u8 *)data + len - remain : NULL,
+										remain, fnode->u.file.serial, fdn);
+
+			//
+			// Flush the new block buffers immediately, so that the new data node will be
+			// created and put in the tree.
+			//
+			// But before do that, we need to make sure the previous
+			// data block (if exist) been flushed first.
+			//
+			if (fdn > 1) {
+				uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn - 1);
+			}
+			else {
+				uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial);
+			}
+			// Now flush the new block.
+			uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn);
+
+			if (size == 0) 
+				break;
+
+			remain -= size;
+		}
+		else {
+
+			if(fdn == 0)
+				dnode = obj->node;
+			else
+				dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
+
+			if(dnode == NULL) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "can't find data node in tree ?");
+				obj->err = UEUNKNOWN_ERR;
+				break;
+			}
+			size = do_WriteInternalBlock(obj, dnode, fdn,
+									data ? (u8 *)data + len - remain : NULL, remain,
+									write_start - GetStartOfDataBlock(obj, fdn));
+#ifdef CONFIG_FLUSH_BUF_AFTER_WRITE
+			if (fdn == 0)
+				uffs_BufFlushGroup(dev, fnode->u.file.parent, fnode->u.file.serial);
+			else
+				uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn);
+#endif
+			if (size == 0)
+				break;
+
+			remain -= size;
+		}
+	}
+
+	uffs_Assert(fnode == obj->node, "obj->node change!\n");
+
+	return remain;
+}
+
+
+/**
+ * write data to obj, from obj->pos
+ *
+ * \param[in] obj file obj
+ * \param[in] data data pointer
+ * \param[in] len length of data to be write
+ *
+ * \return bytes wrote to obj
+ */
+int uffs_WriteObject(uffs_Object *obj, const void *data, int len)
+{
+	uffs_Device *dev = obj->dev;
+	TreeNode *fnode = NULL;
+	int remain;
+	u32 pos;
+	int wrote = 0;
+
+	if (obj == NULL) 
+		return 0;
+
+	if (obj->dev == NULL || obj->open_succ != U_TRUE) {
+		obj->err = UEBADF;
+		return 0;
+	}
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		uffs_Perror(UFFS_MSG_NOISY, "Can't write to an dir object!");
+		obj->err = UEACCES;
+		return 0;
+	}
+
+	if (obj->oflag == UO_RDONLY) {
+		obj->err = UEACCES;  // can't write to 'read only' mode opened file
+		return 0;
+	}
+
+	fnode = obj->node;
+
+	uffs_ObjectDevLock(obj);
+
+	if (obj->oflag & UO_APPEND)
+		obj->pos = fnode->u.file.len;
+	else {
+		if (obj->pos > fnode->u.file.len) {
+			// current pos pass over the end of file, need to fill the gap with '\0'
+			pos = obj->pos;	// save desired pos
+			obj->pos = fnode->u.file.len; // filling gap from the end of the file.
+			remain = do_WriteObject(obj, NULL, pos - fnode->u.file.len);  // Write filling bytes. Note: the filling data does not count as 'wrote' in this write operation.
+			obj->pos = pos - remain;
+			if (remain > 0)	// fail to fill the gap ? stop.
+				goto ext;
+		}
+	}
+
+	remain = do_WriteObject(obj, data, len);
+	wrote = len - remain;
+	obj->pos += wrote;
+
+ext:
+	if (HAVE_BADBLOCK(dev))
+		uffs_BadBlockRecover(dev);
+
+	uffs_ObjectDevUnLock(obj);
+
+	uffs_Assert(fnode == obj->node, "obj->node change!\n");
+
+	return wrote;
+}
+
+/**
+ * read data from obj
+ *
+ * \param[in] obj uffs object
+ * \param[out] data output data buffer
+ * \param[in] len required length of data to be read from object->pos
+ *
+ * \return return bytes of data have been read
+ */
+int uffs_ReadObject(uffs_Object *obj, void *data, int len)
+{
+	uffs_Device *dev = obj->dev;
+	TreeNode *fnode = NULL;
+	u32 remain = len;
+	u16 fdn;
+	u32 read_start;
+	TreeNode *dnode;
+	u32 size;
+	uffs_Buf *buf;
+	u32 blockOfs;
+	u16 page_id;
+	u8 type;
+	u32 pageOfs;
+
+	if (obj == NULL)
+		return 0;
+
+	fnode = obj->node;
+
+	if (obj->dev == NULL || obj->open_succ == U_FALSE) {
+		obj->err = UEBADF;
+		return 0;
+	}
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		uffs_Perror(UFFS_MSG_NOISY, "Can't read data from a dir object!");
+		obj->err = UEBADF;
+		return 0;
+	}
+
+	if (obj->pos > fnode->u.file.len) {
+		return 0; //can't read file out of range
+	}
+
+	if (obj->oflag & UO_WRONLY) {
+		obj->err = UEACCES;
+		return 0;
+	}
+
+	uffs_ObjectDevLock(obj);
+
+	while (remain > 0) {
+		read_start = obj->pos + len - remain;
+		if (read_start >= fnode->u.file.len) {
+			//uffs_Perror(UFFS_MSG_NOISY, "read point out of file ?");
+			break;
+		}
+
+		fdn = GetFdnByOfs(obj, read_start);
+		if (fdn == 0) {
+			dnode = obj->node;
+			type = UFFS_TYPE_FILE;
+		}
+		else {
+			type = UFFS_TYPE_DATA;
+			dnode = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
+			if (dnode == NULL) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "can't get data node in entry!");
+				obj->err = UEUNKNOWN_ERR;
+				break;
+			}
+		}
+
+		blockOfs = GetStartOfDataBlock(obj, fdn);
+		page_id = (read_start - blockOfs) / dev->com.pg_data_size;
+
+		if (fdn == 0) {
+			/**
+			 * fdn == 0: this means that the reading is start from the first block,
+			 * since the page 0 is for file attr, so we move to the next page ID.
+			 */
+			page_id++;
+		}
+
+		buf = uffs_BufGetEx(dev, type, dnode, (u16)page_id, obj->oflag);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "can't get buffer when read obj.");
+			obj->err = UEIOERR;
+			break;
+		}
+
+		pageOfs = read_start % dev->com.pg_data_size;
+		if (pageOfs >= buf->data_len) {
+			//uffs_Perror(UFFS_MSG_NOISY, "read data out of page range ?");
+			uffs_BufPut(dev, buf);
+			break;
+		}
+		size = (remain + pageOfs > buf->data_len ? buf->data_len - pageOfs : remain);
+
+		uffs_BufRead(dev, buf, (u8 *)data + len - remain, pageOfs, size);
+		uffs_BufPut(dev, buf);
+
+		remain -= size;
+	}
+
+	obj->pos += (len - remain);
+
+	if (HAVE_BADBLOCK(dev)) 
+		uffs_BadBlockRecover(dev);
+
+	uffs_ObjectDevUnLock(obj);
+
+	uffs_Assert(fnode == obj->node, "obj->node change!\n");
+
+	return len - remain;
+}
+
+/**
+ * move the file pointer
+ *
+ * \param[in] obj uffs object
+ * \param[in] offset offset from origin
+ * \param[in] origin the origin position, one of:
+ *				#USEEK_CUR, #USEEK_SET or #USEEK_END
+ *
+ * \return the new file pointer position if success,
+ *			or -1 if the new position would be negative.
+ */
+long uffs_SeekObject(uffs_Object *obj, long offset, int origin)
+{
+	if (obj->type == UFFS_TYPE_DIR) {
+		uffs_Perror(UFFS_MSG_NOISY, "Can't seek a dir object!");
+		obj->err = UEACCES;
+	}
+	else {
+		uffs_ObjectDevLock(obj);
+		switch (origin) {
+			case USEEK_CUR:
+				if ((long)obj->pos + offset < 0) {
+					obj->err = UEINVAL;
+				}
+				else {
+					obj->pos += offset;
+				}
+				break;
+			case USEEK_SET:
+				if (offset < 0) {
+					obj->err = UEINVAL;
+				}
+				else {
+					obj->pos = offset;
+				}
+				break;
+			case USEEK_END:
+				if ((long)obj->node->u.file.len + offset < 0) {
+					obj->err = UEINVAL;
+				}
+				else {
+					obj->pos = obj->node->u.file.len + offset;
+				}
+				break;
+		}
+		uffs_ObjectDevUnLock(obj);
+	}
+
+	return (obj->err == UENOERR ? (long)obj->pos : -1);
+}
+
+/**
+ * get current file pointer
+ *
+ * \param[in] obj uffs object
+ *
+ * \return return the file pointer position if the obj is valid,
+ *	 return -1 if obj is invalid.
+ */
+int uffs_GetCurOffset(uffs_Object *obj)
+{
+	if (obj) {
+		if (obj->dev && obj->open_succ == U_TRUE)
+			return obj->pos;
+	}
+	return -1;
+}
+
+/**
+ * check whether the file pointer is at the end of file
+ *
+ * \param[in] obj uffs object
+ *
+ * \return return 1 if file pointer is at the end of file,
+ *	 return -1 if error occur, else return 0.
+ */
+int uffs_EndOfFile(uffs_Object *obj)
+{
+	if (obj) {
+		if (obj->dev && obj->type == UFFS_TYPE_FILE && obj->open_succ == U_TRUE) {
+			if (obj->pos >= obj->node->u.file.len) {
+				return 1;
+			}
+			else {
+				return 0;
+			}
+		}
+	}
+
+	return -1;
+}
+
+//
+// To trancate the file, this is the last block to be trancated.
+// We need to discard one or more pages within this block, hence requires 'block recover'.
+//
+static URET do_TruncateInternalWithBlockRecover(uffs_Object *obj,
+												u16 fdn, u32 remain, RunOptionE run_opt)
+{
+	uffs_Device *dev = obj->dev;
+	TreeNode *fnode = obj->node;
+	u16 page_id, max_page_id;
+	TreeNode *node;
+	uffs_Buf *buf = NULL;
+	u8 type;
+	u32 block_start;
+	u16 parent, serial;
+	int slot;
+	uffs_BlockInfo *bc = NULL;
+	int block = -1;
+
+	if (fdn == 0) {
+		node = fnode;
+		type = UFFS_TYPE_FILE;
+		max_page_id = obj->head_pages;
+		block_start = 0;
+		parent = node->u.file.parent;
+		serial = node->u.file.serial;
+		block = node->u.file.block;
+	}
+	else {
+		node = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn);
+		if (node == NULL) {
+			obj->err = UEIOERR;
+			uffs_Perror(UFFS_MSG_SERIOUS,
+						"can't find data node when truncate obj");
+			goto ext;
+		}
+		block = node->u.data.block;
+		type = UFFS_TYPE_DATA;
+		max_page_id = dev->attr->pages_per_block - 1;
+		block_start = obj->head_pages * dev->com.pg_data_size +
+						(fdn - 1) * dev->com.pg_data_size *
+						dev->attr->pages_per_block;
+		parent = node->u.data.parent;
+		serial = node->u.data.serial;
+	}
+
+	if (run_opt == eDRY_RUN) {
+		// checking the buffer. this is the main reason why we need the 'dry run' mode.
+		for (page_id = 0; page_id <= max_page_id; page_id++) {
+			buf = uffs_BufFind(dev, parent, serial, page_id);
+			if (buf) {
+				//!< ok, the buffer was loaded before ...
+				if (uffs_BufIsFree(buf) == U_FALSE) {
+					obj->err = UEEXIST;
+					break;	//!< and someone is still holding the buffer,
+							//   can't truncate it !!!
+				}
+			}
+		}
+		buf = NULL;
+		goto ext;
+	}
+	
+	// find the last page *after* truncate
+	for (page_id = (fdn == 0 ? 1 : 0); page_id <= max_page_id; page_id++) {
+		if (block_start + (page_id + 1) * dev->com.pg_data_size >= remain)
+			break;
+	}
+
+	if (!uffs_Assert(page_id <= max_page_id, "fdn = %d, block_start = %d, remain = %d\n", fdn, block_start, remain)) {
+		obj->err = UEUNKNOWN_ERR;
+		goto ext;
+	}
+
+	// flush buffer before performing block recovery
+	uffs_BufFlushGroup(dev, parent, serial);
+
+	// load the last page
+	buf = uffs_BufGetEx(dev, type, node, page_id, obj->oflag);
+	if (buf == NULL) {
+		obj->err = UENOMEM;
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't get buf");
+		goto ext;
+	}
+
+	uffs_BufWrite(dev, buf, NULL, 0, 0); // just make this buf dirty
+
+	// lock the group
+	slot = uffs_BufFindGroupSlot(dev, parent, serial);
+	uffs_BufLockGroup(dev, slot);
+
+	if (remain == 0) // remain == 0: means discard all data in this block.
+		buf->data_len = 0;
+	else {
+		remain = (remain % dev->com.pg_data_size);
+		// remain == 0: means that we need to keep all data in this page.
+		buf->data_len = (remain == 0 ? dev->com.pg_data_size : remain);
+	}
+
+	/* mark this buf as UFFS_BUF_EXT_MARK_TRUNC_TAIL, when flushing
+		dirty buffers, UFFS will not do page recover for pages after
+		this buf page id (because this file is ended at this page) */
+	buf->ext_mark |= UFFS_BUF_EXT_MARK_TRUNC_TAIL;
+
+	uffs_BufPut(dev, buf);
+
+	// invalidate the rest page buf
+	page_id++;
+	for (; page_id <= max_page_id; page_id++) {
+		buf = uffs_BufFind(dev, parent, serial, page_id);
+		if (buf)
+			uffs_BufMarkEmpty(dev, buf);
+	}
+
+	// flush dirty buffer immediately, forcing block recovery.
+	uffs_BufFlushGroupEx(dev, parent, serial, U_TRUE);
+
+	// unlock the group
+	uffs_BufUnLockGroup(dev, slot);
+
+	// Invalidate block info cache for the 'old' block
+	bc = uffs_BlockInfoGet(dev, block);
+	if (bc) {
+		uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES);
+		uffs_BlockInfoPut(dev, bc);
+	}
+
+ext:
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+/**
+ * truncate an object
+ *
+ * \param[in] obj object to be truncated
+ * \param[in] remain data bytes to be remained in this object
+ *
+ * \return U_SUCC or U_FAIL (error code in obj->err)
+ */
+URET uffs_TruncateObject(uffs_Object *obj, u32 remain)
+{
+	uffs_ObjectDevLock(obj);
+	if (do_TruncateObject(obj, remain, eDRY_RUN) == U_SUCC)
+		do_TruncateObject(obj, remain, eREAL_RUN);
+	uffs_ObjectDevUnLock(obj);
+
+	uffs_FlushObject(obj);
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+/** truncate obj without lock device */
+static URET do_TruncateObject(uffs_Object *obj, u32 remain, RunOptionE run_opt)
+{
+	uffs_Device *dev = obj->dev;
+	TreeNode *fnode = obj->node;
+	u16 fdn;
+	u32 flen;
+	u32 block_start;
+	TreeNode *node;
+	uffs_BlockInfo *bc;
+	uffs_Buf *buf;
+	u16 page;
+	int pos;
+
+	pos = obj->pos;   // save current file position
+
+	if (obj->dev == NULL || obj->open_succ == U_FALSE || fnode == NULL) {
+		obj->err = UEBADF;
+		goto ext;
+	}
+
+	/* can't truncate a dir */
+	/* TODO: delete files under dir ? */
+	if (obj->type == UFFS_TYPE_DIR) {
+		obj->err = UEEXIST;
+		goto ext;
+	}
+
+	if (remain >= fnode->u.file.len) {
+		goto ext;	//!< nothing to do ... 
+	}
+
+	flen = fnode->u.file.len;
+
+	if (flen < remain) {
+		// file is shorter than 'reamin', fill the gap with '\0'
+		if (run_opt == eREAL_RUN) {
+			obj->pos = flen;  // move file pointer to the end
+			if (do_WriteObject(obj, NULL, remain - flen) > 0) {	// fill '\0' ...
+				uffs_Perror(UFFS_MSG_SERIOUS, "Write object not finished. expect %d but only %d wrote.",
+												remain - flen, fnode->u.file.len - flen);
+				obj->err = UEIOERR;   // likely be an I/O error.
+			}
+			flen = obj->node->u.file.len;
+		}
+	}
+	else {
+		while (flen > remain) {
+			fdn = GetFdnByOfs(obj, flen - 1);
+
+			//uffs_BufFlushGroup(dev, obj->serial, fdn);	//!< flush the buffer
+
+			block_start = GetStartOfDataBlock(obj, fdn);
+			if (remain <= block_start && fdn > 0) {
+				node = uffs_TreeFindDataNode(dev, obj->serial, fdn);
+				if (node == NULL) {
+					uffs_Perror(UFFS_MSG_SERIOUS,
+								"can't find data node when trancate obj.");
+					obj->err = UEIOERR;
+					goto ext;
+				}
+				bc = uffs_BlockInfoGet(dev, node->u.data.block);
+				if (bc == NULL) {
+					uffs_Perror(UFFS_MSG_SERIOUS,
+								"can't get block info when trancate obj.");
+					obj->err = UEIOERR;
+					goto ext;
+				}
+
+				for (page = 0; page < dev->attr->pages_per_block; page++) {
+					buf = uffs_BufFind(dev, fnode->u.file.serial, fdn, page);
+					if (buf) {
+						//!< ok, the buffer was loaded before ...
+						if (uffs_BufIsFree(buf) == U_FALSE) {
+							uffs_BlockInfoPut(dev, bc);
+							goto ext;	//!< and someone is still holding the buffer,
+										//   can't truncate it !!!
+						}
+						else if (run_opt == eREAL_RUN)
+							uffs_BufMarkEmpty(dev, buf);	//!< discard the buffer
+					}
+				}
+
+				if (run_opt == eREAL_RUN) {
+					uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, node);
+					uffs_FlashEraseBlock(dev, bc->block);
+					node->u.list.block = bc->block;
+					if (HAVE_BADBLOCK(dev))
+						uffs_BadBlockProcess(dev, node);
+					else
+						uffs_TreeInsertToErasedListTail(dev, node);
+
+					fnode->u.file.len = block_start;
+				}
+
+				flen = block_start;
+				uffs_BlockInfoPut(dev, bc);
+			}
+			else {
+				if (do_TruncateInternalWithBlockRecover(obj, fdn,
+														remain, run_opt) == U_SUCC) {
+					if (run_opt == eREAL_RUN)
+						fnode->u.file.len = remain;
+					flen = remain;
+				}
+			}
+		}
+	}
+
+	if (HAVE_BADBLOCK(dev)) 
+		uffs_BadBlockRecover(dev);
+ext:
+	obj->pos = pos;  // keep file pointer offset not changed.
+
+	uffs_Assert(fnode == obj->node, "obj->node change!\n");
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+
+}
+
+/**
+ * \brief check if there are anyone holding buf of this obj.
+ *        If no one holding the buffers, expire the buffer.
+ * \return
+ *		0	: no one holding any buf of this obj
+ *		>0	: the ref_count of buf which refer to this obj.
+ */
+int _CheckObjBufRef(uffs_Object *obj)
+{
+	uffs_Device *dev = obj->dev;
+	uffs_Buf *buf;
+	TreeNode *node = obj->node;
+	u16 parent, serial, last_serial;
+
+	// check the DIR or FILE block
+	for (buf = uffs_BufFind(dev, obj->parent, obj->serial, UFFS_ALL_PAGES);
+		 buf != NULL;
+		 buf = uffs_BufFindFrom(dev, buf->next, obj->parent, obj->serial, UFFS_ALL_PAGES))
+	{
+		if (buf->ref_count > 0) {
+			// oops ...
+			uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count",
+				obj->parent, obj->serial, buf->ref_count);
+
+			return buf->ref_count;
+		}
+		else {
+			buf->mark = UFFS_BUF_EMPTY;
+		}
+	}
+
+	if (buf == NULL || buf->ref_count == 0) {
+		// check the DATA block
+		if (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0) {
+
+			parent = obj->serial;
+			last_serial = GetFdnByOfs(obj, node->u.file.len - 1);
+			for (serial = 1; serial <= last_serial; serial++) {
+
+				for (buf = uffs_BufFind(dev, parent, serial, UFFS_ALL_PAGES);
+					 buf != NULL;
+					 buf = uffs_BufFindFrom(dev, buf->next, parent, serial, UFFS_ALL_PAGES))
+				{
+					if (buf->ref_count != 0) {
+						// oops ...
+						uffs_Perror(UFFS_MSG_SERIOUS, "someone still hold buf parent = %d, serial = %d, ref_count",
+							parent, serial, buf->ref_count);
+
+						return buf->ref_count;
+					}
+					else {
+						buf->mark = UFFS_BUF_EMPTY;
+					}
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * \brief delete uffs object
+ *
+ * \param[in] name full name of object
+ * \param[out] err return error code
+ *
+ * \return U_SUCC if object is deleted successfully. 
+ *	return U_FAIL if error happen, error code is set to *err.
+ */
+URET uffs_DeleteObject(const char * name, int *err)
+{
+	uffs_Object *obj, *work;
+	TreeNode *node, *d_node;
+	uffs_Device *dev = NULL;
+	u16 block;
+	u16 serial, parent, last_serial;
+	UBOOL bad = U_FALSE;
+	URET ret = U_FAIL;
+
+	obj = uffs_GetObject();
+	if (obj == NULL) {
+		if (err)
+			*err = UEMFILE;
+		goto ext_unlock;
+	}
+
+	if (uffs_OpenObject(obj, name, UO_RDWR|UO_DIR) == U_FAIL) {
+		if (uffs_OpenObject(obj, name, UO_RDWR) == U_FAIL) {
+			if (err)
+				*err = UENOENT;
+			goto ext_unlock;
+		}
+	}
+
+	dev = obj->dev;
+
+	// working throught object pool see if the object is opened ...
+	uffs_ObjectDevLock(obj);
+	work = NULL;
+	while ((work = (uffs_Object *)uffs_PoolFindNextAllocated(&_object_pool, work)) != NULL) {
+		if (work != obj && 
+			work->dev &&
+			work->dev == obj->dev &&
+			work->node &&
+			work->node == obj->node) {
+			// this object is opened, can't delete it.
+			if (err)
+				*err = UEACCES;
+			goto ext_lock;
+		}
+	}
+
+	if (obj->type == UFFS_TYPE_DIR) {
+		// if the dir is not empty, can't delete it.
+		node = uffs_TreeFindDirNodeWithParent(dev, obj->serial);
+		if (node != NULL) {
+			if (err)
+				*err = UEACCES;
+			goto ext_lock;  //have sub dirs ?
+		}
+
+		node = uffs_TreeFindFileNodeWithParent(dev, obj->serial);
+		if (node != NULL) {
+			if (err)
+				*err = UEACCES;
+			goto ext_lock;  //have sub files ?
+		}
+	}
+
+	// before erase the block, we need to take care of the buffer ...
+	uffs_BufFlushAll(dev);
+
+	if (_CheckObjBufRef(obj) > 0) {
+		if (err)
+			*err = UEACCES;
+		goto ext_lock;
+	}
+
+	node = obj->node;
+
+	// ok, now we are safe to erase DIR/FILE block :-)
+	block = GET_BLOCK_FROM_NODE(obj);
+	parent = obj->serial;
+	last_serial = (obj->type == UFFS_TYPE_FILE && node->u.file.len > 0 ? GetFdnByOfs(obj, node->u.file.len - 1) : 0);
+
+	uffs_BreakFromEntry(dev, obj->type, node);
+	uffs_FlashEraseBlock(dev, block);
+	node->u.list.block = block;
+	node->u.list.u.serial = obj->serial;
+
+	// From now on, the object is gone physically,
+	// but we need to 'suspend' this node so that no one will re-use
+	// the serial number during deleting the reset part of object.
+
+	if (HAVE_BADBLOCK(dev)) {
+		uffs_BadBlockProcessSuspend(dev, node);
+		bad = U_TRUE;  // will be put into 'bad' list later
+	}
+	else {
+		uffs_TreeSuspendAdd(dev, node);
+		bad = U_FALSE;	// will be put into erased list later
+	}
+
+	// now erase DATA blocks
+	if (obj->type == UFFS_TYPE_FILE && last_serial > 0) {
+		for (serial = 1; serial <= last_serial; serial++) {
+
+			uffs_ObjectDevUnLock(obj);
+			; // yield CPU to improve responsive when deleting large file.
+			uffs_ObjectDevLock(obj);
+
+			d_node = uffs_TreeFindDataNode(dev, parent, serial);
+			if (uffs_Assert(d_node != NULL, "Can't find DATA node parent = %d, serial = %d\n", parent, serial)) {
+				uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, d_node);
+				block = d_node->u.data.block;
+				uffs_FlashEraseBlock(dev, block);
+				d_node->u.list.block = block;
+				if (HAVE_BADBLOCK(dev))
+					uffs_BadBlockProcess(dev, d_node);
+				else
+					uffs_TreeInsertToErasedListTail(dev, d_node);
+			}
+		}
+	}
+	
+	// now process the suspend node
+	uffs_TreeRemoveSuspendNode(dev, node);
+	if (bad)
+		uffs_TreeInsertToBadBlockList(dev, node);
+	else
+		uffs_TreeInsertToErasedListTail(dev, node);
+
+	ret = U_SUCC;
+
+ext_lock:
+	uffs_ObjectDevUnLock(obj);
+ext_unlock:
+	do_ReleaseObjectResource(obj);
+
+	uffs_PutObject(obj);
+
+	return ret;
+}
+
+/**
+ * Remove object under a new parent, change object name.
+ *
+ * \param[in|out] obj
+ * \param[in] new_parent new parent's serial number
+ * \param[in] new_name new name of the object.
+ *			 if new_name == NULL, keep the old name.
+ * \param[in] name_len new name length.
+ *
+ * \return U_SUCC or U_FAIL (obj->err for the reason)
+ */
+URET uffs_MoveObjectEx(uffs_Object *obj,
+					   int new_parent, const char *new_name, int name_len)
+{
+	uffs_Buf *buf;
+	uffs_FileInfo fi;
+	uffs_Device *dev = obj->dev;
+	TreeNode *node = obj->node;
+
+	if (dev == NULL || node == NULL || obj->open_succ != U_TRUE) {
+		obj->err = UEBADF;
+		goto ext;
+	}
+
+	uffs_ObjectDevLock(obj);
+
+	obj->parent = new_parent;
+
+	if (name_len > 0) {
+
+		buf = uffs_BufGetEx(dev, obj->type, node, 0, obj->oflag);
+		if (buf == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "can't get buf when rename!");
+			obj->err = UEIOERR;
+			goto ext_1;
+		}
+
+		memcpy(&fi, buf->data, sizeof(uffs_FileInfo));
+
+		if (new_name[name_len-1] == '/')
+			name_len--;
+
+		memcpy(fi.name, new_name, name_len);
+		fi.name[name_len] = 0;
+		fi.name_len = name_len;
+		fi.last_modify = uffs_GetCurDateTime();
+
+		buf->parent = new_parent;	// !! need to manually change the 'parent' !!
+		uffs_BufWrite(dev, buf, &fi, 0, sizeof(uffs_FileInfo));
+		uffs_BufPut(dev, buf);
+
+		// !! force a block recover so that all old tag will be expired !!
+		// This is important so we only need to check
+		// the first spare when mount UFFS :)
+		uffs_BufFlushGroupEx(dev, obj->parent, obj->serial, U_TRUE);
+
+		obj->name = new_name;
+		obj->name_len = name_len;
+		obj->sum = uffs_MakeSum16(fi.name, fi.name_len);
+	}
+
+	//update the check sum and new parent of tree node
+	if (obj->type == UFFS_TYPE_DIR) {
+		obj->node->u.dir.checksum = obj->sum;
+		obj->node->u.dir.parent = new_parent;
+	}
+	else {
+		obj->node->u.file.checksum = obj->sum;
+		obj->node->u.file.parent = new_parent;
+	}
+
+ext_1:
+	uffs_ObjectDevUnLock(obj);
+ext:
+
+	return (obj->err == UENOERR ? U_SUCC : U_FAIL);
+}
+
+/**
+ * \brief rename(move) file or dir.
+ * \return U_SUCC if success, otherwise return U_FAIL and set error code to *err.
+ * \note rename/move file between different mount point is not allowed.
+ */
+URET uffs_RenameObject(const char *old_name, const char *new_name, int *err)
+{
+	uffs_Object *obj = NULL, *new_obj = NULL;
+	URET ret = U_FAIL;
+	int oflag;
+
+	obj = uffs_GetObject();
+	new_obj = uffs_GetObject();
+
+	if (obj == NULL || new_obj == NULL) {
+		if (err) 
+			*err = UEINVAL;
+		goto ext;
+	}
+
+	oflag = UO_RDONLY;
+	if (uffs_OpenObject(new_obj, new_name, oflag) == U_SUCC) {
+		uffs_CloseObject(new_obj);
+		uffs_Perror(UFFS_MSG_NOISY, "new object already exist!");
+		if (err)
+			*err = UEEXIST;
+		goto ext;
+	}
+	oflag |= UO_DIR;
+	if (uffs_OpenObject(new_obj, new_name, oflag) == U_SUCC) {
+		uffs_CloseObject(new_obj);
+		uffs_Perror(UFFS_MSG_NOISY, "new object already exist!");
+		if (err)
+			*err = UEEXIST;
+		goto ext;
+	}
+
+	if (uffs_ParseObject(new_obj, new_name) != U_SUCC) {
+		uffs_Perror(UFFS_MSG_NOISY, "parse new name fail !");
+		if (err)
+			*err = UENOENT;
+		goto ext;
+	}
+
+	if (new_obj->name_len == 0) {
+		uffs_Perror(UFFS_MSG_NOISY, "invalid new name");
+		if (err)
+			*err = UEINVAL;
+		goto ext;
+	}
+
+	oflag = UO_RDONLY;
+	if (uffs_OpenObject(obj, old_name, oflag) != U_SUCC) {
+		oflag |= UO_DIR;
+		if (uffs_OpenObject(obj, old_name, oflag) != U_SUCC) {
+			uffs_Perror(UFFS_MSG_NOISY, "Can't open old object !");
+			if (err)
+				*err = UEACCES;
+			goto ext;
+		}
+	}
+
+	if (obj->dev != new_obj->dev) {
+		uffs_Perror(UFFS_MSG_NOISY,
+					"Can't move object between different mount point");
+		if (err)
+			*err = UEACCES;
+	}
+	else {
+		ret = uffs_MoveObjectEx(obj, new_obj->parent,
+									new_obj->name, new_obj->name_len);
+		if (ret == U_FAIL && err)
+			*err = obj->err;
+	}
+
+	uffs_CloseObject(obj);
+
+ext:
+	if (obj) uffs_PutObject(obj);
+	if (new_obj) {
+		do_ReleaseObjectResource(new_obj);
+		uffs_PutObject(new_obj);
+	}
+
+	return ret;
+}
+

+ 204 - 0
src/uffs/uffs_init.c

@@ -0,0 +1,204 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_init.c
+ * \brief initialize uffs file system device
+ * \author Ricky Zheng, created 12th May, 2005
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_tree.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_badblock.h"
+#include "uffs/uffs_utils.h"
+#include <string.h>
+
+#define PFX "init: "
+
+static URET uffs_InitDeviceConfig(uffs_Device *dev)
+{
+    if (dev->cfg.dirty_groups == 0)
+        dev->cfg.dirty_groups = MAX_DIRTY_BUF_GROUPS;
+
+	if (!uffs_Assert(dev->cfg.dirty_groups >= 1 && dev->cfg.dirty_groups <= MAX_DIRTY_BUF_GROUPS,
+						"invalid config: dirty_groups = %d\n", dev->cfg.dirty_groups))
+		return U_FAIL;
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0
+	dev->cfg.bc_caches = MAX_CACHED_BLOCK_INFO;
+	dev->cfg.page_buffers = MAX_PAGE_BUFFERS;
+	dev->cfg.dirty_pages = MAX_DIRTY_PAGES_IN_A_BLOCK;
+	dev->cfg.reserved_free_blocks = MINIMUN_ERASED_BLOCK;
+#else
+	if (dev->cfg.bc_caches == 0)
+		dev->cfg.bc_caches = MAX_CACHED_BLOCK_INFO;
+	if (dev->cfg.page_buffers == 0)
+		dev->cfg.page_buffers = MAX_PAGE_BUFFERS;
+	if (dev->cfg.dirty_pages == 0)
+		dev->cfg.dirty_pages = MAX_DIRTY_PAGES_IN_A_BLOCK;
+	if (dev->cfg.reserved_free_blocks == 0)
+		dev->cfg.reserved_free_blocks = MINIMUN_ERASED_BLOCK;
+
+	if (!uffs_Assert(dev->cfg.page_buffers - CLONE_BUFFERS_THRESHOLD >= 3, "invalid config: page_buffers = %d\n", dev->cfg.page_buffers))
+		return U_FAIL;
+
+#endif
+	return U_SUCC;
+}
+
+URET uffs_InitDevice(uffs_Device *dev)
+{
+	URET ret;
+
+	ret = uffs_InitDeviceConfig(dev);
+	if (ret != U_SUCC)
+		return U_FAIL;
+
+	if (dev->mem.init) {
+		if (dev->mem.init(dev) != U_SUCC) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "Init memory allocator fail.");
+			return U_FAIL;
+		}
+	}
+
+	memset(&(dev->st), 0, sizeof(uffs_FlashStat));
+
+	uffs_DeviceInitLock(dev);
+	uffs_BadBlockInit(dev);
+
+
+	if (uffs_FlashInterfaceInit(dev) != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "Can't initialize flash interface !");
+		goto fail;
+	}
+
+	uffs_Perror(UFFS_MSG_NOISY, "init page buf");
+	ret = uffs_BufInit(dev, dev->cfg.page_buffers, dev->cfg.dirty_pages);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_DEAD, "Initialize page buffers fail");
+		goto fail;
+	}
+	uffs_Perror(UFFS_MSG_NOISY, "init block info cache");
+	ret = uffs_BlockInfoInitCache(dev, dev->cfg.bc_caches);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_DEAD, "Initialize block info fail");
+		goto fail;
+	}
+
+	ret = uffs_TreeInit(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "fail to init tree buffers");
+		goto fail;
+	}
+
+	ret = uffs_BuildTree(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "fail to build tree");
+		goto fail;
+	}
+
+	return U_SUCC;
+
+fail:
+	uffs_DeviceReleaseLock(dev);
+
+	return U_FAIL;
+}
+
+URET uffs_ReleaseDevice(uffs_Device *dev)
+{
+	URET ret;
+
+	ret = uffs_BlockInfoReleaseCache(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS,  "fail to release block info.");
+		goto ext;
+	}
+
+	ret = uffs_BufReleaseAll(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS,  "fail to release page buffers");
+		goto ext;
+	}
+
+	ret = uffs_TreeRelease(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "fail to release tree buffers!");
+		goto ext;
+	}
+
+	ret = uffs_FlashInterfaceRelease(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "fail to release tree buffers!");
+		goto ext;
+	}
+
+	if (dev->mem.release)
+		ret = dev->mem.release(dev);
+
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "fail to release memory allocator!");
+	}
+
+	uffs_DeviceReleaseLock(dev);
+
+ext:
+	return ret;
+
+}
+
+URET uffs_InitFileSystemObjects(void)
+{
+	if (uffs_InitObjectBuf() == U_SUCC) {
+		if (uffs_DirEntryBufInit() == U_SUCC) {
+			uffs_InitGlobalFsLock();
+			return U_SUCC;
+		}
+	}
+
+	return U_FAIL;
+}
+
+URET uffs_ReleaseFileSystemObjects(void)
+{
+	if (uffs_ReleaseObjectBuf() == U_SUCC) {
+		if (uffs_DirEntryBufRelease() == U_SUCC) {
+			uffs_ReleaseGlobalFsLock();
+			return U_SUCC;
+		}
+	}
+
+	return U_FAIL;
+}

+ 92 - 0
src/uffs/uffs_mem.c

@@ -0,0 +1,92 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_mem.c
+ * \brief uffs static memory allocator
+ * \author Ricky Zheng, created 23th Feb, 2007
+ */
+
+#include <string.h>
+#include "uffs_config.h"
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_mem.h"
+
+#define PFX "mem : "
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0
+static void * static_malloc(struct uffs_DeviceSt *dev, unsigned int size)
+{
+	struct uffs_memAllocatorSt *mem = &dev->mem;
+	void *p = NULL;
+
+	size += (size % sizeof(long) ? sizeof(long) - (size % sizeof(long)) : 0);
+
+	if (mem->buf_size - mem->pos < (int)size) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"Memory alloc failed! (alloc %d, free %d)",
+					size, mem->buf_size - mem->pos);
+	}
+	else {
+		p = mem->buf_start + mem->pos;
+		mem->pos += size;
+		uffs_Perror(UFFS_MSG_NOISY,
+					"0x%p: Allocated %d, free %d",
+					p, size, mem->buf_size - mem->pos);
+	}
+
+	return p;
+}
+
+void uffs_MemSetupStaticAllocator(uffs_MemAllocator *allocator,
+								  void *pool, int size)
+{
+	allocator->buf_start = (char *)pool;
+	allocator->buf_size = size;
+	allocator->pos = 0;
+	allocator->malloc = static_malloc;
+	allocator->free = NULL;  //never free memory for static memory allocator
+
+	uffs_Perror(UFFS_MSG_NOISY,
+				"System static memory: %d bytes", allocator->buf_size);
+	
+}
+
+#endif
+
+
+
+
+
+

+ 366 - 0
src/uffs/uffs_mtb.c

@@ -0,0 +1,366 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_mtb.c
+ * \brief mount table operations
+ * \author Ricky Zheng, created 11th July, 2009
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_tree.h"
+#include "uffs/uffs_mtb.h"
+#include "uffs/uffs_fd.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_fs.h"
+#include <string.h>
+
+#define PFX "mtb : "
+
+static struct uffs_MountTableEntrySt *m_head = NULL;		// list of mounted entries
+static struct uffs_MountTableEntrySt *m_free_head = NULL;	// list of unmounted entries
+
+/** Return mounted entries header */
+uffs_MountTable * uffs_MtbGetMounted(void)
+{
+	return m_head;
+}
+
+/** Return unmounted entries header */
+uffs_MountTable * uffs_MtbGetUnMounted(void)
+{
+	return m_free_head;
+}
+
+/**
+ * \brief Register mount table
+ * \param mtb mount table entry
+ * \return 0 succ
+ *         -1 failed (e.g. already registered and mounted)
+ */
+int uffs_RegisterMountTable(uffs_MountTable *mtb)
+{
+	uffs_MountTable *work = NULL;
+	static int dev_num = 0;
+
+	if (mtb == NULL) 
+		return -1;
+
+	for (work = m_head; work; work = work->next) {
+		if (work == mtb)
+			return -1; // already mounted ?
+	}
+
+	for (work = m_free_head; work; work = work->next) {
+		if (work == mtb)
+			return 0; // already registered.
+	}
+
+	/* replace the free head */
+	if (m_free_head)
+		m_free_head->prev = mtb;
+	mtb->prev = NULL;
+	mtb->next = m_free_head;
+	m_free_head = mtb;
+	
+	mtb->dev->dev_num = ++dev_num;
+
+	return 0;
+}
+
+/**
+ * \brief Remove mount entry from the table
+ * \param mtb mount table entry
+ * \return 0 removed succ
+ *         -1 entry in used or not in the 'unmounted' list
+ */
+int uffs_UnRegisterMountTable(uffs_MountTable *mtb)
+{
+	uffs_MountTable *work = NULL;
+
+	if (mtb == NULL)
+		return -1;
+
+	for (work = m_head; work; work = work->next) {
+		if (work == mtb)
+			return -1;	// in the mounted list ? busy, return
+	}
+
+	for (work = m_free_head; work; work = work->next) {
+		if (work == mtb) {
+			// found, remove it from the list
+			if (work->next)
+				work->next->prev = work->prev;
+			if (work->prev)
+				work->prev->next = work->next;
+			if (work == m_free_head)
+				m_free_head = work->next;
+
+			break;
+		}
+	}
+
+	return work ? 0 : -1;
+}
+
+static uffs_MountTable * uffs_GetMountTableByMountPoint(const char *mount, uffs_MountTable *head)
+{
+	uffs_MountTable *work = NULL;
+
+	for (work = head; work; work = work->next) {
+		if (strcmp(work->mount, mount) == 0)
+			break;
+	}	
+	return work;
+}
+
+/**
+ * \brief mount partition
+ * \param[in] mount partition mount point
+ * \return 0 succ
+ *         <0 fail
+ *
+ * \note use uffs_RegisterMountTable() register mount entry before you can mount it.
+ *       mount point should ended with '/', e.g. '/sys/'
+ */
+int uffs_Mount(const char *mount)
+{
+	uffs_MountTable *mtb;
+
+	if (uffs_GetMountTableByMountPoint(mount, m_head) != NULL) {
+		uffs_Perror(UFFS_MSG_NOISY,	"'%s' already mounted", mount);
+		return -1; // already mounted ?
+	}
+	
+	mtb = uffs_GetMountTableByMountPoint(mount, m_free_head);
+	if (mtb == NULL) {
+		uffs_Perror(UFFS_MSG_NOISY,	"'%s' not registered", mount);
+		return -1;	// not registered ?
+	}
+
+	uffs_Perror(UFFS_MSG_NOISY,
+				"init device for mount point %s ...",
+				mtb->mount);
+
+	mtb->dev->par.start = mtb->start_block;
+	if (mtb->end_block < 0) {
+		mtb->dev->par.end = 
+			mtb->dev->attr->total_blocks + mtb->end_block;
+	}
+	else {
+		mtb->dev->par.end = mtb->end_block;
+	}
+
+	if (mtb->dev->Init(mtb->dev) == U_FAIL) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"init device for mount point %s fail",
+					mtb->mount);
+		return -1;
+	}
+
+	uffs_Perror(UFFS_MSG_NOISY, "mount partiton: %d,%d",
+		mtb->dev->par.start, mtb->dev->par.end);
+
+	if (uffs_InitDevice(mtb->dev) != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "init device fail !");
+		return -1;
+	}
+
+	/* now break it from unmounted list */
+	if (mtb->prev)
+		mtb->prev->next = mtb->next;
+	if (mtb->next)
+		mtb->next->prev = mtb->prev;
+	if (m_free_head == mtb)
+		m_free_head = mtb->next;
+
+	/* link to mounted list */
+	mtb->prev = NULL;
+	mtb->next = m_head;
+	if (m_head)
+		m_head->prev = mtb;
+	m_head = mtb;
+
+	return 0;
+}
+
+/**
+ * \brief unmount parttion
+ * \param[in] mount partition mount point
+ * \return 0 succ
+ *         <0 fail
+ */
+int uffs_UnMount(const char *mount)
+{
+	uffs_MountTable *mtb = uffs_GetMountTableByMountPoint(mount, m_head);
+
+	if (mtb == NULL) {
+		uffs_Perror(UFFS_MSG_NOISY,	"'%s' not mounted ?", mount);
+		return -1;  // not mounted ?
+	}
+
+	if (uffs_GetMountTableByMountPoint(mount, m_free_head) != NULL) {
+		uffs_Perror(UFFS_MSG_NOISY,	"'%s' already unmounted ?", mount);
+		return -1;  // already unmounted ?
+	}
+
+	if (mtb->dev->ref_count != 0) {
+		uffs_Perror(UFFS_MSG_NORMAL, "Can't unmount '%s' - busy", mount);
+		return -1;
+	}
+
+	if (uffs_ReleaseDevice(mtb->dev) == U_FAIL) {
+		uffs_Perror(UFFS_MSG_NORMAL, "Can't release device for mount point '%s'", mount);
+		return -1;
+	}
+
+	mtb->dev->Release(mtb->dev);
+
+	// break from mounted list
+	if (mtb->prev)
+		mtb->prev->next = mtb->next;
+	if (mtb->next)
+		mtb->next->prev = mtb->prev;
+	if (mtb == m_head)
+		m_head = mtb->next;
+
+	// put to unmounted list
+	mtb->prev = NULL;
+	mtb->next = m_free_head;
+	if (m_free_head)
+		m_free_head->prev = mtb;
+	m_free_head = mtb;
+
+	return 0;
+}
+
+/**
+ * find the matched mount point from a given full absolute path.
+ *
+ * \param[in] path full path
+ * \return the length of mount point.
+ */
+int uffs_GetMatchedMountPointSize(const char *path)
+{
+	int pos;
+	uffs_Device *dev;
+
+	if (path[0] != '/')
+		return 0;
+
+	pos = strlen(path);
+
+	while (pos > 0) {
+		if ((dev = uffs_GetDeviceFromMountPointEx(path, pos)) != NULL ) {
+			uffs_PutDevice(dev);
+			return pos;
+		}
+		else {
+			if (path[pos-1] == '/') 
+				pos--;
+			//back forward search the next '/'
+			for (; pos > 0 && path[pos-1] != '/'; pos--)
+				;
+		}
+	}
+
+	return pos;
+}
+
+/**
+ * get device from mount point.
+ *
+ * \param[in] mount mount point name.
+ * \return NULL if mount point is not found.
+ */
+uffs_Device * uffs_GetDeviceFromMountPoint(const char *mount)
+{
+	uffs_MountTable *mtb = uffs_GetMountTableByMountPoint(mount, m_head);
+
+	if (mtb) {
+		mtb->dev->ref_count++;
+		return mtb->dev;
+	}
+
+	return NULL;
+}
+
+/**
+ * get device from mount point.
+ *
+ * \param[in] mount mount point name.
+ * \param[in] len mount point name length.
+ * \return NULL if mount point is not found.
+ */
+uffs_Device * uffs_GetDeviceFromMountPointEx(const char *mount, int len)
+{
+	uffs_MountTable *work = NULL;
+
+	for (work = m_head; work; work = work->next) {
+		if (strlen(work->mount) == len &&
+				strncmp(mount, work->mount, len) == 0) {
+			work->dev->ref_count++;
+			return work->dev;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * return mount point from device
+ *
+ * \param[in] dev uffs device
+ * \return NULL if mount point is not found, 
+ *		otherwise return mount point name in mount table.
+ */
+const char * uffs_GetDeviceMountPoint(uffs_Device *dev)
+{
+	uffs_MountTable *work = NULL;
+
+	for (work = m_head; work; work = work->next) {
+		if (work->dev == dev) {
+			return work->mount;
+		}
+	}
+
+	return NULL;	
+}
+
+void uffs_PutDevice(uffs_Device *dev)
+{
+	dev->ref_count--;
+}
+
+

+ 386 - 0
src/uffs/uffs_pool.c

@@ -0,0 +1,386 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_pool.c
+ * \brief Fast fixed size memory pool management.
+ * \author Ricky Zheng, Simon Kallweit
+ */
+
+#include "uffs_config.h" 
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_pool.h"
+
+/*
+
+	usage:
+
+	#define BUF_SIZE	32
+	#define NUM_BUFS	1024
+
+	static int pool_mem[NUM_BUFS * BUF_SIZE / sizeof(int)];
+	static uffs_Pool pool;
+
+	uffs_PoolInit(&pool, pool_mem, sizeof(pool_mem), BUF_SIZE, NUM_BUFS);
+
+	void * p;
+	p = uffs_PoolGet(&pool);
+	...
+	uffs_PoolPut(p, &pool);
+
+	notice:
+
+	uffs_PoolInit will assert when NUM_BUFS is not at least 1, or BUF_SIZE is
+	not	aligned to the platforms pointer size.
+
+*/
+
+
+/**
+ * \brief Initializes the memory pool.
+ * \param[in] pool memory pool
+ * \param[in] mem pool memory
+ * \param[in] mem_size size of pool memory
+ * \param[in] buf_size size of a single buffer
+ * \param[in] num_bufs number of buffers
+ * \return Returns U_SUCC if successful.
+ */
+URET uffs_PoolInit(uffs_Pool *pool,
+				   void *mem, u32 mem_size, u32 buf_size, u32 num_bufs)
+{
+	unsigned int i;
+	uffs_PoolEntry *e1, *e2;
+
+	if (!uffs_Assert(pool != NULL, "pool missing") ||
+		!uffs_Assert(mem != NULL, "pool memory missing") ||
+		!uffs_Assert(num_bufs > 0, "not enough buffers") ||
+		!uffs_Assert(buf_size % sizeof(void *) == 0,
+					"buffer size not aligned to pointer size") ||
+		!uffs_Assert(mem_size == num_bufs * buf_size,
+					"pool memory size is wrong"))
+	{
+		return U_FAIL;
+	}
+
+	pool->mem = (u8 *)mem;
+	pool->buf_size = buf_size;
+	pool->num_bufs = num_bufs;
+
+	uffs_SemCreate(&pool->sem);	
+	uffs_SemWait(pool->sem);
+
+	// Initialize the free_list
+	e1 = e2 = pool->free_list = (uffs_PoolEntry *) pool->mem;
+	for (i = 1; i < pool->num_bufs; i++) {
+		e2 = (uffs_PoolEntry *) (pool->mem + i * pool->buf_size);
+		e1->next = e2;
+		e1 = e2;
+	}
+	e2->next = NULL;
+	
+	uffs_SemSignal(pool->sem);
+
+	return U_SUCC;
+}
+
+/**
+ * \brief verify pointer validity aganist memory pool
+ * \return U_TRUE if valid, U_FALSE if invalid.
+ */
+UBOOL uffs_PoolVerify(uffs_Pool *pool, void *p)
+{
+	return p &&
+		(u8 *)p >= pool->mem &&
+		(u8 *)p < pool->mem + (pool->buf_size * pool->num_bufs) &&
+		(((u8 *)p - pool->mem) % pool->buf_size) == 0 ? U_TRUE : U_FALSE;
+}
+
+/**
+ * \brief Releases the memory pool.
+ * \param[in] pool memory pool
+ * \return Returns U_SUCC if successful.
+ */
+URET uffs_PoolRelease(uffs_Pool *pool)
+{
+	if (!uffs_Assert(pool != NULL, "pool missing"))
+		return U_FAIL;
+	
+	uffs_SemDelete(&pool->sem);
+
+	return U_SUCC;
+}
+
+/**
+ * \brief Get a buffer from the memory pool.
+ * \param[in] pool memory pool
+ * \return Returns a pointer to the buffer or NULL if none is available.
+ */
+void *uffs_PoolGet(uffs_Pool *pool)
+{
+	uffs_PoolEntry *e;
+
+	if (!uffs_Assert(pool != NULL, "pool missing"))
+		return NULL;
+
+	e = pool->free_list;
+	if (e)
+		pool->free_list = e->next;
+
+	return e;
+}
+
+/**
+ * \brief Get a buffer from the memory pool.
+ * This version is locked and should be used when multiple threads access the
+ * same memory pool.
+ * \param[in] pool memory pool
+ * \return Returns a pointer to the buffer or NULL if none is available.
+ */
+void *uffs_PoolGetLocked(uffs_Pool *pool)
+{
+	uffs_PoolEntry *e;
+
+	if (!uffs_Assert(pool != NULL, "pool missing"))
+		return NULL;
+
+	uffs_SemWait(pool->sem);
+	e = pool->free_list;
+	if (e)
+		pool->free_list = e->next;
+	uffs_SemSignal(pool->sem);
+
+	return e;
+}
+
+/**
+ * \brief Puts a buffer back to the memory pool.
+ * \param[in] pool memory pool
+ * \param[in] p buffer to put back
+ * \return Returns 0 if successful.
+ */
+int uffs_PoolPut(uffs_Pool *pool, void *p)
+{
+	uffs_PoolEntry *e = (uffs_PoolEntry *)p;
+
+	if (!uffs_Assert(pool != NULL, "pool missing"))
+		return -1;
+
+	if (e) {
+		e->next = pool->free_list;
+		pool->free_list = e;
+		return 0;
+	}
+
+	return -1;
+}
+
+/**
+ * \brief Puts a buffer back to the memory pool.
+ * This version is locked and should be used when multiple threads access the
+ * same memory pool.
+ * \param[in] pool memory pool
+ * \param[in] p buffer to put back
+ * \return Returns 0 if successful.
+ */
+int uffs_PoolPutLocked(uffs_Pool *pool, void *p)
+{
+	uffs_PoolEntry *e = (uffs_PoolEntry *)p;
+
+	if (!uffs_Assert(pool != NULL, "pool missing"))
+		return -1;
+
+	if (e) {
+		uffs_SemWait(pool->sem);
+		e->next = pool->free_list;
+		pool->free_list = e;
+		uffs_SemSignal(pool->sem);
+		return 0;
+	}
+
+	return -1;
+}
+
+/**
+ * \brief Gets a buffer by index (offset).
+ * This method returns a buffer from the memory pool by index.
+ * \param[in] pool memory pool
+ * \param[in] index index
+ * \return Returns a pointer to the buffer.
+ */
+void *uffs_PoolGetBufByIndex(uffs_Pool *pool, u32 index)
+{
+	if (!uffs_Assert(pool != NULL, "pool missing") ||
+		!uffs_Assert(index < pool->num_bufs,
+				"index(%d) out of range(max %d)", index, pool->num_bufs))
+	{
+		return NULL;
+	}
+
+	return (u8 *) pool->mem + index * pool->buf_size;
+}
+
+/**
+ * \brief Gets the index (offset) of a buffer.
+ * This method returns the index of a buffer from the memory pool.
+ * \param[in] pool memory pool
+ * \param[in] p buffer to get index from
+ * \return Returns the index of the buffer.
+ */
+u32 uffs_PoolGetIndex(uffs_Pool *pool, void *p)
+{
+	if (!uffs_Assert(pool != NULL, "pool missing") ||
+		!uffs_Assert(p >= (void *) pool->mem &&
+			p < (void *) (pool->mem + pool->num_bufs * pool->buf_size),
+			"pointer out of range"))
+	{
+		uffs_Panic();
+	}
+
+	return ((u8 *) p - pool->mem) / pool->buf_size;
+}
+
+/**
+ * \brief Check given buffer in free list
+ * \return U_TRUE if it's in free list, U_FALSE if not.
+ */
+UBOOL uffs_PoolCheckFreeList(uffs_Pool *pool, void *p)
+{
+	uffs_PoolEntry *e;
+	for (e = pool->free_list; e; e = e->next) {
+		if ((void *)e == p)
+			return U_TRUE;
+	}
+	return U_FALSE;
+}
+
+/**
+ * \brief this is more efficient version for small nodes number memory pool (< 32)
+ */
+static void * FindNextAllocatedInSmallPool(uffs_Pool *pool, void *from)
+{
+	u32 map = 0;
+	uffs_PoolEntry *e;
+	u32 i;
+
+	for (e = pool->free_list; e; e = e->next)
+		map |= (1 << uffs_PoolGetIndex(pool, e));
+
+	for (i = uffs_PoolGetIndex(pool, from);
+			(map & (1 << i)) && i < 32 && i < pool->num_bufs;
+				i++);
+
+	return i < 32 && i < pool->num_bufs ?
+			uffs_PoolGetBufByIndex(pool, i) : NULL;
+}
+
+
+/**
+ * \brief Find next allocated memory block
+ *
+ * \param[in] pool memory pool
+ * \param[in] from search start address, if NULL, from pool->mem
+ *
+ * \return next allocated memory block, NULL if not found.
+ *
+ * \note This is NOT efficient, don't do it on a pool with large free nodes !
+ */
+void * uffs_PoolFindNextAllocated(uffs_Pool *pool, void *from)
+{
+	uffs_PoolEntry *e = NULL;
+	u8 *p = (u8 *)from;
+
+	if (p == NULL)
+		p = pool->mem;
+	else
+		p += pool->buf_size;
+
+	if (pool->num_bufs < 32)
+		return FindNextAllocatedInSmallPool(pool, p);
+
+	// work through the free list, stop if not in free list,
+	// otherwise move to next entry and search free list again.
+
+	if (pool->free_list) {
+		while (uffs_PoolVerify(pool, p)) {
+			e = pool->free_list;
+			while (e) {
+				if (p == (u8 *)e) {
+					p += pool->buf_size; // in free list, move to next entry
+					break;
+				}
+				e = e->next;
+			}
+			if (e == NULL)	// not in free_list, gotcha
+				break;
+		}	
+	}
+
+	return uffs_PoolVerify(pool, p) ? p : NULL ;
+}
+
+/**
+ * \brief get free memory block count
+ */
+int uffs_PoolGetFreeCount(uffs_Pool *pool)
+{
+	int count = 0;
+	uffs_PoolEntry *e;
+
+	e = pool->free_list;
+	while (e) {
+		count++;
+		e = e->next;
+	}
+
+	return count;
+}
+
+/**
+ * \brief put all memory block back, return how many memory blocks were put back
+ */
+int uffs_PoolPutAll(uffs_Pool *pool)
+{
+	void *p = NULL;
+	int count = 0;
+
+	do {
+		p = uffs_PoolFindNextAllocated(pool, p);
+		if (p) {
+			uffs_PoolPut(pool, p);
+			count++;
+		}
+	} while (p);
+
+	return count;
+}

+ 448 - 0
src/uffs/uffs_public.c

@@ -0,0 +1,448 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_public.c
+ * \brief public and miscellaneous functions
+ * \author Ricky Zheng, created 10th May, 2005
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_types.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_crc.h"
+
+#include <string.h>
+
+#define PFX "pub : "
+
+
+int uffs_GetFirstBlockTimeStamp(void)
+{
+	return 0;
+}
+
+int uffs_GetNextBlockTimeStamp(int prev)
+{
+	return (prev + 1) % 3;
+}
+
+UBOOL uffs_IsSrcNewerThanObj(int src, int obj)
+{
+	switch (src - obj) {
+	case 0:
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"the two block have the same time stamp ?");
+		break;
+	case 1:
+	case -2:
+		return U_TRUE;
+	case -1:
+	case 2:
+		return U_FALSE;
+	default:
+		uffs_Perror(UFFS_MSG_SERIOUS,  "time stamp out of range !");
+		break;
+	}
+
+	return U_FALSE;
+}
+
+
+/** 
+ * \brief given a page, search the block to find
+ *			a better page with the same page id
+ *
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \param[in] page page number to be compared with
+ *
+ * \return the better page number, could be the same with the given page.
+ *         if the given page does not have good tag, return UFFS_INVALID_PAGE.
+ */
+u16 uffs_FindBestPageInBlock(uffs_Device *dev, uffs_BlockInfo *bc, u16 page)
+{
+	int i;
+	uffs_Tags *tag, *tag_old;
+
+	if (!uffs_Assert(page != UFFS_INVALID_PAGE, "invalid param !"))
+		return page;	// just in case ...
+
+	uffs_BlockInfoLoad(dev, bc, page); // load old page
+	tag_old = GET_TAG(bc, page);
+
+	if (!uffs_Assert(TAG_IS_GOOD(tag_old), "try to find a invalid page ?"))
+		return UFFS_INVALID_PAGE;
+
+	if (page == dev->attr->pages_per_block - 1)	// already the last page ?
+		return page;
+
+	for (i = dev->attr->pages_per_block - 1; i > page; i--) {
+		uffs_BlockInfoLoad(dev, bc, i);
+		tag = GET_TAG(bc, i);
+		if (TAG_IS_GOOD(tag) &&
+			TAG_PAGE_ID(tag) == TAG_PAGE_ID(tag_old) &&
+			TAG_PARENT(tag) == TAG_PARENT(tag_old) &&
+			TAG_SERIAL(tag) == TAG_SERIAL(tag_old))
+		{
+			break;
+		}
+	}
+
+	return i;
+}
+
+/** 
+ * \brief find a valid page with given page_id
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \param[in] page_id page_id to be find
+ * \return the valid page number which has given page_id
+ * \retval >=0 page number
+ * \retval UFFS_INVALID_PAGE page not found
+ */
+u16 uffs_FindPageInBlockWithPageId(uffs_Device *dev,
+								   uffs_BlockInfo *bc, u16 page_id)
+{
+	u16 page;
+	uffs_Tags *tag;
+
+	//Indeed, the page which has page_id, should ahead of page_id ...
+	for (page = page_id; page < dev->attr->pages_per_block; page++) {
+		uffs_BlockInfoLoad(dev, bc, page);
+		tag = &(bc->spares[page].tag);
+		if (TAG_IS_GOOD(tag) && TAG_PAGE_ID(tag) == page_id)
+			return page;
+	}
+
+	return UFFS_INVALID_PAGE;
+}
+
+/** 
+ * Are all the pages in the block used ?
+ */
+UBOOL uffs_IsBlockPagesFullUsed(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	uffs_Tags *tag;
+
+	// if the last page is dirty, then the whole block is full
+	uffs_BlockInfoLoad(dev, bc, dev->attr->pages_per_block - 1);
+	tag = GET_TAG(bc, dev->attr->pages_per_block - 1);
+
+	return TAG_IS_GOOD(tag) ? U_TRUE : U_FALSE;
+}
+
+/** 
+ * Is this block used ?
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \retval U_TRUE block is used
+ * \retval U_FALSE block is free
+ */
+UBOOL uffs_IsThisBlockUsed(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	uffs_Tags *tag;
+
+	// if the first page is dirty, then this block is used.
+	uffs_BlockInfoLoad(dev, bc, 0);
+	tag = GET_TAG(bc, 0);
+
+	return TAG_IS_DIRTY(tag) ? U_TRUE : U_FALSE;
+}
+
+/** 
+ * get block time stamp from a exist block
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ */
+int uffs_GetBlockTimeStamp(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	if(uffs_IsThisBlockUsed(dev, bc) == U_FALSE) 
+		return uffs_GetFirstBlockTimeStamp();
+	else{
+		uffs_BlockInfoLoad(dev, bc, 0);
+		return TAG_BLOCK_TS(GET_TAG(bc, 0));
+	}
+
+}
+
+/** 
+ * find first free page from 'pageFrom'
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \param[in] pageFrom search from this page
+ * \return return first free page number from 'pageFrom'
+ * \retval UFFS_INVALID_PAGE no free page found
+ * \retval >=0 the first free page number
+ */
+u16 uffs_FindFirstFreePage(uffs_Device *dev,
+						   uffs_BlockInfo *bc, u16 pageFrom)
+{
+	u16 i;
+
+	for (i = pageFrom; i < dev->attr->pages_per_block; i++) {
+		uffs_BlockInfoLoad(dev, bc, i);
+		if (uffs_IsPageErased(dev, bc, i) == U_TRUE)
+			return i;
+	}
+
+	return UFFS_INVALID_PAGE; //free page not found
+}
+
+
+/** 
+ * calculate sum of data, 8bit version
+ * \param[in] p data pointer
+ * \param[in] len length of data
+ * \return return sum of data, 8bit
+ */
+u8 uffs_MakeSum8(const void *p, int len)
+{
+	return uffs_crc16sum(p, len) & 0xFF;
+}
+
+/** 
+ * calculate sum of datam, 16bit version
+ * \param[in] p data pointer
+ * \param[in] len length of data
+ * \return return sum of data, 16bit
+ */
+u16 uffs_MakeSum16(const void *p, int len)
+{
+	return uffs_crc16sum(p, len);
+}
+
+/** 
+ * create a new file on a free block
+ * \param[in] dev uffs device
+ * \param[in] parent parent dir serial num
+ * \param[in] serial serial num of this new file
+ * \param[in] bc block information
+ * \param[in] fi file information
+ * \note parent, serial, bc must be provided before,
+ *		 and all information in fi should be filled well before.
+ */
+URET uffs_CreateNewFile(uffs_Device *dev,
+						u16 parent, u16 serial,
+						uffs_BlockInfo *bc, uffs_FileInfo *fi)
+{
+	uffs_Tags *tag;
+	uffs_Buf *buf;
+
+	uffs_BlockInfoLoad(dev, bc, 0);
+
+	tag = GET_TAG(bc, 0);
+	TAG_PARENT(tag) = parent;
+	TAG_SERIAL(tag) = serial;
+	TAG_DATA_LEN(tag) = sizeof(uffs_FileInfo);
+
+	buf = uffs_BufGet(dev, parent, serial, 0);
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "get buf fail.");
+		return U_FAIL;
+	}
+
+	memcpy(buf->data, fi, TAG_DATA_LEN(tag));
+	buf->data_len = TAG_DATA_LEN(tag);
+
+	return uffs_BufPut(dev, buf);
+}
+
+
+/** 
+ * \brief calculate data length of a file block
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ */
+int uffs_GetBlockFileDataLength(uffs_Device *dev, uffs_BlockInfo *bc, u8 type)
+{
+	u16 page_id;
+	u16 i;
+	uffs_Tags *tag;
+	int size = 0;
+	u16 page;
+	u16 lastPage = dev->attr->pages_per_block - 1;
+
+	uffs_BlockInfoLoad(dev, bc, lastPage);
+	tag = GET_TAG(bc, lastPage);
+
+	if (TAG_IS_GOOD(tag)) {
+		// First try the last page.
+		// if it's the full loaded file/data block, then we have a quick path.
+		if (type == UFFS_TYPE_FILE) {
+			if (TAG_PAGE_ID(tag) == (lastPage - 1)) {
+				size = dev->com.pg_data_size * (dev->attr->pages_per_block - 2) + TAG_DATA_LEN(tag);
+				return size;
+			}
+		}
+		if (type == UFFS_TYPE_DATA) {
+			if (TAG_PAGE_ID(tag) == lastPage) {
+				size = dev->com.pg_data_size * (dev->attr->pages_per_block - 1) + TAG_DATA_LEN(tag);
+				return size;
+			}
+		}
+	}
+
+	// ok, it's not the full loaded file/data block,
+	// need to read all spares....
+	uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
+	tag = GET_TAG(bc, 0);
+	if (uffs_Assert(TAG_IS_GOOD(tag), "block %d page 0 does not have good tag ?", bc->block)) {
+		if (TAG_TYPE(tag) == UFFS_TYPE_FILE) {
+			page_id = 1;	//In file header block, file data page_id from 1
+			i = 1;			//search from page 1
+		}
+		else {
+			page_id = 0;	//in normal file data block, page_id from 0
+			i = 0;			//in normal file data block, search from page 0
+		}
+		for (; i < dev->attr->pages_per_block; i++) {
+			tag = GET_TAG(bc, i);
+			if (TAG_IS_GOOD(tag)) {
+				if (page_id == TAG_PAGE_ID(tag)) {
+					page = uffs_FindBestPageInBlock(dev, bc, i);
+					if (uffs_Assert(page != UFFS_INVALID_PAGE, "got an invalid page ?")) {
+						size += TAG_DATA_LEN(GET_TAG(bc, page));
+						page_id++;
+					}
+				}
+			}
+		}
+	}
+
+	return size;
+}
+
+/** 
+ * get free pages number
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ */
+int uffs_GetFreePagesCount(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	int count = 0;
+	int i;
+
+	// search from the last page ... to first page
+	for (i = dev->attr->pages_per_block - 1; i >= 0; i--) {
+		uffs_BlockInfoLoad(dev, bc, i);
+		if (uffs_IsPageErased(dev, bc, (u16)i) == U_TRUE) {
+			count++;
+		}
+		else {
+			if (TAG_IS_GOOD(GET_TAG(bc, i)))  // it won't be any free page if we see a good tag.
+				break;
+		}
+	}
+
+	return count;
+}
+/** 
+ * \brief Is the block erased ?
+ * \param[in] dev uffs device
+ * \param[in] bc block info
+ * \param[in] page page number to be check
+ * \retval U_TRUE block is erased, ready to use
+ * \retval U_FALSE block is dirty, maybe use by file
+ */
+UBOOL uffs_IsPageErased(uffs_Device *dev, uffs_BlockInfo *bc, u16 page)
+{
+	uffs_Tags *tag;
+
+	uffs_BlockInfoLoad(dev, bc, page);
+	tag = GET_TAG(bc, page);
+
+	if (!TAG_IS_SEALED(tag) &&
+		!TAG_IS_DIRTY(tag) &&
+		!TAG_IS_VALID(tag)) {
+		return U_TRUE;
+	}
+
+	return U_FALSE;
+}
+
+/** 
+ * get partition used (bytes)
+ */
+int uffs_GetDeviceUsed(uffs_Device *dev)
+{
+	return (dev->par.end - dev->par.start + 1 -
+			dev->tree.bad_count	- dev->tree.erased_count
+			) *
+				dev->attr->page_data_size *
+					dev->attr->pages_per_block;
+}
+
+/** 
+ * get partition free (bytes)
+ */
+int uffs_GetDeviceFree(uffs_Device *dev)
+{
+	return dev->tree.erased_count *
+			dev->attr->page_data_size *
+				dev->attr->pages_per_block;
+}
+
+/** 
+ * get partition total size (bytes)
+ */
+int uffs_GetDeviceTotal(uffs_Device *dev)
+{
+	return (dev->par.end - dev->par.start + 1) *
+				dev->attr->page_data_size *
+					dev->attr->pages_per_block;
+}
+
+/**
+ * load mini hader from flash
+ */
+URET uffs_LoadMiniHeader(uffs_Device *dev,
+						 int block, u16 page, struct uffs_MiniHeaderSt *header)
+{
+	int ret;
+	struct uffs_FlashOpsSt *ops = dev->ops;
+
+	if (ops->ReadPageWithLayout) {
+		ret = ops->ReadPageWithLayout(dev, block, page, (u8 *)header, 
+										sizeof(struct uffs_MiniHeaderSt), NULL, NULL, NULL);
+	}
+	else {
+		ret = ops->ReadPage(dev, block, page, (u8 *)header, sizeof(struct uffs_MiniHeaderSt), NULL, NULL, 0);
+	}
+
+	dev->st.page_header_read_count++;
+
+	return UFFS_FLASH_HAVE_ERR(ret) ? U_FAIL : U_SUCC;
+}
+

+ 1278 - 0
src/uffs/uffs_tree.c

@@ -0,0 +1,1278 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_tree.c
+ * \brief seting up uffs tree data structure
+ * \author Ricky Zheng, created 13th May, 2005
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_pool.h"
+#include "uffs/uffs_flash.h"
+#include "uffs/uffs_badblock.h"
+
+#include <string.h>
+
+#define TPOOL(dev) &(dev->mem.tree_pool)
+
+#define PFX "tree: "
+
+static void uffs_InsertToFileEntry(uffs_Device *dev, TreeNode *node);
+static void uffs_InsertToDirEntry(uffs_Device *dev, TreeNode *node);
+static void uffs_InsertToDataEntry(uffs_Device *dev, TreeNode *node);
+
+static TreeNode * uffs_TreeGetErasedNodeNoCheck(uffs_Device *dev);
+
+
+struct BlockTypeStatSt {
+	int dir;
+	int file;
+	int data;
+};
+
+/** 
+ * \brief initialize tree buffers
+ * \param[in] dev uffs device
+ */
+URET uffs_TreeInit(uffs_Device *dev)
+{
+	int size;
+	int num;
+	uffs_Pool *pool;
+	int i;
+
+	size = sizeof(TreeNode);
+	num = dev->par.end - dev->par.start + 1;
+	
+	pool = &(dev->mem.tree_pool);
+
+	if (dev->mem.tree_nodes_pool_size == 0) {
+		if (dev->mem.malloc) {
+			dev->mem.tree_nodes_pool_buf = dev->mem.malloc(dev, size * num);
+			if (dev->mem.tree_nodes_pool_buf)
+				dev->mem.tree_nodes_pool_size = size * num;
+		}
+	}
+	if (size * num > dev->mem.tree_nodes_pool_size) {
+		uffs_Perror(UFFS_MSG_DEAD,
+					"Tree buffer require %d but only %d available.",
+					size * num, dev->mem.tree_nodes_pool_size);
+		memset(pool, 0, sizeof(uffs_Pool));
+		return U_FAIL;
+	}
+	uffs_Perror(UFFS_MSG_NOISY, "alloc tree nodes %d bytes.", size * num);
+	
+	uffs_PoolInit(pool, dev->mem.tree_nodes_pool_buf,
+					dev->mem.tree_nodes_pool_size, size, num);
+
+	dev->tree.erased = NULL;
+	dev->tree.erased_tail = NULL;
+	dev->tree.erased_count = 0;
+	dev->tree.bad = NULL;
+	dev->tree.bad_count = 0;
+
+	for (i = 0; i < DIR_NODE_ENTRY_LEN; i++) {
+		dev->tree.dir_entry[i] = EMPTY_NODE;
+	}
+
+	for (i = 0; i < FILE_NODE_ENTRY_LEN; i++) {
+		dev->tree.file_entry[i] = EMPTY_NODE;
+	}
+
+	for (i = 0; i < DATA_NODE_ENTRY_LEN; i++) {
+		dev->tree.data_entry[i] = EMPTY_NODE;
+	}
+
+	dev->tree.max_serial = ROOT_DIR_SERIAL;
+	
+	return U_SUCC;
+}
+/** 
+ * \brief release tree buffers, call this function when unmount
+ * \param[in] dev uffs device
+ */
+URET uffs_TreeRelease(uffs_Device *dev)
+{
+	uffs_Pool *pool;
+	
+	pool = &(dev->mem.tree_pool);
+	if (pool->mem && dev->mem.free) {
+		dev->mem.free(dev, pool->mem);
+		pool->mem = NULL;
+		dev->mem.tree_nodes_pool_size = 0;
+	}
+	uffs_PoolRelease(pool);
+	memset(pool, 0, sizeof(uffs_Pool));
+
+	return U_SUCC;
+}
+
+static u16 _GetBlockFromNode(u8 type, TreeNode *node)
+{
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		return node->u.dir.block;
+	case UFFS_TYPE_FILE:
+		return node->u.file.block;
+	case UFFS_TYPE_DATA:
+		return node->u.data.block;
+	}
+	uffs_Perror(UFFS_MSG_SERIOUS, "unkown type, X-block");
+	return UFFS_INVALID_BLOCK;
+}
+
+#if 0
+static u16 _GetParentFromNode(u8 type, TreeNode *node)
+{
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		return node->u.dir.parent;
+	case UFFS_TYPE_FILE:
+		return node->u.file.parent;
+	case UFFS_TYPE_DATA:
+		return node->u.data.parent;
+	}
+	uffs_Perror(UFFS_MSG_SERIOUS, "unkown type, X-parent");
+	return INVALID_UFFS_SERIAL;
+}
+
+
+static u16 _GetSerialFromNode(u8 type, TreeNode *node)
+{
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		return node->u.dir.serial;
+	case UFFS_TYPE_FILE:
+		return node->u.file.serial;
+	case UFFS_TYPE_DATA:
+		return node->u.data.serial;
+	}
+	uffs_Perror(UFFS_MSG_SERIOUS, "unkown type, X-serial");
+	return INVALID_UFFS_SERIAL;
+}
+#endif
+
+/** 
+ * insert a TreeNode *node to tree
+ * \param[in] dev uffs device
+ * \param[in] type type of node
+ * \param[in] node node to be insert to
+ */
+void uffs_InsertNodeToTree(uffs_Device *dev, u8 type, TreeNode *node)
+{
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		uffs_InsertToDirEntry(dev, node);
+		break;
+	case UFFS_TYPE_FILE:
+		uffs_InsertToFileEntry(dev, node);
+		break;
+	case UFFS_TYPE_DATA:
+		uffs_InsertToDataEntry(dev, node);
+		break;
+	default:
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"unkown type, can't insert to tree");
+		break;
+	}
+}
+
+/** 
+ * find a node from tree
+ * \param[in] dev uffs device
+ * \param[in] type type of node
+ * \param[in] parent parent serial num
+ * \param[in] serial serial num
+ */
+TreeNode * uffs_FindFromTree(uffs_Device *dev,
+							 u8 type, u16 parent, u16 serial)
+{
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		return uffs_TreeFindDirNode(dev, serial);
+	case UFFS_TYPE_FILE:
+		return uffs_TreeFindFileNode(dev, serial);
+	case UFFS_TYPE_DATA:
+		return uffs_TreeFindDataNode(dev, parent, serial);
+	}
+	uffs_Perror(UFFS_MSG_SERIOUS,
+				"unkown type, can't find node");
+	return NULL;
+}
+
+
+
+static URET _BuildValidTreeNode(uffs_Device *dev,
+								TreeNode *node,		//!< empty node
+								uffs_BlockInfo *bc,
+								struct BlockTypeStatSt *st)
+{
+	uffs_Tags *tag;
+	TreeNode *node_alt;
+	u16 block, parent, serial, block_alt, block_save;
+	uffs_BlockInfo *bc_alt;
+	u8 type;
+	int page;
+	UBOOL needToInsertToTree = U_FALSE;
+	uffs_Buf *buf = NULL;
+	uffs_FileInfo *info;
+	u16 data_sum = 0;
+
+	// check the first page on the block ...
+	uffs_BlockInfoLoad(dev, bc, 0);
+
+	tag = GET_TAG(bc, 0);  //get first page's tag
+
+	if (!TAG_IS_DIRTY(tag)) {
+		// should never go here ... unless mark dirty page failed ?
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"First page is clean in a non-erased block ?");
+		return U_FAIL;
+	}
+
+	if (!TAG_IS_VALID(tag)) {
+		//first page is invalid ? should be erased now!
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"first page in block %d is invalid, will be erased now!",
+					bc->block);
+		goto process_invalid_block;		
+	}
+
+	block = bc->block;
+	parent = TAG_PARENT(tag);
+	serial = TAG_SERIAL(tag);
+	type = TAG_TYPE(tag);
+
+	// check if there is an 'alternative block' 
+	// (node which has the same serial number) in tree ?
+	node_alt = uffs_FindFromTree(dev, type, parent, serial); 
+
+	if (node_alt != NULL) {
+		//find a alternate node ! need to check the timestamp !
+
+		block_alt = _GetBlockFromNode(type, node_alt);
+
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"Process unclean block (%d vs %d)", block, block_alt);
+
+		if (block_alt == INVALID_UFFS_SERIAL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "invalid block ?");
+			return U_FAIL;
+		}
+		
+		bc_alt = uffs_BlockInfoGet(dev, block_alt);
+		if (bc_alt == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "can't get block info ");
+			return U_FAIL;
+		}
+		uffs_BlockInfoLoad(dev, bc_alt, 0);
+		if (uffs_IsSrcNewerThanObj (
+				TAG_BLOCK_TS(tag),
+				TAG_BLOCK_TS(GET_TAG(bc_alt, 0))) == U_TRUE) {
+
+			//the node is newer than node_alt, so keep node_alt, and erase node
+			uffs_FlashEraseBlock(dev, block);
+			node->u.list.block = block;
+			if (HAVE_BADBLOCK(dev))
+				uffs_BadBlockProcess(dev, node);
+			else
+				uffs_TreeInsertToErasedListTail(dev, node);
+
+			uffs_BlockInfoPut(dev, bc_alt);  //put back bc_alt before we return.
+			return U_SUCC;
+		}
+		else {
+			//the node is older than node_alt, so keep node, and erase node_alt
+			//we use node as erased node to insert to erased list
+
+			block_save = _GetBlockFromNode(type, node_alt);
+			uffs_FlashEraseBlock(dev, block_save);
+			node->u.list.block = block_save;
+			if (HAVE_BADBLOCK(dev))
+				uffs_BadBlockProcess(dev, node);
+			else
+				uffs_TreeInsertToErasedListTail(dev, node);
+
+			//put back bc_alt because we don't need it anymore.
+			uffs_BlockInfoPut(dev, bc_alt);
+			
+			//use node_alt to store new informations in following
+			node = node_alt;
+
+			needToInsertToTree = U_FALSE;
+		}
+	}
+	else {
+		needToInsertToTree = U_TRUE;
+	}
+
+	if (type == UFFS_TYPE_DIR || type == UFFS_TYPE_FILE) {
+		buf = uffs_BufClone(dev, NULL);
+		if (buf == NULL)
+			return U_FAIL;
+		uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES);
+		page = uffs_FindPageInBlockWithPageId(dev, bc, 0);
+		if (page == UFFS_INVALID_PAGE) {
+			uffs_BufFreeClone(dev, buf);
+			uffs_Perror(UFFS_MSG_SERIOUS,
+				"Can't find any valid page for page_id=0 ? invalid block !"
+				"this might be caused by the tag layout change.\n");
+			goto process_invalid_block;
+		}
+		page = uffs_FindBestPageInBlock(dev, bc, page);
+		uffs_FlashReadPage(dev, block, page, buf, U_FALSE);
+		info = (uffs_FileInfo *) (buf->data);
+		data_sum = uffs_MakeSum16(info->name, info->name_len);
+		uffs_BufFreeClone(dev, buf);
+	}
+
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		node->u.dir.block = bc->block;
+		node->u.dir.checksum = data_sum;
+		node->u.dir.parent = TAG_PARENT(tag);
+		node->u.dir.serial = TAG_SERIAL(tag);
+		st->dir++;
+		break;
+	case UFFS_TYPE_FILE:
+		node->u.file.block = bc->block;
+		node->u.file.checksum = data_sum;
+		node->u.file.parent = TAG_PARENT(tag);
+		node->u.file.serial = TAG_SERIAL(tag);
+		node->u.file.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_FILE);  
+		st->file++;
+		break;
+	case UFFS_TYPE_DATA:
+		node->u.data.block = bc->block;
+		node->u.data.parent = TAG_PARENT(tag);
+		node->u.data.serial = TAG_SERIAL(tag);
+		node->u.data.len = uffs_GetBlockFileDataLength(dev, bc, UFFS_TYPE_DATA); 
+		st->data++;
+		break;
+	}
+
+	if (needToInsertToTree == U_TRUE) {
+		uffs_InsertNodeToTree(dev, type, node);
+	}
+
+	return U_SUCC;
+
+process_invalid_block:
+	/* erase the invalid block */
+	uffs_FlashEraseBlock(dev, bc->block);
+
+	node->u.list.block = bc->block;
+
+	if (HAVE_BADBLOCK(dev))
+		uffs_BadBlockProcess(dev, node);
+	else
+		uffs_TreeInsertToErasedListTail(dev, node);
+
+	return U_SUCC;
+}
+
+
+static URET _ScanAndFixUnCleanPage(uffs_Device *dev, uffs_BlockInfo *bc)
+{
+	int page;
+	uffs_Tags *tag;
+	struct uffs_MiniHeaderSt header;
+
+	/* in most case, the valid block contents fewer free page,
+		so it's better scan from the last page ... to page 1.
+		note: scanning page 0 is not necessary, will check it later.
+
+		The worse case: read (pages_per_block - 1) * (mini header + spares) !
+		most case: read one spare.
+	*/
+	for (page = dev->attr->pages_per_block - 1; page > 0; page--) {
+		uffs_BlockInfoLoad(dev, bc, page);
+		tag = GET_TAG(bc, page);
+
+		if (TAG_IS_SEALED(tag))
+			break;	// tag sealed, no unclean page in this block.
+
+		if (TAG_IS_DIRTY(tag) || TAG_IS_VALID(tag)) {  // tag not sealed but dirty/valid ?
+			uffs_Perror(UFFS_MSG_NORMAL,
+					"unclean page found, block %d page %d",
+					bc->block, page);
+
+			// ok, an unclean page found.
+			// This unclean page can be identified by tag.
+			// We can leave it as it is, but performing a block recover would be good ?
+			// There won't be another unclean page in this block ... stop here.
+			break;
+		}
+
+		// now we have a clean tag (all 0xFF ?). Need to check mini header to see if it's an unclean page.
+		if (uffs_LoadMiniHeader(dev, bc->block, page, &header) == U_FAIL)
+			return U_FAIL;
+
+		if (header.status != 0xFF) {
+			// page data is dirty? this is an unclean page and we should explicitly mark tag as 'dirty and invalid'.
+			// This writing does not violate "no partial program" claim, because we are writing to a clean page spare.
+			uffs_Perror(UFFS_MSG_NORMAL,
+						"unclean page found, block %d page %d, mark it.",
+						bc->block, page);
+			uffs_FlashMarkDirtyPage(dev, bc, page);
+		}
+	}
+
+	return U_SUCC;
+}
+
+
+static URET _BuildTreeStepOne(uffs_Device *dev)
+{
+	int block_lt;
+	uffs_BlockInfo *bc = NULL;
+	TreeNode *node;
+	struct uffs_TreeSt *tree;
+	uffs_Pool *pool;
+	struct uffs_MiniHeaderSt header;
+	URET ret = U_SUCC;
+	struct BlockTypeStatSt st = {0, 0, 0};
+	
+	tree = &(dev->tree);
+	pool = TPOOL(dev);
+
+	tree->bad = NULL;
+	tree->bad_count = 0;
+	tree->erased = NULL;
+	tree->erased_tail = NULL;
+	tree->erased_count = 0;
+
+	uffs_Perror(UFFS_MSG_NOISY, "build tree step one");
+
+//	printf("s:%d e:%d\n", dev->par.start, dev->par.end);
+	for (block_lt = dev->par.start; block_lt <= dev->par.end; block_lt++) {
+		bc = uffs_BlockInfoGet(dev, block_lt);
+		if (bc == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "step one:fail to get block info");
+			ret = U_FAIL;
+			break;
+		}
+		node = (TreeNode *)uffs_PoolGet(pool);
+		if (node == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "insufficient tree node!");
+			ret = U_FAIL;
+			break;
+		}
+
+		// Need to check bad block at first !
+		if (uffs_FlashIsBadBlock(dev, block_lt) == U_TRUE) {
+			node->u.list.block = block_lt;
+			uffs_TreeInsertToBadBlockList(dev, node);
+			uffs_Perror(UFFS_MSG_NORMAL, "found bad block %d", block_lt);
+		}
+		else if (uffs_IsPageErased(dev, bc, 0) == U_TRUE) { //@ read one spare: 0
+			// page 0 tag shows it's an erased block, we need to check the mini header status to make sure it is clean.
+			if (uffs_LoadMiniHeader(dev, block_lt, 0, &header) == U_FAIL) {
+				uffs_Perror(UFFS_MSG_SERIOUS,
+							"I/O error when reading mini header !"
+							"block %d page %d",
+							block_lt, 0);
+				ret = U_FAIL;
+				break;
+			}
+
+			if (header.status != 0xFF) {
+				// page 0 tag is clean but page data is dirty ???
+				// this block should be erased immediately !
+				uffs_FlashEraseBlock(dev, block_lt);
+			}
+			node->u.list.block = block_lt;
+			if (HAVE_BADBLOCK(dev)) {
+				uffs_Perror(UFFS_MSG_NORMAL,
+							"New bad block (%d) discovered.", block_lt);
+				uffs_BadBlockProcess(dev, node);
+			}
+			else {
+				// page 0 is clean does not means all pages in this block are clean,
+				// need to check this block later before use it.
+				uffs_TreeInsertToErasedListTailEx(dev, node, 1);
+			}
+		}
+		else {
+			
+			// this block have valid data page(s).
+
+			ret = _ScanAndFixUnCleanPage(dev, bc);
+			if (ret == U_FAIL)
+				break;
+
+			ret = _BuildValidTreeNode(dev, node, bc, &st);
+			if (ret == U_FAIL)
+				break;
+
+		}
+		uffs_BlockInfoPut(dev, bc);
+	} //end of for
+
+	if(ret == U_FAIL) 
+		uffs_BlockInfoPut(dev, bc);
+
+	uffs_Perror(UFFS_MSG_NORMAL,
+				"DIR %d, FILE %d, DATA %d", st.dir, st.file, st.data);
+
+	return ret;
+}
+
+static URET _BuildTreeStepTwo(uffs_Device *dev)
+{
+	//Randomise the start point of erased block to implement wear levelling
+	u32 startCount = 0;
+	u32 endPoint;
+	TreeNode *node;
+
+	uffs_Perror(UFFS_MSG_NOISY, "build tree step two");
+
+	endPoint = uffs_GetCurDateTime() % (dev->tree.erased_count + 1);
+	while (startCount < endPoint) {
+		node = uffs_TreeGetErasedNodeNoCheck(dev);
+		if (node == NULL) {
+			uffs_Perror(UFFS_MSG_SERIOUS, "No erased block ?");
+			return U_FAIL;
+		}
+		uffs_TreeInsertToErasedListTailEx(dev, node, -1);
+		startCount++;
+	}
+
+	return U_SUCC;
+}
+
+TreeNode * uffs_TreeFindFileNode(uffs_Device *dev, u16 serial)
+{
+	int hash;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+
+	hash = serial & FILE_NODE_HASH_MASK;
+	x = tree->file_entry[hash];
+	while (x != EMPTY_NODE) {
+		node = FROM_IDX(x, TPOOL(dev));
+		if (node->u.file.serial == serial) {
+			return node;
+		}
+		else {
+			x = node->hash_next;
+		}
+	}
+	return NULL;
+}
+
+/** add a node into suspend list */
+void uffs_TreeSuspendAdd(uffs_Device *dev, TreeNode *node)
+{
+	node->u.list.next = dev->tree.suspend;
+	node->u.list.prev = NULL;
+
+	if (dev->tree.suspend)
+		dev->tree.suspend->u.list.prev = node;
+	dev->tree.suspend = node;
+}
+
+/** search suspend list */
+TreeNode * uffs_TreeFindSuspendNode(uffs_Device *dev, u16 serial)
+{
+	TreeNode *node = dev->tree.suspend;
+	while (node) {
+		if (node->u.list.u.serial == serial)
+			break;
+		
+		node = node->u.list.next;
+	}
+
+	return node;
+}
+
+/** remove a node from suspend list */
+void uffs_TreeRemoveSuspendNode(uffs_Device *dev, TreeNode *node)
+{
+	if (node->u.list.prev)
+		node->u.list.prev->u.list.next = node->u.list.next;
+	if (node->u.list.next)
+		node->u.list.next->u.list.prev = node->u.list.prev;
+	if (node == dev->tree.suspend)
+		dev->tree.suspend = NULL;
+}
+
+TreeNode * uffs_TreeFindFileNodeWithParent(uffs_Device *dev, u16 parent)
+{
+	int hash;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+
+	for (hash = 0; hash < FILE_NODE_ENTRY_LEN; hash++) {
+		x = tree->file_entry[hash];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.file.parent == parent) {
+				return node;
+			}
+			else {
+				x = node->hash_next;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindDirNode(uffs_Device *dev, u16 serial)
+{
+	int hash;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+
+	hash = serial & DIR_NODE_HASH_MASK;
+	x = tree->dir_entry[hash];
+	while (x != EMPTY_NODE) {
+		node = FROM_IDX(x, TPOOL(dev));
+		if (node->u.dir.serial == serial) {
+			return node;
+		}
+		else {
+			x = node->hash_next;
+		}
+	}
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindDirNodeWithParent(uffs_Device *dev, u16 parent)
+{
+	int hash;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+
+	for (hash = 0; hash < DIR_NODE_ENTRY_LEN; hash++) {
+		x = tree->dir_entry[hash];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.dir.parent == parent) {
+				return node;
+			}
+			else {
+				x = node->hash_next;
+			}
+		}
+	}
+	
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindFileNodeByName(uffs_Device *dev,
+										const char *name,
+										u32 len,
+										u16 sum, u16 parent)
+{
+	int i;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	
+	for (i = 0; i < FILE_NODE_ENTRY_LEN; i++) {
+		x = tree->file_entry[i];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.file.checksum == sum && node->u.file.parent == parent) {
+				//read file name from flash, and compare...
+				if (uffs_TreeCompareFileName(dev, name, len, sum, 
+												node, UFFS_TYPE_FILE) == U_TRUE) {
+					//Got it!
+					return node;
+				}
+			}
+			x = node->hash_next;
+		}
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindDataNode(uffs_Device *dev, u16 parent, u16 serial)
+{
+	int hash;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	u16 x;
+
+	hash = GET_DATA_HASH(parent, serial);
+	x = tree->data_entry[hash];
+	while(x != EMPTY_NODE) {
+		node = FROM_IDX(x, TPOOL(dev));
+
+		if(node->u.data.parent == parent &&
+			node->u.data.serial == serial)
+				return node;
+
+		x = node->hash_next;
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindDirNodeByBlock(uffs_Device *dev, u16 block)
+{
+	int hash;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	u16 x;
+
+	for (hash = 0; hash < DIR_NODE_ENTRY_LEN; hash++) {
+		x = tree->dir_entry[hash];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.dir.block == block)
+				return node;
+			x = node->hash_next;
+		}
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindErasedNodeByBlock(uffs_Device *dev, u16 block)
+{
+	TreeNode *node;
+	node = dev->tree.erased;
+
+	while (node) {
+		if (node->u.list.block == block) 
+			return node;
+		node = node->u.list.next;
+	}
+		
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindBadNodeByBlock(uffs_Device *dev, u16 block)
+{
+	TreeNode *node;
+	node = dev->tree.bad;
+
+	while (node) {
+		if (node->u.list.block == block) 
+			return node;
+		node = node->u.list.next;
+	}
+		
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindFileNodeByBlock(uffs_Device *dev, u16 block)
+{
+	int hash;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	u16 x;
+
+	for (hash = 0; hash < FILE_NODE_ENTRY_LEN; hash++) {
+		x = tree->file_entry[hash];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.file.block == block)
+				return node;
+			x = node->hash_next;
+		}
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindDataNodeByBlock(uffs_Device *dev, u16 block)
+{
+	int hash;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	u16 x;
+
+	for (hash = 0; hash < DATA_NODE_ENTRY_LEN; hash++) {
+		x = tree->data_entry[hash];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.data.block == block)
+				return node;
+			x = node->hash_next;
+		}
+	}
+
+	return NULL;
+}
+
+TreeNode * uffs_TreeFindNodeByBlock(uffs_Device *dev, u16 block, int *region)
+{
+	TreeNode *node = NULL;
+
+	if (*region & SEARCH_REGION_DATA) {
+		node = uffs_TreeFindDataNodeByBlock(dev, block);
+		if (node) {
+			*region &= SEARCH_REGION_DATA;
+			return node;
+		}
+	}
+	if (*region & SEARCH_REGION_FILE) {
+		node = uffs_TreeFindFileNodeByBlock(dev, block);
+		if (node) {
+			*region &= SEARCH_REGION_FILE;
+			return node;
+		}
+	}
+	if (*region & SEARCH_REGION_DIR) {
+		node = uffs_TreeFindDirNodeByBlock(dev, block);
+		if (node) {
+			*region &= SEARCH_REGION_DIR;
+			return node;
+		}
+	}
+	if (*region & SEARCH_REGION_ERASED) {
+		node = uffs_TreeFindErasedNodeByBlock(dev, block);
+		if (node) {
+			*region &= SEARCH_REGION_ERASED;
+			return node;
+		}
+	}
+	if (*region & SEARCH_REGION_BAD) {
+		node = uffs_TreeFindBadNodeByBlock(dev, block);
+		if (node) {
+			*region &= SEARCH_REGION_BAD;
+			return node;
+		}
+	}
+
+	return node;
+}
+
+TreeNode * uffs_TreeFindDirNodeByName(uffs_Device *dev,
+									  const char *name, u32 len,
+									  u16 sum, u16 parent)
+{
+	int i;
+	u16 x;
+	TreeNode *node;
+	struct uffs_TreeSt *tree = &(dev->tree);
+	
+	for (i = 0; i < DIR_NODE_ENTRY_LEN; i++) {
+		x = tree->dir_entry[i];
+		while (x != EMPTY_NODE) {
+			node = FROM_IDX(x, TPOOL(dev));
+			if (node->u.dir.checksum == sum &&
+					node->u.dir.parent == parent) {
+				//read file name from flash, and compare...
+				if (uffs_TreeCompareFileName(dev, name, len, sum,
+											node, UFFS_TYPE_DIR) == U_TRUE) {
+					//Got it!
+					return node;
+				}
+			}
+			x = node->hash_next;
+		}
+	}
+
+	return NULL;
+
+}
+
+UBOOL uffs_CompareFileName(const char *src, int src_len, const char *des)
+{
+	while (src_len-- > 0) {
+		if(*src++ != *des++)
+			return U_FALSE;
+	}
+
+	return U_TRUE;
+}
+
+/** compare [name] with tree [node] represented object name by loading
+	uffs_FileInfo from storage */
+UBOOL uffs_TreeCompareFileName(uffs_Device *dev,
+							   const char *name, u32 len, u16 sum,
+							   TreeNode *node, int type)
+{
+	UBOOL matched = U_FALSE;
+	uffs_FileInfo *fi;
+	uffs_Buf *buf;
+	u16 data_sum;
+
+	buf = uffs_BufGetEx(dev, type, node, 0, 0);
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "can't get buf !\n ");
+		goto ext;
+	}
+	fi = (uffs_FileInfo *)(buf->data);
+	data_sum = uffs_MakeSum16(fi->name, fi->name_len);
+
+	if (data_sum != sum) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"the obj's sum in storage is different with given sum!");
+		goto ext;
+	}
+
+	if (fi->name_len == len) {
+		if(uffs_CompareFileName(fi->name, fi->name_len, name) == U_TRUE) {
+			matched = U_TRUE;
+		}
+	}
+ext:
+	if (buf)
+		uffs_BufPut(dev, buf);
+
+	return matched;
+}
+
+
+/* calculate file length, etc */
+static URET _BuildTreeStepThree(uffs_Device *dev)
+{
+	int i;
+	u16 x;
+	TreeNode *work;
+	TreeNode *node;
+	struct uffs_TreeSt *tree;
+	uffs_Pool *pool;
+	u16 blockSave;
+
+	TreeNode *cache = NULL;
+	u16 cacheSerial = INVALID_UFFS_SERIAL;
+
+	tree = &(dev->tree);
+	pool = TPOOL(dev);
+
+	uffs_Perror(UFFS_MSG_NOISY, "build tree step three");
+
+	for (i = 0; i < DATA_NODE_ENTRY_LEN; i++) {
+		x = tree->data_entry[i];
+		while (x != EMPTY_NODE) {
+			work = FROM_IDX(x, pool);
+			if (work->u.data.parent == cacheSerial) {
+				node = cache;
+			}
+			else {
+				node = uffs_TreeFindFileNode(dev, work->u.data.parent);
+				cache = node;
+				cacheSerial = work->u.data.parent;
+			}
+			if (node == NULL) {
+				x = work->hash_next;
+				//this data block does not belong to any file ?
+				//should be erased.
+				uffs_Perror(UFFS_MSG_NORMAL,
+					"find a orphan data block:%d, "
+					"parent:%d, serial:%d, will be erased!",
+					work->u.data.block,
+					work->u.data.parent, work->u.data.serial);
+
+				uffs_BreakFromEntry(dev, UFFS_TYPE_DATA, work);
+				blockSave = work->u.data.block;
+				work->u.list.block = blockSave;
+				uffs_FlashEraseBlock(dev, blockSave);
+				if (HAVE_BADBLOCK(dev))
+					uffs_BadBlockProcess(dev, work);
+				else
+					uffs_TreeInsertToErasedListTail(dev, work);
+			}
+			else {
+				node->u.file.len += work->u.data.len;
+				x = work->hash_next;
+			}
+		}
+	}
+
+	return U_SUCC;
+}
+
+/** 
+ * \brief build tree structure from flash
+ * \param[in] dev uffs device
+ */
+URET uffs_BuildTree(uffs_Device *dev)
+{
+	URET ret;
+
+	/***** step one: scan all page spares, classify DIR/FILE/DATA nodes,
+		check bad blocks/uncompleted(conflicted) blocks as well *****/
+
+	/* if the disk is big and full filled of data this step could be
+		the most time consuming .... */
+
+	ret = _BuildTreeStepOne(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "build tree step one fail!");
+		return ret;
+	}
+
+	/***** step two: randomize the erased blocks, for ware-leveling purpose *****/
+	/* this step is very fast :) */
+	ret = _BuildTreeStepTwo(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "build tree step two fail!");
+		return ret;
+	}
+
+	/***** step three: check DATA nodes, find orphan nodes and erase them *****/
+	/* if there are a lot of files and disk is fully filled, this step 
+		could be very time consuming ... */
+	ret = _BuildTreeStepThree(dev);
+	if (ret != U_SUCC) {
+		uffs_Perror(UFFS_MSG_SERIOUS, "build tree step three fail!");
+		return ret;
+	}
+	
+	return U_SUCC;
+}
+
+/** 
+ * find a free file or dir serial NO
+ * \param[in] dev uffs device
+ * \return if no free serial found, return #INVALID_UFFS_SERIAL
+ */
+u16 uffs_FindFreeFsnSerial(uffs_Device *dev)
+{
+	u16 i;
+	TreeNode *node;
+
+	//TODO!! Do we need a faster serial number generating method?
+	//		 it depends on how often creating files or directories
+
+	for (i = ROOT_DIR_SERIAL + 1; i < MAX_UFFS_FSN; i++) {
+		node = uffs_TreeFindDirNode(dev, i);
+		if (node == NULL) {
+			node = uffs_TreeFindFileNode(dev, i);
+			if (node == NULL) {
+				node = uffs_TreeFindSuspendNode(dev, i);
+				if (node == NULL)
+					return i;
+			}
+		}
+	}
+
+	return INVALID_UFFS_SERIAL;
+}
+
+static TreeNode * uffs_TreeGetErasedNodeNoCheck(uffs_Device *dev)
+{
+	TreeNode *node = NULL;
+	if (dev->tree.erased) {
+		node = dev->tree.erased;
+		dev->tree.erased->u.list.prev = NULL;
+		dev->tree.erased = dev->tree.erased->u.list.next;
+		if(dev->tree.erased == NULL) 
+			dev->tree.erased_tail = NULL;
+		dev->tree.erased_count--;
+	}
+	
+	return node;
+}
+
+TreeNode * uffs_TreeGetErasedNode(uffs_Device *dev)
+{
+	TreeNode *node = uffs_TreeGetErasedNodeNoCheck(dev);
+	u16 block;
+	
+	if (node && node->u.list.u.need_check) {
+		block = node->u.list.block;
+		if (uffs_FlashCheckErasedBlock(dev, block) != U_SUCC) {
+			// Hmm, this block is not fully erased ? erase it immediately.
+			uffs_FlashEraseBlock(dev, block);
+			node->u.list.u.need_check = 0;
+		}
+	}
+	return node;
+}
+
+static void _InsertToEntry(uffs_Device *dev, u16 *entry,
+						   int hash, TreeNode *node)
+{
+	node->hash_next = entry[hash];
+	node->hash_prev = EMPTY_NODE;
+	if (entry[hash] != EMPTY_NODE) {
+		FROM_IDX(entry[hash], TPOOL(dev))->hash_prev = TO_IDX(node, TPOOL(dev));
+	}
+	entry[hash] = TO_IDX(node, TPOOL(dev));
+}
+
+
+/** 
+ * break the node from entry
+ */
+void uffs_BreakFromEntry(uffs_Device *dev, u8 type, TreeNode *node)
+{
+	u16 *entry;
+	int hash;
+	TreeNode *work;
+
+	switch (type) {
+	case UFFS_TYPE_DIR:
+		hash = GET_DIR_HASH(node->u.dir.serial);
+		entry = &(dev->tree.dir_entry[hash]);
+		break;
+	case UFFS_TYPE_FILE:
+		hash = GET_FILE_HASH(node->u.file.serial);
+		entry = &(dev->tree.file_entry[hash]);
+		break;
+	case UFFS_TYPE_DATA:
+		hash = GET_DATA_HASH(node->u.data.parent, node->u.data.serial);
+		entry = &(dev->tree.data_entry[hash]);
+		break;
+	default:
+		uffs_Perror(UFFS_MSG_SERIOUS, "unknown type when break...");
+		return;
+	}
+
+	if (node->hash_prev != EMPTY_NODE) {
+		work = FROM_IDX(node->hash_prev, &(dev->mem.tree_pool));
+		work->hash_next = node->hash_next;
+	}
+	if (node->hash_next != EMPTY_NODE) {
+		work = FROM_IDX(node->hash_next, &(dev->mem.tree_pool));
+		work->hash_prev = node->hash_prev;
+	}
+
+	if (*entry == TO_IDX(node, &(dev->mem.tree_pool))) {
+		*entry = node->hash_next;
+	}
+}
+
+static void uffs_InsertToFileEntry(uffs_Device *dev, TreeNode *node)
+{
+	_InsertToEntry(dev, dev->tree.file_entry,
+					GET_FILE_HASH(node->u.file.serial),
+					node);
+}
+
+static void uffs_InsertToDirEntry(uffs_Device *dev, TreeNode *node)
+{
+	_InsertToEntry(dev, dev->tree.dir_entry,
+					GET_DIR_HASH(node->u.dir.serial),
+					node);
+}
+
+static void uffs_InsertToDataEntry(uffs_Device *dev, TreeNode *node)
+{
+	_InsertToEntry(dev, dev->tree.data_entry,
+					GET_DATA_HASH(node->u.data.parent, node->u.data.serial),
+					node);
+}
+
+void uffs_InsertToErasedListHead(uffs_Device *dev, TreeNode *node)
+{
+	struct uffs_TreeSt *tree;
+	tree = &(dev->tree);
+
+	node->u.list.next = tree->erased;
+	node->u.list.prev = NULL;
+
+	if (tree->erased) {
+		tree->erased->u.list.prev = node;
+	}
+
+	tree->erased = node;
+	if (node->u.list.next == tree->erased_tail) {
+		tree->erased_tail = node;
+	}
+	tree->erased_count++;
+}
+
+/**
+ * insert node to erased list.
+ * \param need_check: 0 - no need to check later
+ *                    1 - need to check later
+ *                  < 0 - keep 'node->u.list.need_check' value
+ */
+void uffs_TreeInsertToErasedListTailEx(uffs_Device *dev, TreeNode *node, int need_check)
+{
+	struct uffs_TreeSt *tree;
+	tree = &(dev->tree);
+	
+	if (need_check >= 0)
+		node->u.list.u.need_check = need_check;
+	
+	node->u.list.next = NULL;
+	node->u.list.prev = tree->erased_tail;
+	if (tree->erased_tail) {
+		tree->erased_tail->u.list.next = node;
+	}
+
+	tree->erased_tail = node;
+	if(tree->erased == NULL) {
+		tree->erased = node;
+	}
+	tree->erased_count++;
+}
+
+void uffs_TreeInsertToErasedListTail(uffs_Device *dev, TreeNode *node)
+{
+	// this function is called after the block is erased, so don't need to check.
+	uffs_TreeInsertToErasedListTailEx(dev, node, 0);
+}
+
+void uffs_TreeInsertToBadBlockList(uffs_Device *dev, TreeNode *node)
+{
+	struct uffs_TreeSt *tree;
+
+	tree = &(dev->tree);
+	node->u.list.prev = NULL;
+	node->u.list.next = tree->bad;
+
+	if (tree->bad) {
+		tree->bad->u.list.prev = node;
+	}
+
+	tree->bad = node;
+	tree->bad_count++;
+}
+
+/** 
+ * set tree node block value
+ */
+void uffs_TreeSetNodeBlock(u8 type, TreeNode *node, u16 block)
+{
+	switch (type) {
+	case UFFS_TYPE_FILE:
+		node->u.file.block = block;
+		break;
+	case UFFS_TYPE_DIR:
+		node->u.dir.block = block;
+		break;
+	case UFFS_TYPE_DATA:
+		node->u.data.block = block;
+		break;
+	}
+}
+

+ 402 - 0
src/uffs/uffs_utils.c

@@ -0,0 +1,402 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_utils.c
+ * \brief utilities of uffs
+ * \author Ricky Zheng, created 12th May, 2005
+ */
+#include "uffs_config.h"
+#include "uffs/uffs_device.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_version.h"
+#include "uffs/uffs_badblock.h"
+#include "uffs/uffs_fd.h"
+#include "uffs/uffs_fs.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define PFX "util: "
+
+#define SPOOL(dev) &((dev)->mem.spare_pool)
+
+#ifdef CONFIG_USE_GLOBAL_FS_LOCK
+static OSSEM _global_lock = OSSEM_NOT_INITED;
+
+/* global file system lock */
+void uffs_InitGlobalFsLock(void)
+{
+	uffs_SemCreate(&_global_lock);
+}
+
+void uffs_ReleaseGlobalFsLock(void)
+{
+	uffs_SemDelete(&_global_lock);
+}
+
+void uffs_GlobalFsLockLock(void)
+{
+	uffs_SemWait(_global_lock);
+}
+
+void uffs_GlobalFsLockUnlock(void)
+{
+	uffs_SemSignal(_global_lock);
+}
+
+#else
+
+void uffs_InitGlobalFsLock(void) {}
+void uffs_ReleaseGlobalFsLock(void) {}
+void uffs_GlobalFsLockLock(void) {}
+void uffs_GlobalFsLockUnlock(void) {}
+
+#endif
+
+
+#ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY
+static void _ForceFormatAndCheckBlock(uffs_Device *dev, int block)
+{
+	int i, j;
+	uffs_Buf *buf = NULL;
+	UBOOL bad = U_TRUE;
+	URET ret;
+	struct uffs_FlashOpsSt *ops = dev->ops;
+	struct uffs_TagStoreSt ts;
+	u8 *spare = NULL;
+
+	buf = uffs_BufClone(dev, NULL);
+	if (buf == NULL) {
+		uffs_Perror(UFFS_MSG_SERIOUS,
+					"Alloc page buffer fail ! Format stoped.");
+		goto ext;
+	}
+
+	spare = (u8 *)uffs_PoolGet(SPOOL(dev));
+	if (spare == NULL)
+		goto ext;
+
+	//step 1: Erase, fully fill with 0x0, and check
+	ret = uffs_FlashEraseBlock(dev, block);
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		goto bad_out;
+
+	memset(buf->header, 0, dev->com.pg_size);
+	memset(&ts, 0, sizeof(ts));
+	memset(spare, 0, dev->attr->spare_size);
+
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		if (ops->WritePageWithLayout)
+			ret = ops->WritePageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts);
+		else
+			ret = ops->WritePage(dev, block, i, buf->header, dev->com.pg_size, spare, dev->attr->spare_size);
+
+		if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+			goto bad_out;
+	}
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		memset(buf->header, 0xFF, dev->com.pg_size);
+		memset(&ts, 0xFF, sizeof(ts));
+		memset(spare, 0xFF, dev->attr->spare_size);
+
+		if (ops->ReadPageWithLayout) {
+			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
+			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+				goto bad_out;
+			for (j = 0; j < dev->com.pg_size; j++)
+				if (buf->header[j] != 0)
+					goto bad_out;
+			for (j = 0; j < sizeof(ts); j++)
+				if (((u8 *)&ts)[j] != 0)
+					goto bad_out;
+		}
+		else {
+			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
+			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+				goto bad_out;
+			for (j = 0; j < dev->com.pg_size; j++)
+				if (buf->header[j] != 0)
+					goto bad_out;
+			for (j = 0; j < dev->attr->spare_size; j++)
+				if (spare[j] != 0)
+					goto bad_out;
+		}
+	}
+
+	//step 2: Erase, and check
+	ret = uffs_FlashEraseBlock(dev, block);
+	if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+		goto bad_out;
+
+	for (i = 0; i < dev->attr->pages_per_block; i++) {
+		memset(buf->header, 0, dev->com.pg_size);
+		memset(&ts, 0, sizeof(ts));
+		memset(spare, 0, dev->attr->spare_size);
+
+		if (ops->ReadPageWithLayout) {
+			ret = ops->ReadPageWithLayout(dev, block, i, buf->header, dev->com.pg_size, NULL, &ts, NULL);
+			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+				goto bad_out;
+			for (j = 0; j < dev->com.pg_size; j++)
+				if (buf->header[j] != 0xFF)
+					goto bad_out;
+			for (j = 0; j < sizeof(ts); j++)
+				if (((u8 *)&ts)[j] != 0xFF)
+					goto bad_out;
+		}
+		else {
+			ret = ops->ReadPage(dev, block, i, buf->header, dev->com.pg_size, NULL, spare, dev->attr->spare_size);
+			if (UFFS_FLASH_IS_BAD_BLOCK(ret))
+				goto bad_out;
+			for (j = 0; j < dev->com.pg_size; j++)
+				if (buf->header[j] != 0xFF)
+					goto bad_out;
+			for (j = 0; j < dev->attr->spare_size; j++)
+				if (spare[j] != 0xFF)
+					goto bad_out;
+		}
+	}
+
+	// format succ
+	bad = U_FALSE;
+
+bad_out:
+	if (bad == U_TRUE)
+		uffs_FlashMarkBadBlock(dev, block);
+ext:
+	if (buf)
+		uffs_BufFreeClone(dev, buf);
+
+	if (spare)
+		uffs_PoolPut(SPOOL(dev), spare);
+
+	return;
+}
+#endif
+
+
+
+URET uffs_FormatDevice(uffs_Device *dev, UBOOL force)
+{
+	u16 i, slot;
+	URET ret = U_SUCC;
+	
+	if (dev == NULL)
+		return U_FAIL;
+
+	if (dev->ops == NULL) 
+		return U_FAIL;
+
+	uffs_GlobalFsLockLock();
+
+	ret = uffs_BufFlushAll(dev);
+
+	if (dev->ref_count > 1 && !force) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"can't format when dev->ref_count = %d",
+					dev->ref_count);
+		ret = U_FAIL;
+	}
+
+	if (ret == U_SUCC && force) {
+		uffs_DirEntryBufPutAll(dev);
+		uffs_PutAllObjectBuf(dev);
+		uffs_FdSignatureIncrease();
+	}
+
+	if (ret == U_SUCC &&
+		uffs_BufIsAllFree(dev) == U_FALSE &&
+		!force)
+	{
+		uffs_Perror(UFFS_MSG_NORMAL, "some page still in used!");
+		ret = U_FAIL;
+	}
+
+	if (!force) {
+		for (slot = 0; ret == U_SUCC && slot < dev->cfg.dirty_groups; slot++) {
+			if (dev->buf.dirtyGroup[slot].count > 0) {
+				uffs_Perror(UFFS_MSG_SERIOUS, "there still have dirty pages!");
+				ret = U_FAIL;
+			}
+		}
+	}
+
+	if (ret == U_SUCC)
+		uffs_BufSetAllEmpty(dev);
+
+
+	if (ret == U_SUCC && uffs_BlockInfoIsAllFree(dev) == U_FALSE && !force) {
+		uffs_Perror(UFFS_MSG_NORMAL,
+					"there still have block info cache ? fail to format");
+		ret = U_FAIL;
+	}
+
+	if (ret == U_SUCC)
+		uffs_BlockInfoExpireAll(dev);
+
+	for (i = dev->par.start; ret == U_SUCC && i <= dev->par.end; i++) {
+		if (uffs_FlashIsBadBlock(dev, i) == U_FALSE) {
+			uffs_FlashEraseBlock(dev, i);
+			if (HAVE_BADBLOCK(dev))
+				uffs_BadBlockProcess(dev, NULL);
+		}
+		else {
+#ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY
+			_ForceFormatAndCheckBlock(dev, i);
+#endif
+		}
+	}
+
+	if (ret == U_SUCC && uffs_TreeRelease(dev) == U_FAIL) {
+		ret = U_FAIL;
+	}
+
+	if (ret == U_SUCC && uffs_TreeInit(dev) == U_FAIL) {
+		ret = U_FAIL;
+	}
+
+	if (ret == U_SUCC && uffs_BuildTree(dev) == U_FAIL) {
+		ret = U_FAIL;
+	}
+
+	uffs_GlobalFsLockUnlock();
+
+	return ret;
+}
+
+static const char * GetTagName(struct uffs_TagStoreSt *s)
+{
+	const char *name = "UNKNOWN";
+	struct uffs_NodeTypeNameMapSt maps[] = UFFS_TYPE_NAME_MAP;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(maps); i++) {
+		if (s->type == maps[i].type)
+			name = maps[i].name;
+	}
+
+	return name;
+}
+
+static void DumpBufHex(struct uffs_DeviceSt *dev, const u8* buf, int len, dump_msg_cb *dump)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		dump(dev, "%02X ", buf[i]);
+}
+
+// return -1 if do not need to read next tag
+static int DumpTag(struct uffs_DeviceSt *dev, int block, int page, uffs_Tags *tag, dump_msg_cb *dump)
+{
+	struct uffs_TagStoreSt *s = &tag->s;
+	struct uffs_MiniHeaderSt header;
+	URET ret;
+
+	if (!TAG_IS_DIRTY(tag)) {
+		// is a clean page ?
+		ret = uffs_LoadMiniHeader(dev, block, page, &header);
+		if (ret == U_FAIL) {
+			dump(dev, "Fail to load mini header from page 0\n");
+		}
+		else {
+			if (header.status == 0xFF)
+				dump(dev, "page %d CLEAN\n", page);
+			else {
+				dump(dev, "page %d NOT clean ! header: ", page);
+				DumpBufHex(dev, (u8 *)&header, sizeof(header), dump);
+				dump(dev, ", tag: ");
+				DumpBufHex(dev, (u8 *)s, sizeof(struct uffs_TagStoreSt), dump);
+				dump(dev, "\n");
+			}
+		}
+		return -1;
+	}
+	
+	dump(dev, " - page %2d/%2d %s %d/%d len%4d\n", page, s->page_id, GetTagName(s), s->serial, s->parent, s->data_len);
+	
+	return 0;
+}
+
+static void DumpBlock(struct uffs_DeviceSt *dev, int block, dump_msg_cb *dump)
+{
+	int i;
+	struct uffs_StorageAttrSt *attr = dev->attr;
+	uffs_Tags tag;
+	URET ret;
+
+	dump(dev, "--- Block %d ---\n", block);
+
+	if (uffs_FlashIsBadBlock(dev, block)) {
+		dump(dev, "Bad block\n\n");
+		return;
+	}
+
+	for (i = 0; i < attr->pages_per_block; i++) {
+
+		memset(&tag, 0xFF, sizeof(tag));
+		ret = uffs_FlashReadPageTag(dev, block, i, &tag);
+
+		if (ret == UFFS_FLASH_IO_ERR) {
+			dump(dev, "page %d tag I/O error\n", i);
+			continue;
+		}
+		else if (ret == UFFS_FLASH_ECC_FAIL) {
+			dump(dev, "page %d tag ECC error\n", i);
+			continue;
+		}
+		else if (ret == UFFS_FLASH_NO_ERR || ret == UFFS_FLASH_ECC_OK) {
+			if (ret == UFFS_FLASH_ECC_OK)
+				dump(dev, "page %d tag has bit flip, corrected by ECC\n", i);
+
+			if (DumpTag(dev, block, i, &tag, dump) == 0)
+				continue;
+			else
+				break;
+		}
+		else {
+			dump(dev, "read page %d tag return unexpected: %d\n", i, ret);
+			continue;
+		}
+	}
+	dump(dev, "\n");
+}
+
+void uffs_DumpDevice(struct uffs_DeviceSt *dev, dump_msg_cb *dump)
+{
+	int i;
+	for (i = dev->par.start; i <= dev->par.end; i++) {
+		DumpBlock(dev, i, dump);
+	}	
+}

+ 73 - 0
src/uffs/uffs_version.c

@@ -0,0 +1,73 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_version.c
+ * \brief uffs version information
+ * \author Ricky Zheng, created 8th May, 2005
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_version.h"
+
+#include <stdio.h>
+#define PFX "ver : "
+
+
+static char version_buf[16];
+
+const char * uffs_Version2Str(int ver)
+{
+	sprintf(version_buf, "%1d.%02d.%04d",
+			(ver & 0xff000000) >> 24, (ver & 0xff0000) >> 16, (ver & 0xffff));
+	return version_buf;
+}
+
+int uffs_GetVersion(void)
+{
+	return UFFS_VERSION;
+}
+
+int uffs_GetMainVersion(int ver)
+{
+	return (ver & 0xff000000) >> 24;
+}
+
+int uffs_GetMinorVersion(int ver)
+{
+	return (ver & 0xff0000) >> 16;
+}
+
+int uffs_GetTrivialVersion(int ver)
+{
+	return (ver & 0xff00) >> 8;
+}

+ 20 - 0
src/utils/CMakeLists.txt

@@ -0,0 +1,20 @@
+IF (UNIX)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/posix)
+ENDIF()
+IF (WIN32)
+	INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/platform/win32)
+ENDIF()
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc)
+INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/emu)
+
+LINK_DIRECTORIES(${uffs_BINARY_DIR}/src/emu)
+LINK_DIRECTORIES(${uffs_BINARY_DIR}/src/uffs)
+
+SET(mkuffs_SRCS mkuffs.c)
+ADD_EXECUTABLE(mkuffs ${mkuffs_SRCS})
+TARGET_LINK_LIBRARIES(mkuffs emu uffs emu platform apitest_server)
+IF (UNIX)
+	TARGET_LINK_LIBRARIES(mkuffs pthread)
+ENDIF ()
+
+

+ 493 - 0
src/utils/mkuffs.c

@@ -0,0 +1,493 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_test.c
+ * \brief uffs test main entry
+ * \author Ricky Zheng
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+#include "uffs/uffs_fs.h"
+#include "uffs/uffs_utils.h"
+#include "uffs/uffs_core.h"
+#include "uffs/uffs_mtb.h"
+
+#include "cmdline.h"
+#include "uffs_fileem.h"
+
+#define PFX NULL
+#define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+#define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0
+int main()
+{
+	MSGLN("Static memory allocator is not supported.");
+	return 0;
+}
+#else
+
+extern struct cli_commandset * get_helper_cmds(void);
+extern struct cli_commandset * get_test_cmds(void);
+extern void femu_init_uffs_device(uffs_Device *dev);
+
+static int conf_command_line_mode = 0;
+static int conf_verbose_mode = 0;
+
+static int conf_exec_script = 0;
+static char script_command[256];
+
+#define DEFAULT_EMU_FILENAME "uffsemfile.bin"
+const char * conf_emu_filename = DEFAULT_EMU_FILENAME;
+
+
+/* default basic parameters of the NAND device */
+#define PAGES_PER_BLOCK_DEFAULT			32
+#define PAGE_DATA_SIZE_DEFAULT			512
+#define PAGE_SPARE_SIZE_DEFAULT			16
+#define STATUS_BYTE_OFFSET_DEFAULT		5
+#define TOTAL_BLOCKS_DEFAULT			128
+#define ECC_OPTION_DEFAULT				UFFS_ECC_SOFT
+//#define ECC_OPTION_DEFAULT			UFFS_ECC_HW
+//#define ECC_OPTION_DEFAULT			UFFS_ECC_HW_AUTO
+
+#define MAX_MOUNT_TABLES		10
+#define MAX_MOUNT_POINT_NAME	32
+
+static int conf_pages_per_block = PAGES_PER_BLOCK_DEFAULT;
+static int conf_page_data_size = PAGE_DATA_SIZE_DEFAULT;
+static int conf_page_spare_size = PAGE_SPARE_SIZE_DEFAULT;
+static int conf_status_byte_offset = STATUS_BYTE_OFFSET_DEFAULT;
+static int conf_total_blocks = TOTAL_BLOCKS_DEFAULT;
+static int conf_ecc_option = ECC_OPTION_DEFAULT;
+static int conf_ecc_size = 0; // 0 - Let UFFS choose the size
+
+static const char *g_ecc_option_strings[] = UFFS_ECC_OPTION_STRING;
+
+static struct uffs_MountTableEntrySt conf_mounts[MAX_MOUNT_TABLES] = {{0}};
+static uffs_Device conf_devices[MAX_MOUNT_TABLES] = {{0}};
+static char mount_point_name[MAX_MOUNT_TABLES][MAX_MOUNT_POINT_NAME] = {{0}};
+
+static void setup_storage(struct uffs_StorageAttrSt *attr)
+{
+	attr->total_blocks = conf_total_blocks;				/* total blocks */
+	attr->page_data_size = conf_page_data_size;			/* page data size */
+	attr->spare_size = conf_page_spare_size;			/* page spare size */
+	attr->pages_per_block = conf_pages_per_block;		/* pages per block */
+
+	attr->block_status_offs = conf_status_byte_offset;	/* block status offset is 5th byte in spare */
+	attr->ecc_opt = conf_ecc_option;					/* ECC option */
+	attr->ecc_size = conf_ecc_size;						/* ECC size */
+	attr->layout_opt = UFFS_LAYOUT_UFFS;				/* let UFFS handle layout */
+}
+
+
+static void setup_device(uffs_Device *dev)
+{
+	dev->Init = femu_InitDevice;
+	dev->Release = femu_ReleaseDevice;
+	dev->attr = femu_GetStorage();
+}
+
+static void setup_emu_private(uffs_FileEmu *emu)
+{
+	memset(emu, 0, sizeof(uffs_FileEmu));
+	emu->emu_filename = conf_emu_filename;
+}
+
+static int init_uffs_fs(void)
+{
+	static int bIsFileSystemInited = 0;
+	struct uffs_MountTableEntrySt *mtbl = &(conf_mounts[0]);
+	struct uffs_ConfigSt cfg = {
+		0,			// bc_caches - default
+		0,			// page_buffers - default
+		0,			// dirty_pages - default
+		0,			// dirty_groups - default
+		0,			// reserved_free_blocks - default
+	};
+
+	if (bIsFileSystemInited)
+		return -4;
+
+	bIsFileSystemInited = 1;
+
+	while (mtbl->dev) {
+
+		memcpy(&mtbl->dev->cfg, &cfg, sizeof(struct uffs_ConfigSt));
+
+#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
+		uffs_MemSetupSystemAllocator(&mtbl->dev->mem);
+#endif
+		setup_device(mtbl->dev);
+		uffs_RegisterMountTable(mtbl);
+		mtbl++;
+	}
+
+	// mount partitions
+	for (mtbl = &(conf_mounts[0]); mtbl->mount != NULL; mtbl++) {
+		uffs_Mount(mtbl->mount);
+	}
+
+	return uffs_InitFileSystemObjects() == U_SUCC ? 0 : -1;
+}
+
+static int release_uffs_fs(void)
+{
+	int ret = 0;
+	uffs_MountTable *mtb;
+
+	for (mtb = &(conf_mounts[0]); ret == 0 && mtb->mount != NULL; mtb++) {
+		uffs_UnMount(mtb->mount);
+	}
+
+	if (ret == 0)
+		ret = (uffs_ReleaseFileSystemObjects() == U_SUCC ? 0 : -1);
+
+	return ret;
+}
+
+/* mount point arg: /sys/,100,-1 */
+static int parse_mount_point(char *arg, int m_idx)
+{
+	int start = 0, end = -1;
+	char *p = arg;
+	struct uffs_MountTableEntrySt *mtbl = &(conf_mounts[m_idx]);
+
+	while(*p && *p != ',' && *p != ' ' && *p != '\t')
+		p++;
+
+	if (*p == 0 || p == arg) 
+		return -1;
+
+	mtbl->mount = &(mount_point_name[m_idx][0]);
+	memcpy((char *)mtbl->mount, arg, p - arg);
+	((char *)(mtbl->mount))[p - arg] = 0;
+
+	p++;
+	arg = p;
+	while(*p && *p != ',' && *p != ' ' && *p != '\t')
+		p++;
+
+	if (p != arg) {
+		if (sscanf(arg, "%i", &start) < 1) 
+			return -1;
+		p++;
+		arg = p;
+
+		while(*p && *p != ',' && *p != ' ' && *p != '\t')
+			p++;
+
+		if (p != arg) {
+			if (sscanf(arg, "%i", &end) < 1)
+				return -1;
+		}
+	}
+	mtbl->start_block = start;
+	mtbl->end_block = end;
+	mtbl->dev = &(conf_devices[m_idx]);
+
+	return 0;
+}
+
+static int parse_options(int argc, char *argv[])
+{
+    int iarg;
+    int usage = 0;
+	int m_idx = 0;
+    static char em_file[128];
+	int i;
+
+    for (iarg = 1; iarg < argc && !usage; iarg++) {
+        const char *arg = argv[iarg];
+        
+        if (arg[0] == '-') {
+            if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
+                usage++;
+            }
+            else if (!strcmp(arg, "-f") || !strcmp(arg, "--file")) {
+                if (++iarg >= argc)
+					usage++;
+				else {
+					strcpy(em_file, argv[iarg]);
+					conf_emu_filename = (const char *)em_file;
+				}
+            }
+            else if (!strcmp(arg, "-c") || !strcmp(arg, "--command-line")) {
+				conf_command_line_mode = 1;
+            }
+            else if (!strcmp(arg, "-p") || !strcmp(arg, "--page-size")) {
+                if (++iarg >= argc) 
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_page_data_size) < 1)
+					usage++;
+				if (conf_page_data_size <= 0 || conf_page_data_size > UFFS_MAX_PAGE_SIZE) {
+					MSGLN("ERROR: Invalid page data size");
+					usage++;
+				}
+            }
+            else if (!strcmp(arg, "-s") || !strcmp(arg, "--spare-size")) {
+                if (++iarg >= argc) 
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_page_spare_size) < 1) 
+					usage++;
+				if (conf_page_spare_size < sizeof(struct uffs_TagStoreSt) + 1 ||
+					(conf_page_spare_size % 4) != 0 || conf_page_spare_size > UFFS_MAX_SPARE_SIZE) {
+					MSGLN("ERROR: Invalid spare size");
+					usage++;
+				}
+            }
+            else if (!strcmp(arg, "-o") || !strcmp(arg, "--status-offset")) {
+                if (++iarg >= argc) 
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_status_byte_offset) < 1)
+					usage++;
+				if (conf_status_byte_offset < 0)
+					usage++;
+            }
+            else if (!strcmp(arg, "-b") || !strcmp(arg, "--block-pages")) {
+                if (++iarg >= argc)
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_pages_per_block) < 1)
+					usage++;
+				if (conf_pages_per_block < 2)
+					usage++;
+            }
+            else if (!strcmp(arg, "-t") || !strcmp(arg, "--total-blocks")) {
+                if (++iarg >= argc)
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_total_blocks) < 1)
+					usage++;
+				if (conf_total_blocks < 2)
+					usage++;
+            }
+            else if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
+				conf_verbose_mode++;
+            }
+            else if (!strcmp(arg, "-m") || !strcmp(arg, "--mount")) {
+				if (++iarg > argc)
+					usage++;
+				else if (parse_mount_point(argv[iarg], m_idx) < 0)
+					usage++;
+				m_idx++;
+            }
+			else if (!strcmp(arg, "-e") || !strcmp(arg, "--exec")) {
+				if (++iarg > argc)
+					usage++;
+				else {
+					sprintf(script_command, "script %s", argv[iarg]);
+					conf_exec_script = 1;
+				}
+			}
+			else if (!strcmp(arg, "-x") || !strcmp(arg, "--ecc-option")) {
+				if (++iarg > argc)
+					usage++;
+				else {
+					for (i = 0; i < ARRAY_SIZE(g_ecc_option_strings); i++) {
+						if (!strcmp(argv[iarg], g_ecc_option_strings[i])) {
+							conf_ecc_option = i;
+							break;
+						}
+					}
+					if (i == ARRAY_SIZE(g_ecc_option_strings)) {
+						MSGLN("ERROR: Invalid ECC option");
+						usage++;
+					}
+				}
+			}
+			else if (!strcmp(arg, "-z") || !strcmp(arg, "--ecc-size")) {
+                if (++iarg >= argc)
+					usage++;
+                else if (sscanf(argv[iarg], "%i", &conf_ecc_size) < 1)
+					usage++;
+				if (conf_ecc_size < 0 || conf_ecc_size > UFFS_MAX_ECC_SIZE) {
+					MSGLN("ERROR: Invalid ecc size");
+					usage++;
+				}
+			}
+            else {
+                MSGLN("Unknown option: %s, try %s --help", arg, argv[0]);
+				return -1;
+            }
+        }
+        else {
+            MSGLN("Unexpected parameter: %s, try %s --help", arg, argv[0]);
+			return -1;
+        }
+    }
+    
+    if (usage) {
+        MSGLN("Usage: %s [options]", argv[0]);
+        MSGLN("  -h  --help                                show usage");
+        MSGLN("  -c  --command-line                        command line mode");
+        MSGLN("  -v  --verbose                             verbose mode");
+        MSGLN("  -f  --file           <file>               uffs image file");
+        MSGLN("  -p  --page-size      <n>                  page data size, default=%d", PAGE_DATA_SIZE_DEFAULT);
+        MSGLN("  -s  --spare-size     <n>                  page spare size, default=%d", PAGE_SPARE_SIZE_DEFAULT);
+		MSGLN("  -o  --status-offset  <n>                  status byte offset, default=%d", STATUS_BYTE_OFFSET_DEFAULT);
+        MSGLN("  -b  --block-pages    <n>                  pages per block, default=%d", PAGES_PER_BLOCK_DEFAULT);
+        MSGLN("  -t  --total-blocks   <n>                  total blocks");
+        MSGLN("  -m  --mount          <mount_point,start,end> , for example: -m /,0,-1");
+		MSGLN("  -x  --ecc-option     <none|soft|hw|auto>  ECC option, default=%s", g_ecc_option_strings[ECC_OPTION_DEFAULT]);
+		MSGLN("  -z  --ecc-size       <n>                  ECC size, default=0 (auto)");
+        MSGLN("  -e  --exec           <file>               execute a script file");
+        MSGLN("");
+
+        return -1;
+    }
+
+	if (m_idx == 0) {
+		// if not given mount information, use default ('/' for whole partition)
+		parse_mount_point("/,0,-1", 0);
+	}
+
+	return 0;
+}
+
+
+static void print_mount_points(void)
+{
+	struct uffs_MountTableEntrySt *m;
+
+	m = &(conf_mounts[0]);
+	while (m->dev) {
+		MSGLN ("Mount point: %s, start: %d, end: %d", m->mount, m->start_block, m->end_block);
+		m++;
+	}
+}
+
+static void print_params(void)
+{
+	MSGLN("Parameters summary:");
+	MSGLN("  uffs image file: %s", conf_emu_filename);
+	MSGLN("  page size: %d", conf_page_data_size);
+	MSGLN("  page spare size: %d", conf_page_spare_size);
+	MSGLN("  pages per block: %d", conf_pages_per_block);
+	MSGLN("  total blocks: %d", conf_total_blocks);
+	MSGLN("  ecc option: %d (%s)", conf_ecc_option, g_ecc_option_strings[conf_ecc_option]);
+	MSGLN("  ecc size: %d%s", conf_ecc_size, conf_ecc_size == 0 ? " (auto)" : "");
+	MSGLN("  bad block status offset: %d", conf_status_byte_offset);
+	MSGLN("");
+}
+
+#ifdef UNIX
+#include <execinfo.h>
+#include <signal.h>
+void crash_handler(int sig)
+{
+  void *array[10];
+  size_t size;
+
+  // get void*'s for all entries on the stack
+  size = backtrace(array, 10);
+
+  // print out all the frames to stderr
+  fprintf(stderr, "Error: signal %d:\n", sig);
+  backtrace_symbols_fd(array, size, 2);
+  exit(1);
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+	int ret;
+
+#ifdef UNIX
+	signal(SIGSEGV, crash_handler);
+#endif
+
+	uffs_SetupDebugOutput(); 	// setup debug output as early as possible
+
+	if (parse_options(argc, argv) < 0) {
+		return -1;
+	}
+	
+	if (conf_verbose_mode) {
+		#if 1
+		MSGLN("Internal data structure size:");
+		MSGLN("  TreeNode: %d", sizeof(TreeNode));
+		MSGLN("  struct BlockListSt: %d", sizeof(struct BlockListSt));
+		MSGLN("  struct DirhSt: %d", sizeof(struct DirhSt));
+		MSGLN("  struct FilehSt: %d", sizeof(struct FilehSt));
+		MSGLN("  struct FdataSt: %d", sizeof(struct FdataSt));
+		MSGLN("  struct uffs_TagStoreSt: %d", sizeof(struct uffs_TagStoreSt));
+		MSGLN("  uffs_Buf: %d", sizeof(uffs_Buf));
+		MSGLN("  struct uffs_BlockInfoSt: %d", sizeof(struct uffs_BlockInfoSt));
+		MSGLN("");
+		#endif
+		print_params();
+		print_mount_points();
+	}
+
+	// setup file emulator storage with parameters from command line
+	setup_storage(femu_GetStorage());
+
+	// setup file emulator private data
+	setup_emu_private(femu_GetPrivate());
+
+	ret = init_uffs_fs();
+	if (ret != 0) {
+		MSGLN ("Init file system fail: %d", ret);
+		return -1;
+	}
+
+	cli_add_commandset(get_helper_cmds());
+	cli_add_commandset(get_test_cmds());
+	if (conf_command_line_mode) {
+		if (conf_exec_script) {
+			cli_interpret(script_command);
+		}
+		cli_main_entry();
+	}
+	else {
+		if (conf_exec_script) {
+			cli_interpret(script_command);
+		}
+		else {
+			cli_main_entry();
+		}
+	}
+
+	release_uffs_fs();
+
+	return 0;
+}
+#endif
+
+
+

+ 25 - 0
tools/chomp_uffs_perror.rb

@@ -0,0 +1,25 @@
+#usage:
+#	find . -name "*.c" | xargs /usr/bin/ruby path/to/chomp_uffs_perror.rb
+#
+
+ARGV.each do |file|
+	lines = []
+	count = 0
+	File.open(file).each_line do |line|
+		if line =~ /^(.*uffs_Perror.+)PFX(\s*".+)$/
+			lines << ($1 + $2)
+			count += 1
+                        #puts ($1 + $2)
+		else
+			lines << line
+		end
+	end
+	if count > 0
+		f = File.open(file, "w")
+		lines.each do |s|
+			f.puts s
+		end
+		f.close
+		puts "Fix file #{file}, modified lines: #{count}"
+	end
+end

+ 24 - 0
tools/format_code.rb

@@ -0,0 +1,24 @@
+#usage:
+#	find . -name "*.c" | xargs /usr/bin/ruby path/to/format_code.rb
+#
+
+ARGV.each do |file|
+	lines = []
+	count = 0
+	File.open(file).each_line do |line|
+		if line =~ /^(.*)\s$/
+			lines << $1.dup
+			count += 1
+		else
+			lines << line
+		end
+	end
+	if count > 0
+		f = File.open(file, "w")
+		lines.each do |s|
+			f.puts s
+		end
+		f.close
+		puts "Fix file #{file}, modified lines: #{count}"
+	end
+end

+ 3 - 0
tools/make_package.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+git ls-tree --name-only --full-name -r $1 | xargs tar -jcvf uffs-$1.tar.bz2

+ 322 - 0
uffs_config.h

@@ -0,0 +1,322 @@
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/** 
+ * \file uffs_config.h
+ * \brief basic configuration of uffs
+ * \author Ricky Zheng
+ */
+
+#ifndef _UFFS_CONFIG_H_
+#define _UFFS_CONFIG_H_
+
+/**
+ * \def UFFS_MAX_PAGE_SIZE
+ * \note maximum page size UFFS support
+ */
+#define UFFS_MAX_PAGE_SIZE		2048
+
+/**
+ * \def UFFS_MAX_SPARE_SIZE
+ */
+#define UFFS_MAX_SPARE_SIZE ((UFFS_MAX_PAGE_SIZE / 256) * 8)
+
+/**
+ * \def UFFS_MAX_ECC_SIZE
+ */
+#define UFFS_MAX_ECC_SIZE  ((UFFS_MAX_PAGE_SIZE / 256) * 5)
+
+/**
+ * \def MAX_CACHED_BLOCK_INFO
+ * \note uffs cache the block info for opened directories and files,
+ *       a practical value is 5 ~ MAX_OBJECT_HANDLE
+ */
+#define MAX_CACHED_BLOCK_INFO	50
+
+/** 
+ * \def MAX_PAGE_BUFFERS
+ * \note the bigger value will bring better read/write performance.
+ *       but few writing performance will be improved when this 
+ *       value is become larger than 'max pages per block'
+ */
+#define MAX_PAGE_BUFFERS		40
+
+
+/** 
+ * \def CLONE_BUFFER_THRESHOLD
+ * \note reserve buffers for clone. 1 or 2 should be enough.
+ */
+#define CLONE_BUFFERS_THRESHOLD	2
+
+/**
+ * \def MAX_SPARE_BUFFERS
+ * \note spare buffers are used for lower level flash operations,
+ *		 5 should be enough.
+ */
+#define MAX_SPARE_BUFFERS		5
+
+
+/**
+ * \def MAX_DIRTY_PAGES_IN_A_BLOCK 
+ * \note this value should be between '2' and the lesser of
+ *		 'max pages per block' and (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1).
+ *
+ *       the smaller the value the frequently the buffer will be flushed.
+ */
+#define MAX_DIRTY_PAGES_IN_A_BLOCK 32	
+
+/**
+ * \def MAX_DIRTY_BUF_GROUPS
+ */
+#define MAX_DIRTY_BUF_GROUPS	3
+
+/**
+ * \def CONFIG_ENABLE_UFFS_DEBUG_MSG
+ * \note Enable debug message output. You must call uffs_InitDebugMessageOutput()
+ *      to initialize debug apart from enable debug feature.
+ */
+#define CONFIG_ENABLE_UFFS_DEBUG_MSG
+
+/**
+ * \def CONFIG_USE_GLOBAL_FS_LOCK
+ * \note use global lock instead of per-device lock.
+ *       this is required if you use fd APIs in multi-thread environment.
+ */
+#define CONFIG_USE_GLOBAL_FS_LOCK
+
+
+/**
+ * \def CONFIG_USE_PER_DEVICE_LOCK
+ * \note use per-device lock.
+ *		 this is required if you use fs APIs in multi-thread environment.
+ */
+//#define CONFIG_USE_PER_DEVICE_LOCK
+
+
+
+/**
+ * \def CONFIG_USE_STATIC_MEMORY_ALLOCATOR
+ * \note uffs will use static memory allocator if this is defined.
+ *       to use static memory allocator, you need to provide memory
+ *       buffer when creating uffs_Device.
+ *
+ *       use UFFS_STATIC_BUFF_SIZE() to calculate memory buffer size.
+ */
+#define CONFIG_USE_STATIC_MEMORY_ALLOCATOR 0
+
+/**
+ * \def CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR
+ * \note  using system platform's 'malloc' and 'free'.
+ */
+#define CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR 1
+
+
+
+/** 
+ * \def CONFIG_FLUSH_BUF_AFTER_WRITE
+ * \note UFFS will write all data directly into flash in 
+ *       each 'write' call if you enable this option.
+ *       (which means lesser data lost when power failure but
+ *		 poorer writing performance).
+ *		 It's not recommended to open this define for normal applications.
+ */
+#define CONFIG_FLUSH_BUF_AFTER_WRITE
+
+
+/**
+ * \def CONFIG_UFFS_AUTO_LAYOUT_MTD_COMP
+ * \note Use Linux MTD compatiable spare placement for UFFS_LAYOUT_AUTO,
+ *       only valid for page data size 512 or 2048.
+ */
+//#define CONFIG_UFFS_AUTO_LAYOUT_USE_MTD_SCHEME
+
+
+/** 
+ * \def MAX_OBJECT_HANDLE
+ * maximum number of object handle 
+ */
+#define MAX_OBJECT_HANDLE	50
+#define FD_SIGNATURE_SHIFT	6	
+
+
+/**
+ * \def MAX_DIR_HANDLE
+ * maximum number of uffs_DIR
+ */
+#define MAX_DIR_HANDLE 10	
+
+/**
+ * \def MINIMUN_ERASED_BLOCK
+ *  UFFS will not allow appending or creating new files when the free/erased block
+ *  is lower then MINIMUN_ERASED_BLOCK.
+ */
+#define MINIMUN_ERASED_BLOCK 2
+
+/**
+ * \def CONFIG_CHANGE_MODIFY_TIME
+ * \note If defined, closing a file which is opened for writing/appending will
+ *       update the file's modify time as well. Disable this feature will save a
+ *       lot of writing activities if you frequently open files for write and close it.
+ */
+//#define CONFIG_CHANGE_MODIFY_TIME
+
+
+/**
+ * \def CONFIG_ENABLE_BAD_BLOCK_VERIFY
+ * \note allow erase and verify block marked as 'bad' when format UFFS partition.
+ *		it's not recommended for most NAND flash.
+ */
+#define CONFIG_ENABLE_BAD_BLOCK_VERIFY
+
+/**
+ * \def CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+ * \note erase block again before mark bad block
+ */
+//#define CONFIG_ERASE_BLOCK_BEFORE_MARK_BAD
+
+/**
+ * \def CONFIG_PAGE_WRITE_VERIFY
+ * \note verify page data after write, for extra safe data storage.
+ */
+#define CONFIG_PAGE_WRITE_VERIFY
+
+/**
+ * \def CONFIG_BAD_BLOCK_POLICY_STRICT
+ * \note If this is enabled, UFFS will report the block as 'bad' if any bit-flips found;
+ *       otherwise, UFFS report bad block only when ECC failed or reported
+ *		 by low level flash driver.
+ *
+ * \note Enable this will ensure your data always be stored on completely good blocks.
+ */
+#define CONFIG_BAD_BLOCK_POLICY_STRICT
+
+
+/**
+ * \def CONFIG_ENABLE_PAGE_DATA_CRC
+ * \note If this is enabled, UFFS save page data CRC16 sum in mini header,
+ *       it provides extra protection for data integrity.
+ */
+#define CONFIG_ENABLE_PAGE_DATA_CRC
+
+
+/** micros for calculating buffer sizes */
+
+/**
+ *	\def UFFS_BLOCK_INFO_BUFFER_SIZE
+ *	\brief calculate memory bytes for block info caches
+ */
+#define UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block)	\
+			(											\
+				(										\
+					sizeof(uffs_BlockInfo) +			\
+					sizeof(uffs_PageSpare) * n_pages_per_block \
+				 ) * MAX_CACHED_BLOCK_INFO				\
+			)
+
+/**
+ *	\def UFFS_PAGE_BUFFER_SIZE
+ *	\brief calculate memory bytes for page buffers
+ */
+#define UFFS_PAGE_BUFFER_SIZE(n_page_size)	\
+			(								\
+				(							\
+					sizeof(uffs_Buf) + n_page_size	\
+				) * MAX_PAGE_BUFFERS		\
+			)
+
+/**
+ *	\def UFFS_TREE_BUFFER_SIZE
+ *	\brief calculate memory bytes for tree nodes
+ */
+#define UFFS_TREE_BUFFER_SIZE(n_blocks) (sizeof(TreeNode) * n_blocks)
+
+
+#define UFFS_SPARE_BUFFER_SIZE (MAX_SPARE_BUFFERS * UFFS_MAX_SPARE_SIZE)
+
+
+/**
+ *	\def UFFS_STATIC_BUFF_SIZE
+ *	\brief calculate total memory usage of uffs system
+ */
+#define UFFS_STATIC_BUFF_SIZE(n_pages_per_block, n_page_size, n_blocks) \
+			(		\
+				UFFS_BLOCK_INFO_BUFFER_SIZE(n_pages_per_block) + \
+				UFFS_PAGE_BUFFER_SIZE(n_page_size) + \
+				UFFS_TREE_BUFFER_SIZE(n_blocks) + \
+				UFFS_SPARE_BUFFER_SIZE \
+			 )
+
+
+
+/* config check */
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD) < 3
+#error "MAX_PAGE_BUFFERS is too small"
+#endif
+
+#if (MAX_DIRTY_PAGES_IN_A_BLOCK < 2)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should >= 2"
+#endif
+
+#if (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD - 1 < MAX_DIRTY_PAGES_IN_A_BLOCK)
+#error "MAX_DIRTY_PAGES_IN_A_BLOCK should < (MAX_PAGE_BUFFERS - CLONE_BUFFERS_THRESHOLD)"
+#endif
+
+#if defined(CONFIG_PAGE_WRITE_VERIFY) && (CLONE_BUFFERS_THRESHOLD < 2)
+#error "CLONE_BUFFERS_THRESHOLD should >= 2 when CONFIG_PAGE_WRITE_VERIFY is enabled."
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 1
+#error "Please enable ONLY one memory allocator"
+#endif
+
+#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR == 0
+#error "Please enable ONE of memory allocators"
+#endif
+
+#if defined(CONFIG_USE_GLOBAL_FS_LOCK) && defined(CONFIG_USE_PER_DEVICE_LOCK)
+#error "enable either CONFIG_USE_GLOBAL_FS_LOCK or CONFIG_USE_PER_DEVICE_LOCK, not both"
+#endif
+
+#if (MAX_OBJECT_HANDLE > (1 << FD_SIGNATURE_SHIFT))
+#error "Please increase FD_SIGNATURE_SHIFT !"
+#endif
+
+#ifdef WIN32
+# pragma warning(disable : 4996)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4214)
+# pragma warning(disable : 4127)
+# pragma warning(disable : 4389)
+# pragma warning(disable : 4100)
+#endif
+
+#endif

+ 359 - 0
uffs_nandif.c

@@ -0,0 +1,359 @@
+/*
+ * RT-Thread Device Interface for uffs
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "dfs_uffs.h"
+
+static int nand_init_flash(uffs_Device *dev)
+{
+    return UFFS_FLASH_NO_ERR;
+}
+
+static int nand_release_flash(uffs_Device *dev)
+{
+    return UFFS_FLASH_NO_ERR;
+}
+static int nand_erase_block(uffs_Device *dev, unsigned block)
+{
+    int res;
+
+    res = rt_mtd_nand_erase_block(RT_MTD_NAND_DEVICE(dev->_private), block);
+
+    return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
+}
+
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+static int nand_check_block(uffs_Device *dev, unsigned block)
+{
+    int res;
+
+    res = rt_mtd_nand_check_block(RT_MTD_NAND_DEVICE(dev->_private), block);
+
+    return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
+}
+
+static int nand_mark_badblock(uffs_Device *dev, unsigned block)
+{
+    int res;
+
+    res = rt_mtd_nand_mark_badblock(RT_MTD_NAND_DEVICE(dev->_private), block);
+
+    return res == RT_EOK ? UFFS_FLASH_NO_ERR : UFFS_FLASH_IO_ERR;
+}
+#endif
+
+#if (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_NONE) || (RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_SOFT)
+static int nand_read_page(uffs_Device *dev,
+                          u32          block,
+                          u32          page,
+                          u8          *data,
+                          int          data_len,
+                          u8          *ecc,
+                          rt_uint8_t  *spare,
+                          int          spare_len)
+{
+    int res;
+
+    page = block * dev->attr->pages_per_block + page;
+    if (data == NULL && spare == NULL)
+    {
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+        RT_ASSERT(0); //should not be here
+#else
+        /* check block status: bad or good */
+        rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
+
+        rt_memset(spare, 0, UFFS_MAX_SPARE_SIZE);
+
+        rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
+                         page, RT_NULL, 0,
+                         spare, dev->attr->spare_size);//dev->mem.spare_data_size
+
+        res = spare[dev->attr->block_status_offs] == 0xFF ?
+                               UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
+
+        return res;
+#endif
+    }
+
+    rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
+                     page, data, data_len, spare, spare_len);
+
+    return UFFS_FLASH_NO_ERR;
+}
+
+static int nand_write_page(uffs_Device *dev,
+                           u32          block,
+                           u32          page,
+                           const u8    *data,
+                           int          data_len,
+                           const u8    *spare,
+                           int          spare_len)
+{
+    int res;
+
+    RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
+
+    page = block * dev->attr->pages_per_block + page;
+
+    if (data == NULL && spare == NULL)
+    {
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+        RT_ASSERT(0); //should not be here
+#else
+        /* mark bad block  */
+        rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
+
+        rt_memset(spare, 0xFF, UFFS_MAX_SPARE_SIZE);
+        spare[dev->attr->block_status_offs] =  0x00;
+
+        res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
+                                page, RT_NULL, 0,
+                                spare, dev->attr->spare_size);//dev->mem.spare_data_size
+        if (res != RT_EOK)
+            goto __error;
+#endif
+    }
+
+    res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
+                           page,  data, data_len, spare, spare_len);
+    if (res != RT_EOK)
+        goto __error;
+
+    return UFFS_FLASH_NO_ERR;
+
+__error:
+    return UFFS_FLASH_IO_ERR;
+}
+
+const uffs_FlashOps nand_ops =
+{
+    nand_init_flash,    /* InitFlash() */
+    nand_release_flash, /* ReleaseFlash() */
+    nand_read_page,     /* ReadPage() */
+    NULL,               /* ReadPageWithLayout */
+    nand_write_page,    /* WritePage() */
+    NULL,               /* WritePageWithLayout */
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+    nand_check_block,
+    nand_mark_badblock,
+#else
+    NULL,               /* IsBadBlock(), let UFFS take care of it. */
+    NULL,               /* MarkBadBlock(), let UFFS take care of it. */
+#endif
+    nand_erase_block,   /* EraseBlock() */
+};
+
+void uffs_setup_storage(struct uffs_StorageAttrSt *attr,
+                        struct rt_mtd_nand_device *nand)
+{
+    rt_memset(attr, 0, sizeof(struct uffs_StorageAttrSt));
+
+//  attr->total_blocks = nand->end_block - nand->start_block + 1;/* no use */
+    attr->page_data_size = nand->page_size;                /* page data size */
+    attr->pages_per_block = nand->pages_per_block;         /* pages per block */
+    attr->spare_size = nand->oob_size;                     /* page spare size */
+    attr->ecc_opt = RT_CONFIG_UFFS_ECC_MODE;               /* ecc option */
+    attr->ecc_size = 0;                                    /* ecc size is 0 , the uffs will calculate the ecc size*/
+    attr->block_status_offs = attr->ecc_size;              /* indicate block bad or good, offset in spare */
+    attr->layout_opt = RT_CONFIG_UFFS_LAYOUT;              /* let UFFS do the spare layout */
+}
+
+#elif  RT_CONFIG_UFFS_ECC_MODE == UFFS_ECC_HW_AUTO
+static int WritePageWithLayout(uffs_Device         *dev,
+                               u32                  block,
+                               u32                  page,
+                               const u8            *data,
+                               int                  data_len,
+                               const u8            *ecc,  //NULL
+                               const uffs_TagStore *ts)
+{
+    int res;
+    int spare_len;
+    rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
+
+    RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
+
+    page = block * dev->attr->pages_per_block + page;
+    spare_len = dev->mem.spare_data_size;
+
+    if (data == NULL && ts == NULL)
+    {
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+        RT_ASSERT(0); //should not be here
+#else
+        /* mark bad block  */
+        rt_memset(spare, 0xFF, UFFS_MAX_SPARE_SIZE);
+        spare[dev->attr->block_status_offs] =  0x00;
+
+        res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
+                                page, RT_NULL, 0,
+                                spare, dev->attr->spare_size);//dev->mem.spare_data_size
+        if (res != RT_EOK)
+            goto __error;
+
+        dev->st.io_write++;
+        return UFFS_FLASH_NO_ERR;
+#endif
+    }
+
+    if (data != NULL && data_len != 0)
+    {
+        RT_ASSERT(data_len == dev->attr->page_data_size);
+
+        dev->st.page_write_count++;
+        dev->st.io_write += data_len;
+    }
+
+    if (ts != RT_NULL)
+    {
+        uffs_FlashMakeSpare(dev, ts, RT_NULL, (u8 *)spare);
+        dev->st.spare_write_count++;
+        dev->st.io_write += spare_len;
+    }
+
+    res = rt_mtd_nand_write(RT_MTD_NAND_DEVICE(dev->_private),
+                            page, data, data_len, spare, spare_len);
+    if (res != RT_EOK)
+        goto __error;
+
+    return UFFS_FLASH_NO_ERR;
+
+__error:
+    return UFFS_FLASH_IO_ERR;
+}
+
+static URET ReadPageWithLayout(uffs_Device   *dev,
+                               u32            block,
+                               u32            page,
+                               u8            *data,
+                               int            data_len,
+                               u8            *ecc,              //NULL
+                               uffs_TagStore *ts,
+                               u8            *ecc_store)        //NULL
+{
+    int res = UFFS_FLASH_NO_ERR;
+    int spare_len;
+    rt_uint8_t spare[UFFS_MAX_SPARE_SIZE];
+
+    RT_ASSERT(UFFS_MAX_SPARE_SIZE >= dev->attr->spare_size);
+
+    page = block * dev->attr->pages_per_block + page;
+    spare_len = dev->mem.spare_data_size;
+
+    if (data == RT_NULL && ts == RT_NULL)
+    {
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+        RT_ASSERT(0); //should not be here
+#else
+        /* check block good or bad */
+
+        rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
+                         page, RT_NULL, 0,
+                         spare, dev->attr->spare_size);//dev->mem.spare_data_size
+
+        dev->st.io_read++;
+
+        res = spare[dev->attr->block_status_offs] == 0xFF ?
+                               UFFS_FLASH_NO_ERR : UFFS_FLASH_BAD_BLK;
+        return res;
+#endif
+    }
+
+    if (data != RT_NULL)
+    {
+        dev->st.io_read += data_len;
+        dev->st.page_read_count++;
+    }
+
+    res = rt_mtd_nand_read(RT_MTD_NAND_DEVICE(dev->_private),
+                           page, data, data_len, spare, spare_len);
+    if (res == 0)
+        res = UFFS_FLASH_NO_ERR;
+    else if (res == -1)
+    {
+        //TODO ecc correct, add code to use hardware do ecc correct
+        res = UFFS_FLASH_ECC_OK;
+    }
+    else
+        res = UFFS_FLASH_ECC_FAIL;
+
+    if (ts != RT_NULL)
+    {
+        // unload ts and ecc from spare, you can modify it if you like
+        uffs_FlashUnloadSpare(dev, (const u8 *)spare, ts, RT_NULL);
+
+        if ((spare[spare_len - 1] == 0xFF) && (res == UFFS_FLASH_NO_ERR))
+            res = UFFS_FLASH_NOT_SEALED;
+
+        dev->st.io_read += spare_len;
+        dev->st.spare_read_count++;
+    }
+
+    return res;
+}
+
+const uffs_FlashOps nand_ops =
+{
+    nand_init_flash,    /* InitFlash() */
+    nand_release_flash, /* ReleaseFlash() */
+    NULL,               /* ReadPage() */
+    ReadPageWithLayout, /* ReadPageWithLayout */
+    NULL,               /* WritePage() */
+    WritePageWithLayout,/* WritePageWithLayout */
+
+#if defined(RT_UFFS_USE_CHECK_MARK_FUNCITON)
+    nand_check_block,
+    nand_mark_badblock,
+#else
+    NULL,               /* IsBadBlock(), let UFFS take care of it. */
+    NULL,               /* MarkBadBlock(), let UFFS take care of it. */
+#endif
+    nand_erase_block,   /* EraseBlock() */
+};
+
+static rt_uint8_t hw_flash_data_layout[UFFS_SPARE_LAYOUT_SIZE] =
+{
+    0x05, 0x08, 0xFF, 0x00
+};
+
+static rt_uint8_t hw_flash_ecc_layout[UFFS_SPARE_LAYOUT_SIZE] =
+{
+    0x00, 0x04, 0xFF, 0x00
+};
+
+RT_WEAK void uffs_setup_storage(struct uffs_StorageAttrSt *attr,
+                        struct rt_mtd_nand_device *nand)
+{
+    rt_memset(attr, 0, sizeof(struct uffs_StorageAttrSt));
+
+//  attr->total_blocks = nand->end_block - nand->start_block + 1;/* no use */
+    attr->page_data_size = nand->page_size;                /* page data size */
+    attr->pages_per_block = nand->pages_per_block;         /* pages per block */
+    attr->spare_size = nand->oob_size;                     /* page spare size */
+    attr->ecc_opt = RT_CONFIG_UFFS_ECC_MODE;               /* ecc option */
+    attr->ecc_size = nand->oob_size-nand->oob_free;        /* ecc size */
+    attr->block_status_offs = attr->ecc_size;              /* indicate block bad or good, offset in spare */
+    attr->layout_opt = RT_CONFIG_UFFS_LAYOUT;              /* let UFFS do the spare layout */
+
+    /* calculate the ecc layout array */
+    hw_flash_data_layout[0] = attr->ecc_size + 1; /* ecc size + 1byte block status */
+    hw_flash_data_layout[1] = 0x08;
+    hw_flash_data_layout[2] = 0xFF;
+    hw_flash_data_layout[3] = 0x00;
+
+    hw_flash_ecc_layout[0] = 0;
+    hw_flash_ecc_layout[1] = attr->ecc_size;
+    hw_flash_ecc_layout[2] = 0xFF;
+    hw_flash_ecc_layout[3] = 0x00;
+
+    /* initialize  _uffs_data_layout and _uffs_ecc_layout */
+    rt_memcpy(attr->_uffs_data_layout, hw_flash_data_layout, UFFS_SPARE_LAYOUT_SIZE);
+    rt_memcpy(attr->_uffs_ecc_layout, hw_flash_ecc_layout, UFFS_SPARE_LAYOUT_SIZE);
+
+    attr->data_layout = attr->_uffs_data_layout;
+    attr->ecc_layout = attr->_uffs_ecc_layout;
+}
+#endif

+ 158 - 0
uffs_rtthread.c

@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ */
+/*
+  This file is part of UFFS, the Ultra-low-cost Flash File System.
+  
+  Copyright (C) 2005-2009 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
+
+  UFFS is free software; you can redistribute it and/or modify it under
+  the GNU Library General Public License as published by the Free Software 
+  Foundation; either version 2 of the License, or (at your option) any
+  later version.
+
+  UFFS 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
+  or GNU Library General Public License, as applicable, for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  and GNU Library General Public License along with UFFS; if not, write
+  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA  02110-1301, USA.
+
+  As a special exception, if other files instantiate templates or use
+  macros or inline functions from this file, or you compile this file
+  and link it with other works to produce a work based on this file,
+  this file does not by itself cause the resulting work to be covered
+  by the GNU General Public License. However the source code for this
+  file must still be made available in accordance with section (3) of
+  the GNU General Public License v2.
+ 
+  This exception does not invalidate any other reasons why a work based
+  on this file might be covered by the GNU General Public License.
+*/
+
+/**
+ * \file uffs_os_posix.c
+ * \brief Emulation on POSIX host. This is just a dumb implementation, does not really create semaphores.
+ * \author Ricky Zheng
+ */
+
+#include "uffs_config.h"
+#include "uffs/uffs_os.h"
+#include "uffs/uffs_public.h"
+//#include <time.h>
+
+#define PFX "os  : "
+
+int uffs_SemCreate(OSSEM *sem)
+{
+	static int count = 0;
+	char name [RT_NAME_MAX+1];
+	struct rt_mutex *mutex = RT_NULL;
+
+	rt_snprintf(name, sizeof(name), "usem%d", count++);
+	mutex = rt_mutex_create(name, RT_IPC_FLAG_FIFO);
+	if (mutex != RT_NULL)
+	{
+		*sem = (OSSEM *)mutex;
+		return 0;
+	}
+	uffs_Perror(UFFS_MSG_SERIOUS, "can't get a semphore");
+	return -1;
+}
+
+int uffs_SemWait(OSSEM sem)
+{
+	return rt_mutex_take((struct rt_mutex *)sem, RT_WAITING_FOREVER);
+}
+
+int uffs_SemSignal(OSSEM sem)
+{
+	return rt_mutex_release((struct rt_mutex *)sem);
+}
+
+int uffs_SemDelete(OSSEM *sem)
+{
+	int ret = -1;
+	
+	if (sem) {
+		ret = rt_mutex_delete((struct rt_mutex *)(*sem));
+		if (ret == RT_EOK) {
+			*sem = 0;
+		}			
+	}
+	return ret;
+}
+
+int uffs_OSGetTaskId(void)
+{
+	//TODO: ... return current task ID ...
+	return 0;
+}
+
+unsigned int uffs_GetCurDateTime(void)
+{
+	// FIXME: return system time, please modify this for your platform ! 
+	//			or just return 0 if you don't care about file time.
+#if 0
+	time_t tvalue;
+
+	tvalue = time(NULL);
+	
+	return (unsigned int)tvalue;
+#endif
+	return 0;
+}
+
+#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0
+static void * sys_malloc(struct uffs_DeviceSt *dev, unsigned int size)
+{
+	dev = dev;
+	uffs_Perror(UFFS_MSG_NORMAL, "system memory alloc %d bytes", size);
+	return rt_malloc(size);
+}
+
+static URET sys_free(struct uffs_DeviceSt *dev, void *p)
+{
+	dev = dev;
+	rt_free(p);
+	return U_SUCC;
+}
+
+void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator)
+{
+	allocator->malloc = sys_malloc;
+	allocator->free = sys_free;
+}
+#endif
+
+#if !defined(RT_THREAD)
+/* debug message output throught 'printf' */
+static void output_dbg_msg(const char *msg);
+static struct uffs_DebugMsgOutputSt m_dbg_ops = {
+	output_dbg_msg,
+	NULL,
+};
+
+static void output_dbg_msg(const char *msg)
+{
+	rt_kprintf("%s", msg);
+}
+
+void uffs_SetupDebugOutput(void)
+{
+	uffs_InitDebugMessageOutput(&m_dbg_ops, UFFS_MSG_NOISY);
+}
+#else
+
+void uffs_SetupDebugOutput(void)
+{
+}
+#endif