diff --git a/components/dfs/filesystems/uffs/AUTHORS b/components/dfs/filesystems/uffs/AUTHORS new file mode 100644 index 0000000000..adaedefb4f --- /dev/null +++ b/components/dfs/filesystems/uffs/AUTHORS @@ -0,0 +1 @@ +Ricky Zheng diff --git a/components/dfs/filesystems/uffs/CMakeLists.txt b/components/dfs/filesystems/uffs/CMakeLists.txt new file mode 100644 index 0000000000..96f5a1ca5f --- /dev/null +++ b/components/dfs/filesystems/uffs/CMakeLists.txt @@ -0,0 +1,3 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6 ) +PROJECT( uffs ) +ADD_SUBDIRECTORY( src ) \ No newline at end of file diff --git a/components/dfs/filesystems/uffs/COPYING b/components/dfs/filesystems/uffs/COPYING new file mode 100644 index 0000000000..d511905c16 --- /dev/null +++ b/components/dfs/filesystems/uffs/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. + + + Copyright (C) + + 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. + + , 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. diff --git a/components/dfs/filesystems/uffs/Doxyfile b/components/dfs/filesystems/uffs/Doxyfile new file mode 100644 index 0000000000..f46a4c0af1 --- /dev/null +++ b/components/dfs/filesystems/uffs/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 diff --git a/components/dfs/filesystems/uffs/README b/components/dfs/filesystems/uffs/README new file mode 100644 index 0000000000..00e56cab4b --- /dev/null +++ b/components/dfs/filesystems/uffs/README @@ -0,0 +1,213 @@ +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 + +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 along with uffs source dir, for example: +/+ + +--build/ + +--uffs-1.2.0/ + | + +2) create Makefiles and build: + cd build + cmake ../uffs-1.2.0 + make + +5) run simulator (interactive mode): + src/utils/mkuffs + + +BUILD SIMULATOR ON WINDOWS +-------------------------- + +1) create a 'build' dir along with uffs source dir, +/+ + +--build/ + +--uffs-1.2.0/ + | + +2) Create VC project files: + cd build + cmake ../uffs-1.2.0 + +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. + +LICENCE +------- + From v1.2.1, UFFS is released under a midified 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. + +ACKNOWLEDGMENT +--------------- +Special thanks for your contributions to: +(list in no particular order) + +* Chen Jun +* Michail +* Sjpu +* RobertGray +* Dongbo +* Cag +* Sergey +* Chris Conrad +* Vladimir +* Thien Pham +* Emmanuel Blot +* Michael diff --git a/components/dfs/filesystems/uffs/TODO b/components/dfs/filesystems/uffs/TODO new file mode 100644 index 0000000000..bf6b4fb66a --- /dev/null +++ b/components/dfs/filesystems/uffs/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 diff --git a/components/dfs/filesystems/uffs/doc/Understanding-UFFS.odp b/components/dfs/filesystems/uffs/doc/Understanding-UFFS.odp new file mode 100644 index 0000000000..2cd1735818 Binary files /dev/null and b/components/dfs/filesystems/uffs/doc/Understanding-UFFS.odp differ diff --git a/components/dfs/filesystems/uffs/doc/Understanding-UFFS.pdf b/components/dfs/filesystems/uffs/doc/Understanding-UFFS.pdf new file mode 100644 index 0000000000..489022b3dc Binary files /dev/null and b/components/dfs/filesystems/uffs/doc/Understanding-UFFS.pdf differ diff --git a/components/dfs/filesystems/uffs/doc/uffs-serial-num-relationship.JPG b/components/dfs/filesystems/uffs/doc/uffs-serial-num-relationship.JPG new file mode 100644 index 0000000000..2cdbbcd2b2 Binary files /dev/null and b/components/dfs/filesystems/uffs/doc/uffs-serial-num-relationship.JPG differ diff --git a/components/dfs/filesystems/uffs/src/CMakeLists.txt b/components/dfs/filesystems/uffs/src/CMakeLists.txt new file mode 100644 index 0000000000..0cfc66832a --- /dev/null +++ b/components/dfs/filesystems/uffs/src/CMakeLists.txt @@ -0,0 +1,4 @@ +ADD_SUBDIRECTORY(emu) +ADD_SUBDIRECTORY(uffs) +ADD_SUBDIRECTORY(utils) +ADD_SUBDIRECTORY(example) diff --git a/components/dfs/filesystems/uffs/src/emu/CMakeLists.txt b/components/dfs/filesystems/uffs/src/emu/CMakeLists.txt new file mode 100644 index 0000000000..7e2d7c5e90 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/CMakeLists.txt @@ -0,0 +1,5 @@ +SET(libemu_SRCS cmdline.c cmdline.h helper_cmds.c uffs_fileem.c uffs_fileem.h uffs_os_posix.c test_cmds.c) + +INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc) +ADD_LIBRARY(emu STATIC ${libemu_SRCS} ) + diff --git a/components/dfs/filesystems/uffs/src/emu/cmdline.c b/components/dfs/filesystems/uffs/src/emu/cmdline.c new file mode 100644 index 0000000000..e8596b2954 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/cmdline.c @@ -0,0 +1,265 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +//#include +#include "cmdline.h" +#include "uffs/uffs_fs.h" + +#define PROMPT "UFFS>" + + +static BOOL m_exit = FALSE; +static struct cli_commandset cmdset[200] = {0}; +static int m_cmdCount = 0; +static char str_buf[128]; + +const char * cli_getparam(const char *tail, const char **next) +{ + char *p; + + if (tail == NULL) + return NULL; + + strcpy(str_buf, tail); + p = str_buf; + + while (*tail != ' ' && *tail != 0) { + tail++; p++; + } + + if (*tail == ' ') { + *p = 0; + while(*tail == ' ') + tail++; + } + + if(next) + *next = (char *)(*tail ? tail : NULL); + + return str_buf; +} + + +static BOOL cmdExit(const char *tail) +{ + m_exit = TRUE; + return TRUE; +} + +static BOOL cmdHelp(const char *tail); + + +static struct cli_commandset default_cmdset[] = +{ + { cmdHelp, "help|?", "[]", "Show commands or help on one command" }, + { cmdExit, "exit", NULL, "exit command line" }, + { NULL, NULL, NULL, NULL } +}; + +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 int cmdFind(const char *cmd) +{ + int icmd; + + for (icmd = 0; cmdset[icmd].cmd != NULL; icmd++) { + //printf("cmdFind: Check cmd: %s with %s\n", cmd, cmdset[icmd].cmd); + if (check_cmd(cmdset[icmd].cmd, cmd) == TRUE) + return icmd; + } + return -1; +} + + +static BOOL cmdHelp(const char *tail) +{ + int icmd; + + if (tail == NULL) { + printf("Available commands:\n"); + for (icmd = 0; cmdset[icmd].cmd != NULL; icmd++) { + int i; + printf("%s", cmdset[icmd].cmd); + for (i = strlen(cmdset[icmd].cmd); i%10; i++,printf(" ")); + + //if ((icmd & 7) == 7 || cmdset[icmd+1].cmd == NULL) printf("\n"); + } + printf("\n"); + } + else { + icmd = cmdFind(tail); + if (icmd < 0) { + printf("No such command\n"); + } + else { + printf("%s: %s\n", cmdset[icmd].cmd, cmdset[icmd].descr); + printf("Usage: %s %s\n", cmdset[icmd].cmd, cmdset[icmd].args); + } + } + + return TRUE; +} + + +void cliInterpret(const char *line) +{ + char cmd[64]; + const char *tail; + const char *psep; + int icmd; + + psep = strchr(line, ' '); + cmd[0] = 0; + + if (psep == NULL) { + strncat(cmd, line, sizeof(cmd) - 1); + tail = NULL; + } + else { + strncat(cmd, line, psep - line); + for (tail = psep; *tail == ' '; ++tail); + if (*tail == 0) + tail = NULL; + } + + icmd = cmdFind(cmd); + + if (icmd < 0) { + printf("Unknown command - try help\n"); + return; + } + else { + //printf("Command idx: %d\n", icmd); + if (!cmdset[icmd].handler(tail)) { + cmdHelp(cmd); + } + } +} + +void cli_add_commandset(struct cli_commandset *cmds) +{ + int icmd; + + for (icmd = 0; cmds[icmd].cmd != NULL; icmd++) { + memcpy(&(cmdset[m_cmdCount++]), &(cmds[icmd]), sizeof(struct cli_commandset)); + } +} + +void cliMain() +{ + char line[80]; + int linelen = 0; + + printf("$ "); + cli_add_commandset(default_cmdset); + + while (!m_exit) { + char ch; + ch = getc(stdin); + switch (ch) { + case 8: + case 127: + if (linelen > 0) { + --linelen; + printf("\x08 \x08"); + } + break; + + case '\r': + case '\n': + //printf("\r\n"); + if (linelen > 0) { + line[linelen] = 0; + cliInterpret(line); + } + linelen = 0; + printf("$ "); + break; + + case 21: + while (linelen > 0) { + --linelen; + printf("\x08 \x08"); + } + break; + + default: + if (ch >= ' ' && ch < 127 && linelen < sizeof(line) - 1) { + line[linelen++] = ch; + //printf("%c", ch); + } + } + } +} diff --git a/components/dfs/filesystems/uffs/src/emu/cmdline.h b/components/dfs/filesystems/uffs/src/emu/cmdline.h new file mode 100644 index 0000000000..b71a10f5ab --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/cmdline.h @@ -0,0 +1,64 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + +typedef BOOL command_t(const char *tail); + +struct cli_commandset { + command_t *handler; + const char *cmd; + const char *args; + const char *descr; +}; + +const char * cli_getparam(const char *tail, const char **next); +void cli_add_commandset(struct cli_commandset *cmds); +void cliInterpret(const char *line); +void cliMain(); + +#endif + + diff --git a/components/dfs/filesystems/uffs/src/emu/helper_cmds.c b/components/dfs/filesystems/uffs/src/emu/helper_cmds.c new file mode 100644 index 0000000000..1aeb5e0b67 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/helper_cmds.c @@ -0,0 +1,854 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +#include +#include "uffs/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" + +#define PFX "cmd: " + + +#define MAX_PATH_LENGTH 128 + + +BOOL cmdFormat(const char *tail) +{ + URET ret; + const char *mount = "/"; + uffs_Device *dev; + + if (tail) { + mount = cli_getparam(tail, NULL); + } + uffs_Perror(UFFS_ERR_NORMAL, "Formating %s ... ", mount); + + dev = uffs_GetDeviceFromMountPoint(mount); + if (dev == NULL) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't get device from mount point."); + } + else { + if (dev->ref_count == 1) { + ret = uffs_FormatDevice(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_NORMAL, "Format fail."); + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "Format succ."); + } + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "dev->ref_count: %d, can't format this device.", dev->ref_count); + } + uffs_PutDevice(dev); + } + return TRUE; +} + +BOOL cmdMkf(const char *tail) +{ + int fd; + const char *name; + int oflags = UO_RDWR | UO_CREATE; + + if (tail == NULL) { + return FALSE; + } + + name = cli_getparam(tail, NULL); + fd = uffs_open(name, oflags); + if (fd < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Create %s fail, err: %d", name, uffs_get_error()); + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "Create %s succ.", name); + uffs_close(fd); + } + + return TRUE; +} + +BOOL cmdMkdir(const char *tail) +{ + const char *name; + + if (tail == NULL) { + return FALSE; + } + + name = cli_getparam(tail, NULL); + + if (uffs_mkdir(name) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Create %s fail, err: %d", name, uffs_get_error()); + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "Create %s succ.", name); + } + return TRUE; +} + + +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; +} + +BOOL cmdPwd(const char *tail) +{ + uffs_Perror(UFFS_ERR_NORMAL, "not supported."); + return TRUE; +} + +BOOL cmdCd(const char *tail) +{ + uffs_Perror(UFFS_ERR_NORMAL, "Not supported"); + return TRUE; +} + +BOOL cmdLs(const char *tail) +{ + uffs_DIR *dirp; + struct uffs_dirent *ent; + struct uffs_stat stat_buf; + int count = 0; + char buf[MAX_PATH_LENGTH+2]; + char *name = (char *)tail; + char *sub; + + if (name == NULL) { + uffs_Perror(UFFS_ERR_NORMAL, "Must provide file/dir name."); + return FALSE; + } + + dirp = uffs_opendir(name); + if (dirp == NULL) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open '%s' for list", name); + } + else { + uffs_PerrorRaw(UFFS_ERR_NORMAL, "------name-----------size---------serial-----" TENDSTR); + ent = uffs_readdir(dirp); + while (ent) { + uffs_PerrorRaw(UFFS_ERR_NORMAL, "%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, "/"); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "/ \t<%8d>", CountObjectUnder(sub)); + } + else { + uffs_stat(sub, &stat_buf); + uffs_PerrorRaw(UFFS_ERR_NORMAL, " \t %8d ", stat_buf.st_size); + } + uffs_PerrorRaw(UFFS_ERR_NORMAL, "\t%6d" TENDSTR, ent->d_ino); + count++; + ent = uffs_readdir(dirp); + } + + uffs_closedir(dirp); + + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Total: %d objects." TENDSTR, count); + } + + return TRUE; +} + + +BOOL cmdRm(const char *tail) +{ + const char *name = NULL; + int ret = 0; + struct uffs_stat st; + + if (tail == NULL) return FALSE; + + name = cli_getparam(tail, NULL); + + if (uffs_stat(name, &st) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't stat '%s'", name); + return TRUE; + } + + if (st.st_mode & US_IFDIR) { + ret = uffs_rmdir(name); + } + else { + ret = uffs_remove(name); + } + + if (ret == 0) + uffs_Perror(UFFS_ERR_NORMAL, "Delete '%s' succ.", name); + else + uffs_Perror(UFFS_ERR_NORMAL, "Delete '%s' fail!", name); + + return TRUE; +} + +BOOL cmdRen(const char *tail) +{ + const char *oldname; + const char *newname; + + if (tail == NULL) + return FALSE; + oldname = cli_getparam(tail, &newname); + if (newname == NULL) + return FALSE; + + if (uffs_rename(oldname, newname) == 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Rename from '%s' to '%s' succ.", oldname, newname); + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "Rename from '%s' to '%s' fail!", oldname, newname); + } + return TRUE; +} + +BOOL cmdSt(const char *tail) +{ + uffs_Device *dev; + const char *mount = "/"; + uffs_FlashStat *s; + TreeNode *node; + + if (tail) { + mount = cli_getparam(tail, NULL); + } + + dev = uffs_GetDeviceFromMountPoint(mount); + if (dev == NULL) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't get device from mount point %s", mount); + return TRUE; + } + + s = &(dev->st); + + uffs_PerrorRaw(UFFS_ERR_NORMAL, "----------- basic info -----------" TENDSTR); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "TreeNode size: %d" TENDSTR, sizeof(TreeNode)); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "TagStore size: %d" TENDSTR, sizeof(struct uffs_TagStoreSt)); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxCachedBlockInfo: %d" TENDSTR, MAX_CACHED_BLOCK_INFO); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxPageBuffers: %d" TENDSTR, MAX_PAGE_BUFFERS); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxDirtyPagesPerBlock: %d" TENDSTR, MAX_DIRTY_PAGES_IN_A_BLOCK); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxPathLength: %d" TENDSTR, MAX_PATH_LENGTH); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxObjectHandles: %d" TENDSTR, MAX_OBJECT_HANDLE); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "FreeObjectHandles: %d" TENDSTR, uffs_PoolGetFreeCount(uffs_GetObjectPool())); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "MaxDirHandles: %d" TENDSTR, MAX_DIR_HANDLE); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "FreeDirHandles: %d" TENDSTR, uffs_PoolGetFreeCount(uffs_GetDirEntryBufPool())); + + uffs_PerrorRaw(UFFS_ERR_NORMAL, "----------- statistics for '%s' -----------" TENDSTR, mount); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Block Erased: %d" TENDSTR, s->block_erase_count); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Write Page: %d" TENDSTR, s->page_write_count); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Write Spare: %d" TENDSTR, s->spare_write_count); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Read Page: %d" TENDSTR, s->page_read_count - s->page_header_read_count); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Read Header: %d" TENDSTR, s->page_header_read_count); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Read Spare: %d" TENDSTR, s->spare_read_count); + + uffs_PerrorRaw(UFFS_ERR_NORMAL, "--------- partition info for '%s' ---------" TENDSTR, mount); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Space total: %d" TENDSTR, uffs_GetDeviceTotal(dev)); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Space used: %d" TENDSTR, uffs_GetDeviceUsed(dev)); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Space free: %d" TENDSTR, uffs_GetDeviceFree(dev)); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Page Size: %d" TENDSTR, dev->attr->page_data_size); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Spare Size: %d" TENDSTR, dev->attr->spare_size); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Pages Per Block: %d" TENDSTR, dev->attr->pages_per_block); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Block size: %d" TENDSTR, dev->attr->page_data_size * dev->attr->pages_per_block); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Total blocks: %d of %d" TENDSTR, (dev->par.end - dev->par.start + 1), dev->attr->total_blocks); + if (dev->tree.bad) { + uffs_PerrorRaw(UFFS_ERR_NORMAL, "Bad blocks: "); + node = dev->tree.bad; + while(node) { + uffs_PerrorRaw(UFFS_ERR_NORMAL, "%d, ", node->u.list.block); + node = node->u.list.next; + } + uffs_PerrorRaw(UFFS_ERR_NORMAL, TENDSTR); + } + + uffs_BufInspect(dev); + + uffs_PutDevice(dev); + + return TRUE; + +} + + +BOOL cmdCp(const char *tail) +{ + 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; + + if (!tail) + return FALSE; + + src = cli_getparam(tail, &des); + + if (!des) + return FALSE; + + 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) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", src); + goto fail_ext; + } + } + else { + if ((fd1 = uffs_open(src, UO_RDONLY)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", src); + goto fail_ext; + } + } + + if (des_local) { + if ((fp2 = fopen(des, "wb")) == NULL) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", des); + goto fail_ext; + } + } + else { + if ((fd2 = uffs_open(des, UO_RDWR|UO_CREATE|UO_TRUNC)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s for copy.", des); + goto fail_ext; + } + } + + while ( (src_local ? (feof(fp1) == 0) : (uffs_eof(fd1) == 0)) ) { + if (src_local) { + len = fread(buf, 1, sizeof(buf), fp1); + } + else { + len = uffs_read(fd1, buf, sizeof(buf)); + } + if (len == 0) + break; + if (len < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "read file %s fail ?", src); + break; + } + if (des_local) { + if ((int)fwrite(buf, 1, len, fp2) != len) { + uffs_Perror(UFFS_ERR_NORMAL, "write file %s fail ? ", des); + break; + } + } + else { + if (uffs_write(fd2, buf, len) != len) { + uffs_Perror(UFFS_ERR_NORMAL, "write file %s fail ? ", des); + break; + } + } + } + +fail_ext: + if (fd1 > 0) + uffs_close(fd1); + if (fd2 > 0) + uffs_close(fd2); + if (fp1) + fclose(fp1); + if (fp2) + fclose(fp2); + + return TRUE; +} + +BOOL cmdCat(const char *tail) +{ + int fd; + const char *name; + const char *next; + char buf[100]; + int start = 0, size = 0, printed = 0, n, len; + + if (!tail) + return FALSE; + + name = cli_getparam(tail, &next); + + if ((fd = uffs_open(name, UO_RDONLY)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s", name); + goto fail; + } + + if (next) { + start = strtol(next, (char **) &next, 10); + if (next) size = strtol(next, 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; + uffs_PerrorRaw(UFFS_ERR_NORMAL, "%s", buf); + printed += n; + } + else { + break; + } + } + } + uffs_PerrorRaw(UFFS_ERR_NORMAL, TENDSTR); + uffs_close(fd); + +fail: + + return TRUE; +} + + +static URET test_verify_file(const char *file_name) +{ + int fd; + int ret = U_FAIL; + unsigned char buf[100]; + int i, pos, len; + + if ((fd = uffs_open(file_name, UO_RDONLY)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "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++) { + if (buf[i] != (pos++ & 0xFF)) { + pos--; + uffs_Perror(UFFS_ERR_NORMAL, "Verify file %s failed at: %d, expect %x but got %x", file_name, pos, pos & 0xFF, buf[i]); + goto test_failed; + } + } + } + + if (pos != uffs_seek(fd, 0, USEEK_END)) { + uffs_Perror(UFFS_ERR_NORMAL, "Verify file %s failed. invalid file length.", file_name); + goto test_failed; + } + + uffs_Perror(UFFS_ERR_NORMAL, "Verify file %s succ.", file_name); + ret = U_SUCC; + +test_failed: + uffs_close(fd); + +test_exit: + + return ret; +} + +static URET do_write_test_file(int fd, int size) +{ + long pos; + unsigned char buf[100]; + unsigned char data; + int i, len; + + while (size > 0) { + pos = uffs_seek(fd, 0, USEEK_CUR); + len = (size > sizeof(buf) ? sizeof(buf) : size); + data = pos & 0xFF; + for (i = 0; i < len; i++, data++) { + buf[i] = data; + } + if (uffs_write(fd, buf, len) != len) { + uffs_Perror(UFFS_ERR_NORMAL, "Write file failed, size %d at %d", len, pos); + return U_FAIL; + } + size -= len; + } + + return U_SUCC; +} + +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) { + uffs_Perror(UFFS_ERR_NORMAL, "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) { + uffs_Perror(UFFS_ERR_NORMAL, "Write file %s failed.", file_name); + goto test_failed; + } + ret = U_SUCC; + +test_failed: + uffs_close(fd); + +test_exit: + + return ret; +} + +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) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open file %s for write.", file_name); + goto test_exit; + } + + if (uffs_seek(fd, pos, USEEK_SET) != pos) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't seek file %s at pos %d", file_name, pos); + goto test_failed; + } + + if (do_write_test_file(fd, size) == U_FAIL) { + uffs_Perror(UFFS_ERR_NORMAL, "Write file %s failed.", file_name); + goto test_failed; + } + ret = U_SUCC; + +test_failed: + uffs_close(fd); + +test_exit: + + return ret; +} + + +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) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open dir abc, err: %d", uffs_get_error()); + uffs_Perror(UFFS_ERR_NORMAL, "Try to create a new one..."); + fd = uffs_open("/abc/", UO_RDWR|UO_CREATE|UO_DIR); + if (fd < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "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) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open /abc/test.txt"); + goto exit_test; + } + + sprintf(buf, "123456789ABCDEF"); + ret = uffs_write(fd, buf, strlen(buf)); + uffs_Perror(UFFS_ERR_NORMAL, "write %d bytes to file, content: %s", ret, buf); + + ret = uffs_seek(fd, 3, USEEK_SET); + uffs_Perror(UFFS_ERR_NORMAL, "new file position: %d", ret); + + memset(buf_1, 0, sizeof(buf_1)); + ret = uffs_read(fd, buf_1, 5); + uffs_Perror(UFFS_ERR_NORMAL, "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; +} + +/* test create file, write file and read back */ +BOOL cmdTest1(const char *tail) +{ + int fd; + URET ret; + char buf[100]; + const char *name; + + if (!tail) { + return FALSE; + } + + name = cli_getparam(tail, NULL); + + fd = uffs_open(name, UO_RDWR|UO_CREATE|UO_TRUNC); + if (fd < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s", name); + goto fail; + } + + sprintf(buf, "123456789ABCDEF"); + ret = uffs_write(fd, buf, strlen(buf)); + uffs_Perror(UFFS_ERR_NORMAL, "write %d bytes to file, content: %s", ret, buf); + + ret = uffs_seek(fd, 3, USEEK_SET); + uffs_Perror(UFFS_ERR_NORMAL, "new file position: %d", ret); + + memset(buf, 0, sizeof(buf)); + ret = uffs_read(fd, buf, 5); + uffs_Perror(UFFS_ERR_NORMAL, "read %d bytes, content: %s", ret, buf); + + uffs_close(fd); + +fail: + + return TRUE; +} + +BOOL cmdTest2(const char *tail) +{ + uffs_Perror(UFFS_ERR_NORMAL, "Test return: %s !", DoTest2() == U_SUCC ? "succ" : "failed"); + + return TRUE; +} + +/* Test file append and 'random' write */ +BOOL cmdTest3(const char *tail) +{ + const char *name; + int i; + int write_test_seq[] = { 20, 10, 500, 40, 1140, 900, 329, 4560, 352, 1100 }; + + if (!tail) { + return FALSE; + } + + name = cli_getparam(tail, NULL); + uffs_Perror(UFFS_ERR_NORMAL, "Test append file %s ...", name); + for (i = 1; i < 500; i += 29) { + if (test_append_file(name, i) != U_SUCC) { + uffs_Perror(UFFS_ERR_NORMAL, "Append file %s test failed at %d !", name, i); + return TRUE; + } + } + + uffs_Perror(UFFS_ERR_NORMAL, "Check file %s ... ", name); + if (test_verify_file(name) != U_SUCC) { + uffs_Perror(UFFS_ERR_NORMAL, "Verify file %s failed.", name); + return TRUE; + } + + uffs_Perror(UFFS_ERR_NORMAL, "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) { + uffs_Perror(UFFS_ERR_NORMAL, "Test write file failed !"); + return TRUE; + } + } + + uffs_Perror(UFFS_ERR_NORMAL, "Check file %s ... ", name); + if (test_verify_file(name) != U_SUCC) { + uffs_Perror(UFFS_ERR_NORMAL, "Verify file %s failed.", name); + return TRUE; + } + + uffs_Perror(UFFS_ERR_NORMAL, "Test succ !"); + + return TRUE; +} + +/* open two files and test write */ +BOOL cmdTest4(const char *tail) +{ + int fd1 = -1, fd2 = -1; + + uffs_Perror(UFFS_ERR_NORMAL, "open /a ..."); + if ((fd1 = uffs_open("/a", UO_RDWR | UO_CREATE)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open /a"); + goto fail_exit; + } + + uffs_Perror(UFFS_ERR_NORMAL, "open /b ..."); + if ((fd2 = uffs_open("/b", UO_RDWR | UO_CREATE)) < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open /b"); + uffs_close(fd1); + goto fail_exit; + } + + uffs_Perror(UFFS_ERR_NORMAL, "write (1) to /a ..."); + uffs_write(fd1, "Hello,", 6); + uffs_Perror(UFFS_ERR_NORMAL, "write (1) to /b ..."); + uffs_write(fd2, "Hello,", 6); + uffs_Perror(UFFS_ERR_NORMAL, "write (2) to /a ..."); + uffs_write(fd1, "World.", 6); + uffs_Perror(UFFS_ERR_NORMAL, "write (2) to /b ..."); + uffs_write(fd2, "World.", 6); + uffs_Perror(UFFS_ERR_NORMAL, "close /a ..."); + uffs_close(fd1); + uffs_Perror(UFFS_ERR_NORMAL, "close /b ..."); + uffs_close(fd2); + + return TRUE; + +fail_exit: + return TRUE; +} + +/* test appending file */ +BOOL cmdTest5(const char *tail) +{ + int fd = -1; + URET ret; + char buf[100]; + const char *name; + + if (!tail) { + return FALSE; + } + + name = cli_getparam(tail, NULL); + + fd = uffs_open(name, UO_RDWR|UO_APPEND); + if (fd < 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Can't open %s", name); + goto fail; + } + + sprintf(buf, "append test..."); + ret = uffs_write(fd, buf, strlen(buf)); + if (ret != strlen(buf)) { + uffs_Perror(UFFS_ERR_NORMAL, "write file failed, %d/%d", ret, strlen(buf)); + } + else { + uffs_Perror(UFFS_ERR_NORMAL, "write %d bytes to file, content: %s", ret, buf); + } + + uffs_close(fd); + +fail: + + return TRUE; +} + + + +BOOL cmdMount(const char *tail) +{ + uffs_MountTable *tab = uffs_GetMountTable(); + tail = tail; + + while (tab) { + uffs_Perror(UFFS_ERR_NORMAL, " %s : (%d) ~ (%d)", tab->mount, tab->start_block, tab->end_block); + tab = tab->next; + } + + return TRUE; +} + +static struct cli_commandset cmdset[] = +{ + { cmdFormat, "format", "[]", "Format device" }, + { cmdMkf, "mkfile", "", "create a new file" }, + { cmdMkdir, "mkdir", "", "create a new directory" }, + { cmdRm, "rm", "", "delete file/directory" }, + { cmdRen, "mv|ren", " ", "rename file/directory" }, + { cmdLs, "ls", "", "list dirs and files" }, + { cmdSt, "info|st", "", "show statistic infomation" }, + { cmdTest1, "t1", "", "test 1" }, + { cmdTest2, "t2", NULL, "test 2" }, + { cmdTest3, "t3", "", "test 3" }, + { cmdTest4, "t4", NULL, "test 4" }, + { cmdTest5, "t5", "", "test 5" }, + { cmdCp, "cp", " ", "copy files. the local file name start with '::'" }, + { cmdCat, "cat", "", "show file content" }, + { cmdPwd, "pwd", NULL, "show current dir" }, + { cmdCd, "cd", "", "change current dir" }, + { cmdMount, "mount", NULL, "list mounted file systems" }, + + { NULL, NULL, NULL, NULL } +}; + + +struct cli_commandset * get_helper_cmds() +{ + return cmdset; +}; diff --git a/components/dfs/filesystems/uffs/src/emu/test_cmds.c b/components/dfs/filesystems/uffs/src/emu/test_cmds.c new file mode 100644 index 0000000000..97ac133d26 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/test_cmds.c @@ -0,0 +1,172 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +#include +#include "uffs/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 "uffs/uffs_badblock.h" +#include "cmdline.h" + +#define PFX "test:" + + +static BOOL cmdTestPageReadWrite(const char *tail) +{ + TreeNode *node; + uffs_Device *dev; + uffs_Tags local_tag; + uffs_Tags *tag = &local_tag; + int ret; + u16 block; + u16 page; + uffs_Buf *buf; + + u32 i; + + dev = uffs_GetDeviceFromMountPoint("/"); + if (!dev) + goto ext; + + buf = uffs_BufClone(dev, NULL); + if (!buf) + goto ext; + + node = uffs_TreeGetErasedNode(dev); + if (!node) { + uffs_Perror(UFFS_ERR_SERIOUS, "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_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; + + ret = uffs_FlashWritePageCombine(dev, block, page, buf, tag); + if (UFFS_FLASH_HAVE_ERR(ret)) { + uffs_Perror(UFFS_ERR_SERIOUS, "Write page error: %d", ret); + goto ext; + } + + ret = uffs_FlashReadPage(dev, block, page, buf); + if (UFFS_FLASH_HAVE_ERR(ret)) { + uffs_Perror(UFFS_ERR_SERIOUS, "Read page error: %d", ret); + goto ext; + } + + for (i = 0; i < dev->com.pg_data_size; i++) { + if (buf->data[i] != (i & 0xFF)) { + uffs_Perror(UFFS_ERR_SERIOUS, "Data verify fail at: %d", i); + goto ext; + } + } + + ret = uffs_FlashReadPageSpare(dev, block, page, tag, NULL); + if (UFFS_FLASH_HAVE_ERR(ret)) { + uffs_Perror(UFFS_ERR_SERIOUS, "Read tag (page spare) error: %d", ret); + goto ext; + } + + // verify tag: + if (!TAG_IS_DIRTY(tag)) { + uffs_Perror(UFFS_ERR_SERIOUS, "not dirty ? Tag verify fail!"); + goto ext; + } + + if (!TAG_IS_VALID(tag)) { + uffs_Perror(UFFS_ERR_SERIOUS, "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) { + + uffs_Perror(UFFS_ERR_SERIOUS, "Tag verify fail!"); + goto ext; + } + + uffs_Perror(UFFS_ERR_SERIOUS, "Page read/write test succ."); + +ext: + if (node) { + uffs_FlashEraseBlock(dev, node->u.list.block); + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockProcess(dev, node); + else + uffs_InsertToErasedListHead(dev, node); + } + + if (dev) + uffs_PutDevice(dev); + + if (buf) + uffs_BufFreeClone(dev, buf); + + return TRUE; +} + +static struct cli_commandset cmdset[] = +{ + { cmdTestPageReadWrite, "t_pgrw", NULL, "test page read/write" }, + { NULL, NULL, NULL, NULL } +}; + + +struct cli_commandset * get_test_cmds() +{ + return cmdset; +}; diff --git a/components/dfs/filesystems/uffs/src/emu/uffs_fileem.c b/components/dfs/filesystems/uffs/src/emu/uffs_fileem.c new file mode 100644 index 0000000000..f49dc61c08 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/uffs_fileem.c @@ -0,0 +1,475 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +#include +#include + +#include "uffs/uffs_device.h" +#include "uffs_fileem.h" + +#define PFX "femu: " + + +#define MAXWRITETIME_PAGE 1 +#define MAXWRITETIME_SPARE 1 + +#define FEMU_MAX_SPARE_SIZE UFFS_MAX_SPARE_SIZE + +static u8 em_page_buf[UFFS_MAX_PAGE_SIZE + UFFS_MAX_SPARE_SIZE]; + + +static URET emu_initDevice(uffs_Device *dev); + + +static URET CheckInit(uffs_Device *dev) +{ + int i; + int fSize; + int written; + u8 * p = em_page_buf; + uffs_FileEmu *emu; + + int pg_size, pgd_size, sp_size, blks, blk_pgs, blk_size; + 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; + + emu = (uffs_FileEmu *)(dev->attr->_private); + + if (emu->initCount > 0) { + emu->initCount++; + return U_SUCC; + } + + if (dev->attr->ecc_opt != UFFS_ECC_NONE && + dev->attr->ecc_opt != UFFS_ECC_SOFT) { + return U_FAIL; //!< file emulator don't support HW ECC. + } + + emu->em_monitor_page = (u8 *) malloc(dev->attr->total_blocks * dev->attr->pages_per_block); + if (!emu->em_monitor_page) + return U_FAIL; + emu->em_monitor_spare = (u8 *) malloc(dev->attr->total_blocks * dev->attr->pages_per_block); + if (!emu->em_monitor_spare) + return U_FAIL; + + + //clear monitor + memset(emu->em_monitor_page, 0, blks * blk_pgs); + memset(emu->em_monitor_spare, 0, blks * blk_pgs); + + 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 U_FAIL; + } + + fseek(emu->fp, 0, SEEK_END); + fSize = ftell(emu->fp); + + if (fSize < blk_size * blks) { + printf("Creating uffs emulation file\n"); + fseek(emu->fp, 0, SEEK_SET); + memset(p, 0xff, pgd_size + sp_size); + for (i = 0; i < blk_pgs * blks; i++) { + written = fwrite(p, 1, pgd_size + sp_size, emu->fp); + if (written != pgd_size + sp_size) { + printf("Write failed\n"); + fclose(emu->fp); + emu->fp = NULL; + return U_FAIL; + } + } + } + } + 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 U_FAIL; + } + + emu->initCount++; + + return U_SUCC; +} + + +static int femu_WritePageData(uffs_Device *dev, u32 block, u32 page_num, const u8 *data, int len, u8 *ecc) +{ + int written; + 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; + + if (len > pgd_size) { + printf("femu: write page data out of range!\n"); + goto err; + } + + emu->em_monitor_page[block * blk_pgs + page_num]++; + if (emu->em_monitor_page[block * blk_pgs + page_num] > MAXWRITETIME_PAGE) { + printf("Warrning: block %d page %d exceed it's maximum write time!\r\n", block, page_num); + goto err; + } + + if (data) { + fseek(emu->fp, + (block * blk_pgs + page_num) * + (pgd_size + sp_size), SEEK_SET); + + written = fwrite(data, 1, len, emu->fp); + + if (written != len) { + printf("femu: write page I/O error ?\n"); + goto err; + } + } + + dev->st.page_write_count++; + + return UFFS_FLASH_NO_ERR; +err: + return UFFS_FLASH_IO_ERR; +} + +static int femu_WritePageSpare(uffs_Device *dev, u32 block, u32 page_num, const u8 *spare, int ofs, int len, UBOOL eod) +{ + int written; + 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("WS: %d/%d, size %d\n", block, page_num, len); + + if (len > sp_size) { + printf("femu: write page data out of range!\n"); + goto err; + } + + emu->em_monitor_spare[block*blk_pgs + page_num]++; + if (emu->em_monitor_spare[block*blk_pgs + page_num] > MAXWRITETIME_SPARE) { + printf("Warrning: block %d page %d (spare) exceed it's maximum write time!\r\n", block, page_num); + goto err; + } + + if (spare) { + + // simulate power lost ! produce an unclean page. + if (0 && block == 3 && page_num == 2) { + fflush(emu->fp); + exit(1); + } + + fseek(emu->fp, (block*blk_pgs + page_num) * (pgd_size + sp_size) + dev->attr->page_data_size + ofs, SEEK_SET); + written = fwrite(spare, 1, len, emu->fp); + if (written != len) { + printf("femu: write spare I/O error ?\n"); + goto err; + } + } + + if (eod == U_TRUE) { + // eod: U_TRUE -- single write cycle + // eod: U_FALSE -- this is the write after page data + } + fflush(emu->fp); + + dev->st.spare_write_count++; + + return UFFS_FLASH_NO_ERR; +err: + return UFFS_FLASH_IO_ERR; +} + +static URET femu_ReadPageData(uffs_Device *dev, u32 block, u32 page_num, u8 *data, int len, u8 *ecc) +{ + int nread; + 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; + + if (len > pgd_size) { + printf("femu: read page data out of range!\n"); + goto err; + } + + if (data) { + fseek(emu->fp, (block*blk_pgs + page_num) * (pgd_size + sp_size), SEEK_SET); + nread = fread(data, 1, len, emu->fp); + + // for ECC testing. + if (1 && block == 2 && page_num == 3 && len > 13) { + printf("--- ECC error inject to block %d page %d ---\n", block, page_num); + data[13] = (data[13] & ~0x40) | (~(data[13] & 0x40) & 0x40) ; + } + + if (nread != len) { + printf("femu: read page I/O error ?\n"); + goto err; + } + } + + dev->st.page_read_count++; + + return UFFS_FLASH_NO_ERR; +err: + return UFFS_FLASH_IO_ERR; +} + + + +static URET femu_ReadPageSpare(uffs_Device *dev, u32 block, u32 page_num, u8 *spare, int ofs, int len) +{ + int nread; + int pos; + 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("RS: %d/%d, size %d\n", block, page_num, len); + + if (len > sp_size) { + printf("femu: read page spare out of range!\n"); + goto err; + } + + if (spare) { + pos = (block*blk_pgs + page_num) * (pgd_size + sp_size) + dev->attr->page_data_size + ofs; + if (fseek(emu->fp, pos, SEEK_SET) != 0) { + printf("femu: seek to %d fail!\n", pos); + goto err; + } + nread= fread(spare, 1, len, emu->fp); + + if (nread != len) { + printf("femu: read spare I/O error ?\n"); + goto err; + } + } + + dev->st.spare_read_count++; + + return UFFS_FLASH_NO_ERR; +err: + return UFFS_FLASH_IO_ERR; +} + +static URET femu_EraseBlock(uffs_Device *dev, u32 blockNumber) +{ + + int i; + u8 * pg = em_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)); + + if (1 && (blockNumber == 5)) { // simulate bad block + return UFFS_FLASH_BAD_BLK; + } + + 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; + +} + + +///////////////////////////////////////////////////////////////////////////////// +#if GCC +static uffs_FlashOps emu_flash_ops = { + .ReadPageData = femu_ReadPageData, + .ReadPageSpare = femu_ReadPageSpare, + .ReadPageSpareLayout = NULL, + .WritePageData = femu_WritePageData, + .WritePageSpare = femu_WritePageSpare, + .WritePageSpareLayout = NULL, + .IsBadBlock = NULL, + .MarkBadBlock = NULL, + .EraseBlock = femu_EraseBlock, +}; +#else +static uffs_FlashOps emu_flash_ops = { + femu_ReadPageData, + femu_ReadPageSpare, + NULL, //!< ReadPageSpareLayout, let UFFS do layout + femu_WritePageData, + femu_WritePageSpare, + NULL, //!< WritePageSpareLayout, let UFFS do layout + NULL, //!< IsBadBlock(), let UFFS take care of it. + NULL, //!< MarkBadBlock(), let UFFS take care of it. + femu_EraseBlock, +}; +#endif + +static URET femu_initDevice(uffs_Device *dev) +{ + uffs_Perror(UFFS_ERR_NORMAL, "femu device init."); + + dev->ops = &emu_flash_ops; /* EMU device operations */ + + CheckInit(dev); + + return U_SUCC; +} + +static URET femu_releaseDevice(uffs_Device *dev) +{ + uffs_FileEmu *emu; + + uffs_Perror(UFFS_ERR_NORMAL, "femu device release."); + + emu = (uffs_FileEmu *)(dev->attr->_private); + + emu->initCount--; + if (emu->initCount == 0) { + if (emu->fp) { + fclose(emu->fp); + emu->fp = NULL; + } + + memset(emu, 0, sizeof(uffs_FileEmu)); + + if (emu->em_monitor_page) + free(emu->em_monitor_page); + if (emu->em_monitor_spare) + free(emu->em_monitor_spare); + emu->em_monitor_page = NULL; + emu->em_monitor_spare = NULL; + } + + return U_SUCC; +} + + +void uffs_fileem_setup_device(uffs_Device *dev) +{ + dev->Init = femu_initDevice; + dev->Release = femu_releaseDevice; +} + +///////////////////////////////////////////////////////////////////////////////// diff --git a/components/dfs/filesystems/uffs/src/emu/uffs_fileem.h b/components/dfs/filesystems/uffs/src/emu/uffs_fileem.h new file mode 100644 index 0000000000..72ecbe0eb2 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/uffs_fileem.h @@ -0,0 +1,55 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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" + +typedef struct uffs_FileEmuSt { + int initCount; + FILE *fp; + u8 *em_monitor_page; + u8 * em_monitor_spare; + const char *emu_filename; +} uffs_FileEmu; + +void uffs_fileem_setup_device(uffs_Device *dev); + +#endif + diff --git a/components/dfs/filesystems/uffs/src/emu/uffs_os_posix.c b/components/dfs/filesystems/uffs/src/emu/uffs_os_posix.c new file mode 100644 index 0000000000..780cc91dfc --- /dev/null +++ b/components/dfs/filesystems/uffs/src/emu/uffs_os_posix.c @@ -0,0 +1,104 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_linux.c + * \brief emulation on linux host + * \author Ricky Zheng + */ +#include "uffs/uffs_os.h" +#include "uffs/uffs_public.h" +#include +#include +#include + +#define PFX "linuxemu:" + +int uffs_SemCreate(int n) +{ + //TODO: ... create semaphore, return semaphore handler (rather then return n) ... + return n; +} + +int uffs_SemWait(int sem) +{ + if (sem) { + //TODO: ... wait semaphore available ... + } + return 0; +} + +int uffs_SemSignal(int sem) +{ + if (sem) { + //TODO: ... release semaphore ... + } + return 0; +} + +int uffs_SemDelete(int sem) +{ + if (sem) { + //TODO: ... delete semaphore ... + } + return 0; +} + +int uffs_OSGetTaskId(void) +{ + //TODO: ... return current task ID ... + return 0; +} + + +void uffs_CriticalEnter(void) +{ + //TODO: enter critical section (for example, disable IRQ?) + return; +} + +void uffs_CriticalExit(void) +{ + //TODO: exit from critical section (for example, enable IRQ?) + return; +} + +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; +} diff --git a/components/dfs/filesystems/uffs/src/example/CMakeLists.txt b/components/dfs/filesystems/uffs/src/example/CMakeLists.txt new file mode 100644 index 0000000000..24e9aecfba --- /dev/null +++ b/components/dfs/filesystems/uffs/src/example/CMakeLists.txt @@ -0,0 +1,12 @@ +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) +TARGET_LINK_LIBRARIES(flash-if-example emu uffs emu) diff --git a/components/dfs/filesystems/uffs/src/example/flash-interface-example.c b/components/dfs/filesystems/uffs/src/example/flash-interface-example.c new file mode 100644 index 0000000000..b7427e2921 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/example/flash-interface-example.c @@ -0,0 +1,310 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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.my_application_main_entry + + 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 + +#include "uffs/uffs_device.h" +#include "uffs/uffs_flash.h" +#include "uffs/uffs_mtb.h" +#include "uffs/uffs_fs.h" + +#define PFX "nand-drv:" + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR == 0 +int main() +{ + uffs_Perror(UFFS_ERR_NORMAL, "This example need CONFIG_USE_STATIC_MEMORY_ALLOCATOR = 1"); + return 0; +} +#else + + +#define USE_SINGLE_WRITE_FUN + + +#ifdef USE_SINGLE_WRITE_FUN +static int nand_write_full_page(uffs_Device *dev, u32 block, u32 pageNum, const u8 *page, int len, const u8 *tag, int tag_len, const u8 *ecc); +#else +static int nand_write_page_data(uffs_Device *dev, u32 block, u32 pageNum, const u8 *page, int len, u8 *ecc); +static int nand_write_page_spare(uffs_Device *dev, u32 block, u32 pageNum, const u8 *spare, int ofs, int len, UBOOL eod); +#endif + +static int nand_read_page_data(uffs_Device *dev, u32 block, u32 pageNum, u8 *page, int len, u8 *ecc); +static int nand_read_page_spare(uffs_Device *dev, u32 block, u32 pageNum, u8 *spare, int ofs, int len); + +static int nand_erase_block(uffs_Device *dev, u32 blockNumber); + +static URET nand_init_device(uffs_Device *dev); + + +#ifdef USE_SINGLE_WRITE_FUN +// if you want to optimize nand flash driver, or use special nand hardware controller, +// or use other NAND driver (for example, eCos NAND lib), you shoud do layout in nand driver. +static int nand_write_full_page(uffs_Device *dev, u32 block, u32 pageNum, const u8 *page, int len, const u8 *tag, int tag_len, const u8 *ecc) +{ +#define SPOOL(dev) &((dev)->mem.spare_pool) + + u8 *spare_buf = NULL; + + spare_buf = (u8 *) uffs_PoolGet(SPOOL(dev)); // alloc a spare buffer + + // ... START WRITE COMMAND ... + // ... + + if (page) { + // WRITE page data + // .... + if (dev->attr->ecc_opt == UFFS_ECC_HW) { + // read ECC from hardware controller to ecc buf, + // ... + } + } + + if (tag && tag_len > 0) { + + // now, you can use UFFS's layout function + uffs_FlashMakeSpare(dev, (uffs_TagStore *)tag, ecc, spare_buf); + // or, do your own layout + // .... + + // WRITE spare_buf to page spare ... + // ... + } + + // FINISH write command ... + // ... + // read program status ... + // ... + + if (page) + dev->st.page_write_count++; + if (tag) + dev->st.spare_write_count++; + + if (spare_buf) + uffs_PoolPut(SPOOL(dev), spare_buf); // release spare buffer + + return UFFS_FLASH_NO_ERR; +} + +#else + +static int nand_write_page_data(uffs_Device *dev, u32 block, u32 pageNum, const u8 *page, int len, u8 *ecc) +{ + // send WRITE command + + // ... transfer data ... + + dev->st.page_write_count++; + return UFFS_FLASH_NO_ERR; +} + + +static int nand_write_page_spare(uffs_Device *dev, u32 block, u32 pageNum, const u8 *spare, int ofs, int len, UBOOL eod) +{ + if (eod == U_FALSE) { + // send WRITE command + } + else { + // do not need to send WRITE command if eod == U_FALSE because 'nand_write_page_data' is called before. + } + + // ... transfer data ... + + // send COMMIT command + + // read STATUS + + dev->st.spare_write_count++; + return UFFS_FLASH_NO_ERR; +} + +#endif + + +static int nand_read_page_data(uffs_Device *dev, u32 block, u32 pageNum, u8 *page, int len, u8 *ecc) +{ + // send READ command + + // ... transfer data ... + + // read STATUS + + dev->st.page_read_count++; + return UFFS_FLASH_NO_ERR; +} + +static int nand_read_page_spare(uffs_Device *dev, u32 block, u32 pageNum, u8 *spare, int ofs, int len) +{ + // send READ command + + // ... transfer data ... + + // read STATUS + + dev->st.spare_read_count++; + return UFFS_FLASH_NO_ERR; +} + + +static int nand_erase_block(uffs_Device *dev, u32 blockNumber) +{ + // insert your nand driver codes here ... + + dev->st.block_erase_count++; + return UFFS_FLASH_NO_ERR; +} + + +///////////////////////////////////////////////////////////////////////////////// + +static struct uffs_FlashOpsSt my_nand_driver_ops = { + nand_read_page_data, //ReadPageData + nand_read_page_spare, //ReadPageSpare + NULL, //ReadPageSpareWithLayout +#ifdef USE_SINGLE_WRITE_FUN + NULL, + NULL, + nand_write_full_page, //WriteFullPages +#else + nand_write_page_data, //WritePageData + nand_write_page_spare, //WritePageSpare + NULL, +#endif + NULL, //IsBadBlock + NULL, //MarkBadBlock + nand_erase_block, //EraseBlock +}; + +// change these parameters to fit your nand flash specification +#define MAN_ID MAN_ID_SAMSUNG // simulate Samsung's NAND flash + +#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 */ + +static struct uffs_StorageAttrSt flash_storage = {0}; + +/* 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 setup_flash_storage(struct uffs_StorageAttrSt *attr) +{ + memset(attr, 0, sizeof(struct uffs_StorageAttrSt)); + + 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->ops = &my_nand_driver_ops; + + return U_SUCC; +} + +static URET my_releaseDevice(uffs_Device *dev) +{ + return U_SUCC; +} + +/* 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 int my_init_filesystem(void) +{ + uffs_MountTable *mtbl = &(demo_mount_table[0]); + + /* setup nand storage attributes */ + setup_flash_storage(&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) { + mtbl->dev->Init = my_initDevice; + mtbl->dev->Release = my_releaseDevice; + mtbl->dev->attr = &flash_storage; + uffs_RegisterMountTable(mtbl); + mtbl++; + } + + return uffs_InitMountTable() == U_SUCC ? 0 : -1; +} + +/* application entry */ +int main() +{ + my_init_filesystem(); + + // ... my application codes .... + // read/write/create/delete files ... + + uffs_ReleaseMountTable(); + + return 0; +} + +#endif + + +///////////////////////////////////////////////////////////////////////////////// diff --git a/components/dfs/filesystems/uffs/src/example/static-mem-allocate.c b/components/dfs/filesystems/uffs/src/example/static-mem-allocate.c new file mode 100644 index 0000000000..c0f7d510ea --- /dev/null +++ b/components/dfs/filesystems/uffs/src/example/static-mem-allocate.c @@ -0,0 +1,161 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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. + * \author Ricky Zheng + */ + +#include +#include +#include +#include "uffs/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 "static-example: " + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR == 0 +int main() +{ + uffs_Perror(UFFS_ERR_NORMAL, "This example need CONFIG_USE_STATIC_MEMORY_ALLOCATOR = 1"); + return 0; +} +#else + +extern struct cli_commandset * get_helper_cmds(void); + +#define DEFAULT_EMU_FILENAME "uffsemfile.bin" + +#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 struct uffs_StorageAttrSt emu_storage = {0}; +static struct uffs_FileEmuSt emu_private = {0}; + +/* 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_emu_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; /* ecc option */ + attr->layout_opt = UFFS_LAYOUT_UFFS; /* let UFFS do the spare layout */ + +} + +static void setup_emu_private(uffs_FileEmu *emu) +{ + memset(emu, 0, sizeof(uffs_FileEmu)); + emu->emu_filename = DEFAULT_EMU_FILENAME; +} + +static int init_uffs_fs(void) +{ + struct uffs_MountTableEntrySt *mtbl = &demo_mount; + + /* setup emu storage */ + setup_emu_storage(&emu_storage); + setup_emu_private(&emu_private); + emu_storage._private = &emu_private; + mtbl->dev->attr = &emu_storage; + + /* setup memory allocator */ + uffs_MemSetupStaticAllocator(&mtbl->dev->mem, static_buffer_pool, sizeof(static_buffer_pool)); + + /* setup device */ + uffs_fileem_setup_device(mtbl->dev); + + /* register mount table */ + uffs_RegisterMountTable(mtbl); + + return uffs_InitMountTable() == U_SUCC ? 0 : -1; +} + +static int release_uffs_fs(void) +{ + return uffs_ReleaseMountTable(); +} + +int main(int argc, char *argv[]) +{ + int ret; + + ret = init_uffs_fs(); + + if (ret != 0) { + printf ("Init file system fail: %d\n", ret); + return -1; + } + + cli_add_commandset(get_helper_cmds()); + cliMain(); + + release_uffs_fs(); + + return 0; +} + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs.h new file mode 100644 index 0000000000..f2ef895e09 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs.h @@ -0,0 +1,139 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_ + +#include "uffs/uffs_types.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_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 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 + + + +/** + * \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 32 + +/** \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]; +}; +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; + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_badblock.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_badblock.h new file mode 100644 index 0000000000..c6a5c0b104 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_badblock.h @@ -0,0 +1,70 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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); + +/** 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_blockinfo.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_blockinfo.h new file mode 100644 index 0000000000..fad75d4c2a --- /dev/null +++ b/components/dfs/filesystems/uffs/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 + + 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 + u8 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_buf.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_buf.h new file mode 100644 index 0000000000..c75ca57f29 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_buf.h @@ -0,0 +1,174 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 //!< + +/** 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 + 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 + int ext_mark; //!< extension mark. +}; + +#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); + +/** 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); + +/** 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_config.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_config.h new file mode 100644 index 0000000000..59ba4cd478 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_config.h @@ -0,0 +1,277 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 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 10 + +/** + * \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 10 + + +/** + * \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 7 + +/** + * \def MAX_DIRTY_BUF_GROUPS + */ +#define MAX_DIRTY_BUF_GROUPS 3 + + +/** + * \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_NATIVE_MEMORY_ALLOCATOR + * \note the native memory allocator should only be used for + * tracking memory leak bugs or tracking memory consuming. + * In your final product, you either disable the native memory + * allocator or use the system heap as the memory pool for the + * native memory allocator. + */ +#define CONFIG_USE_NATIVE_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 failue but + * pooer writing performance). + * It's not recommented to open this define for normal applications. + */ +//#define CONFIG_FLUSH_BUF_AFTER_WRITE + +/** + * \def CONFIG_TREE_NODE_USE_DOUBLE_LINK + * \note: enable double link tree node will speed up insert/delete operation, + */ +#define CONFIG_TREE_NODE_USE_DOUBLE_LINK + +/** + * \def MAX_OBJECT_HANDLE + * maximum number of object handle + */ +#define MAX_OBJECT_HANDLE 10 + +/** + * \def MAX_DIR_HANDLE + * maximum number of uffs_DIR + */ +#define MAX_DIR_HANDLE 5 + +/** + * \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 recommented 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 completly good blocks. + */ +#define CONFIG_BAD_BLOCK_POLICY_STRICT + + + +/** 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_NATIVE_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 1 +#error "Please enable ONLY one memory allocator" +#endif + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR + CONFIG_USE_NATIVE_MEMORY_ALLOCATOR + CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR == 0 +#error "Please enable ONE of memory allocators" +#endif + + +#ifdef WIN32 +# pragma warning(disable : 4996) +#endif + +#endif diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_core.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_core.h new file mode 100644 index 0000000000..1f18007f0f --- /dev/null +++ b/components/dfs/filesystems/uffs/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 + + 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_device.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_device.h new file mode 100644 index 0000000000..52a5f559a8 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_device.h @@ -0,0 +1,191 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_config.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 + + + +/** + * \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 partiton +}; + +/** + * \struct uffs_LockSt + * \brief lock stuffs + */ +struct uffs_LockSt { + int 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 initialized 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; +} uffs_FlashStat; + + +/** + * \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 initialization + 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 native memory allocator + u32 ref_count; //!< device reference count + int dev_num; //!< device number (partition number) +}; + +/** create the lock for uffs device */ +URET uffs_DeviceInitLock(uffs_Device *dev); + +/** delete the lock of uffs device */ +URET uffs_DeviceReleaseLock(uffs_Device *dev); + +/** lock uffs device */ +URET uffs_DeviceLock(uffs_Device *dev); + +/** unlock uffs device */ +URET uffs_DeviceUnLock(uffs_Device *dev); + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_ecc.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_ecc.h new file mode 100644 index 0000000000..425bc4ab63 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_ecc.h @@ -0,0 +1,90 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + +#include "uffs/uffs_fs.h" +#include "uffs/uffs_config.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(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(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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fd.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fd.h new file mode 100644 index 0000000000..23e2532649 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fd.h @@ -0,0 +1,150 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + */ + +#include "uffs/uffs_config.h" +#include "uffs/uffs_core.h" +#include "uffs/uffs_fs.h" +#include "uffs/uffs.h" +#include "uffs/uffs_find.h" +#include + +/** + * \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 00400 /* read permission */ +#define US_IWRITE 00200 /* 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 or this record) */ + char d_name[MAX_FILENAME_LENGTH]; /* name of this record */ + + 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 */ +}; + +/** + * \brief POSIX DIR + */ +typedef 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 */ +} 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 */ + u32 st_atime; /* time of last access */ + u32 st_mtime; /* time of last modification */ + u32 st_ctime; /* time of last status change */ +}; + + +URET uffs_InitDirEntryBuf(void); +URET uffs_ReleaseDirEntryBuf(void); +uffs_Pool * uffs_GetDirEntryBufPool(void); + +/* POSIX compliant 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, 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_truncate(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); + +#if 0 +void uffs_seekdir(uffs_DIR *dirp, long loc); +long uffs_telldir(uffs_DIR *dirp); +#endif + +int uffs_get_error(void); +int uffs_set_error(int err); + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_find.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_find.h new file mode 100644 index 0000000000..020d2c9455 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_find.h @@ -0,0 +1,74 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_flash.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_flash.h new file mode 100644 index 0000000000..7740bbd6bc --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_flash.h @@ -0,0 +1,274 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_config.h" +#include "uffs/uffs_core.h" +#include "uffs/uffs_device.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. + + +/** 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 6 //!< 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_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_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) +#else +# define UFFS_FLASH_IS_BAD_BLOCK(e) ((e) == UFFS_FLASH_ECC_FAIL || (e) == UFFS_FLASH_BAD_BLK) +#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) + 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 { + /** + * Read page data. + * + * if ecc_opt is UFFS_ECC_HW, flash driver must calculate and return ecc (if ecc != NULL). + * + * if ecc_opt is UFFS_ECC_HW_AUTO, flash driver do ecc correction aganist ecc in spare area. + * + * \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. + * #UFFS_FLASH_ECC_OK: page data has flip bits and corrected by ecc. + * + * \note pad 0xFF for calculating ECC if len < page_data_size + */ + int (*ReadPageData)(uffs_Device *dev, u32 block, u32 page, u8 *data, int len, u8 *ecc); + + + /** + * Read page spare [len] bytes from [ofs]. + * + * \note flash driver must privide this function. + * + * \return #UFFS_FLASH_NO_ERR: success + * #UFFS_FLASH_IO_ERR: I/O error, expect retry ? + * + * \note flash driver DO NOT need to do ecc correction for spare data, + * UFFS will take care of spare data ecc. + */ + int (*ReadPageSpare)(uffs_Device *dev, u32 block, u32 page, u8 *spare, int ofs, int len); + + /** + * Read page spare, unload to tag and ecc. + * + * \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 ? + * + * \note flash driver DO NOT need to do ecc correction for spare data, + * UFFS will take care of spare data ecc. + */ + int (*ReadPageSpareWithLayout)(uffs_Device *dev, u32 block, u32 page, u8 *tag, int len, u8 *ecc); + + /** + * Write page data. + * + * if ecc_opt is UFFS_ECC_HW, flash driver must calculate and return the ecc. + * if ecc_opt is UFFS_ECC_HW_AUTO, do not need to return ecc. + * + * \return #UFFS_FLASH_NO_ERR: success + * #UFFS_FLASH_IO_ERR: I/O error, expect retry ? + * #UFFS_FLASH_BAD_BLK: a bad block detected. + * + * \note pad 0xFF for calculating ECC if len < page_data_size + */ + int (*WritePageData)(uffs_Device *dev, u32 block, u32 page, const u8 *data, int len, u8 *ecc); + + + /** + * Write [len] bytes to page spare from [ofs]. + * + * \note flash driver must privide 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 (*WritePageSpare)(uffs_Device *dev, u32 block, u32 page, const u8 *spare, int ofs, int len, UBOOL eod); + + /** + * Write full page, include page data and spare. + * + * you need to pack spare within nand driver. + * + * \note if layout_opt is UFFS_LAYOUT_FLASH, flash driver must implement this function. + * UFFS will use this function (if provided) prio to 'WritePageData() + WritePageSpare()' + * + * \return #UFFS_FLASH_NO_ERR: success + * #UFFS_FLASH_IO_ERR: I/O error, expect retry ? + * #UFFS_FLASH_BAD_BLK: a bad block detected. + */ + int (*WriteFullPage)(uffs_Device *dev, u32 block, u32 page, const u8* data, int len, const u8 *ts, int ts_len, const u8 *ecc); + + /** + * 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 provided, 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. + * + * \return 0 if success, otherwise return -1. + */ + int (*MarkBadBlock)(uffs_Device *dev, u32 block); + + /** + * Erase a 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 (*EraseBlock)(uffs_Device *dev, u32 block); +}; + +/** make spare from tag store and ecc */ +void uffs_FlashMakeSpare(uffs_Device *dev, uffs_TagStore *ts, const u8 *ecc, u8* spare); + +/** read page spare, fill tag and ECC */ +int uffs_FlashReadPageSpare(uffs_Device *dev, int block, int page, uffs_Tags *tag, u8 *ecc); + +/** read page data to page buf and do ECC correct */ +int uffs_FlashReadPage(uffs_Device *dev, int block, int page, uffs_Buf *buf); + +/** 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, int block, int page); + +/** + * 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fs.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fs.h new file mode 100644 index 0000000000..157105a520 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_fs.h @@ -0,0 +1,137 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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); +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); + + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mem.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mem.h new file mode 100644 index 0000000000..9d2e2ee3fc --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mem.h @@ -0,0 +1,130 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + +#define MAX_ECC_SIZE (3 * UFFS_MAX_PAGE_SIZE / 256) +#define MAX_SPARE_SIZE (8 * UFFS_MAX_PAGE_SIZE / 256) +#define MAX_SPARE_BUF 10 + + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + +#define HEAP_HASH_BIT 6 /* hash table bit */ +#define HEAP_HASH_SIZE (1 << (HEAP_HASH_BIT - 1)) /* hash table size */ +#define HEAP_HASH_MASK (HEAP_HASH_SIZE - 1) /* hash table mask */ +#define GET_HASH_INDEX(p) ((((unsigned long)(p)) >> 2) & HEAP_HASH_MASK) + +/* memory alloc node */ +typedef struct HeapManagementNodeSt{ + int task_id; /* who alloc this block? it's the caller's task id */ + struct HeapManagementNodeSt * next; /* point to next node */ + void *p; /* point to allocated block */ + int size; /* block size */ +} HeapMm; + +typedef HeapMm* HeapHashTable; + +/** \note: uffs_MemInitHeap should be called before using native memory allocator on each device */ +void uffs_MemInitHeap(void *addr, int size); + +URET uffs_MemInitNativeAllocator(uffs_Device *dev); +int uffs_MemReleaseNativeAllocator(uffs_Device *dev); + +#endif //CONFIG_USE_NATIVE_MEMORY_ALLOCATOR + + +/** uffs native 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 data size, calculated by UFFS. + + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + HeapHashTable tbl[HEAP_HASH_SIZE]; + int count; + int maxused; +#endif + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0 + char *buf_start; + int buf_size; + int pos; +#endif + +} uffs_MemAllocator; + + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 +void uffs_MemSetupNativeAllocator(uffs_MemAllocator *allocator); +#endif + +#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0 +void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator); +#endif + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0 +void uffs_MemSetupStaticAllocator(uffs_MemAllocator *allocator, void *pool, int size); +#endif + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mtb.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mtb.h new file mode 100644 index 0000000000..3309e9d9bf --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_mtb.h @@ -0,0 +1,90 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_config.h" +#include "uffs/uffs_core.h" +#include "uffs/uffs.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + +typedef struct uffs_MountTableEntrySt { + uffs_Device *dev; + int start_block; + int end_block; + const char *mount; + struct uffs_MountTableEntrySt *next; +} uffs_MountTable; + +/** initialize registered mount table */ +URET uffs_InitMountTable(void); + +/** release registered mount table */ +URET uffs_ReleaseMountTable(void); + +/** get registered mount table */ +uffs_MountTable * uffs_GetMountTable(void); + +/** register mount table */ +int uffs_RegisterMountTable(uffs_MountTable *mtab); + +/** 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 diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_os.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_os.h new file mode 100644 index 0000000000..0b63f83755 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_os.h @@ -0,0 +1,65 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + +#ifdef __cplusplus +extern "C"{ +#endif + +#include "uffs/uffs_device.h" +#include "uffs/uffs_core.h" + +#define UFFS_TASK_ID_NOT_EXIST -1 + +typedef int OSSEM; + +/* OS specific functions */ +int uffs_SemCreate(int n); +int uffs_SemWait(int sem); +int uffs_SemSignal(int sem); +int uffs_SemDelete(int sem); + +void uffs_CriticalEnter(void); +void uffs_CriticalExit(void); + +int uffs_OSGetTaskId(void); //get current task id +unsigned int uffs_GetCurDateTime(void); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_pool.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_pool.h new file mode 100644 index 0000000000..5fadd5485f --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_pool.h @@ -0,0 +1,92 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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" + +#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 + int 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); + +#ifdef __cplusplus +} +#endif + +#endif // _UFFS_POOL_H_ diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_public.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_public.h new file mode 100644 index 0000000000..5b22c3b214 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_public.h @@ -0,0 +1,243 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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_config.h" +#include "uffs/uffs_core.h" +#include "uffs/uffs.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +/** + * \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_TagStoreSt_8 + * \brief this data structure describes the page status, for 8 bytes page spare. + * \note there is no tag ecc for this ! + */ +struct uffs_TagStoreSt_8 { + 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 page_id:5; //!< page id + u32 parent:7; //!< parent's serial number + u32 serial:8; //!< serial number + u32 data_len:8; //!< length of page data +}; + +/** + * \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; + + /** + * block_status is not covered by tag_ecc. + * it's loaded from flash but not directly write to flash. + */ + u8 block_status; + + /** internal used */ + u8 _dirty:1; //!< raw data, before doing ecc correction + u8 _valid:1; //!< raw data, before doing ecc correction +}; + +/** + * \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) ((tag)->s.dirty == TAG_DIRTY) +#define TAG_IS_VALID(tag) ((tag)->s.valid == TAG_VALID) +#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 + + +int uffs_GetFirstBlockTimeStamp(void); +int uffs_GetNextBlockTimeStamp(int prev); +UBOOL uffs_IsSrcNewerThanObj(int src, int obj); + + +#include "uffs_device.h" + + + +/********************************** debug & error *************************************/ +#define UFFS_ERR_NOISY -1 +#define UFFS_ERR_NORMAL 0 +#define UFFS_ERR_SERIOUS 1 +#define UFFS_ERR_DEAD 2 + +#define TENDSTR "\n" + +//#define UFFS_DBG_LEVEL UFFS_ERR_NORMAL +#define UFFS_DBG_LEVEL UFFS_ERR_NOISY + +void uffs_DebugMessage(int level, const char *prefix, const char *suffix, const char *errFmt, ...); + +#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__) + + + +void uffs_AssertCall(const char *file, int line, const char *msg); + +#define uffs_Assert(expr, msg) \ + do { \ + if (!(expr)) \ + uffs_AssertCall(__FILE__, __LINE__, msg); \ + } 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) +#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_FindFirstValidPage(uffs_Device *dev, uffs_BlockInfo *bc); +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); + +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); + + +/************************************************************************/ +/* init functions */ +/************************************************************************/ +URET uffs_InitDevice(uffs_Device *dev); +URET uffs_ReleaseDevice(uffs_Device *dev); + + +URET uffs_InitFlashClass(uffs_Device *dev); + + + +#ifdef __cplusplus +} +#endif +#endif // _UFFS_PUBLIC_H_ + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_tree.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_tree.h new file mode 100644 index 0000000000..ff36f2b33a --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_tree.h @@ -0,0 +1,221 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 BlockListSt { /* 10 bytes */ + struct uffs_TreeNodeSt * next; + struct uffs_TreeNodeSt * prev; + u16 block; +}; + +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; +#ifdef CONFIG_TREE_NODE_USE_DOUBLE_LINK + u16 hash_prev; +#endif +} 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 + 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); + +#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_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 + + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_types.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_types.h new file mode 100644 index 0000000000..0b614192bb --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_types.h @@ -0,0 +1,156 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 __cplusplus +extern "C"{ +#endif + +#ifdef _UBASE_ +#include +#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 + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_utils.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_utils.h new file mode 100644 index 0000000000..b3de0a077b --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_utils.h @@ -0,0 +1,85 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 + + +//begin method +#define PARTITION_FOLLOW_PRIVATE 0 +#define PARTITION_BEGIN_ABSOLUTE 1 + +//alloc method +#define ALLOC_BY_SIZE 0 +#define ALLOC_BY_ABSOLUTE 1 +#define ALLOC_USE_FREE 2 + +//struct uffs_PartitionMakeInfoSt { +// u32 begin_method; +// u32 alloc_method; +// union{ +// u32 begin_block; +// u32 begin_offset; +// }; +// union{ +// u32 end_block; +// u32 size; +// u32 remain_size; +// }; +// u32 access; +//}; +// +// +//URET uffs_MakePartition(struct uffs_DeviceSt *dev, struct uffs_PartitionMakeInfoSt *pi, int nums); +// +//void uffs_ListPartition(struct uffs_DeviceSt *dev); + +//get UFFS disk version, if fail, return 0 +int uffs_GetUFFSVersion(struct uffs_DeviceSt *dev); + +URET uffs_FormatDevice(uffs_Device *dev); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/inc/uffs/uffs_version.h b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_version.h new file mode 100644 index 0000000000..0330decf08 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/inc/uffs/uffs_version.h @@ -0,0 +1,54 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 0x01030000 //"01.03.0000" + +const char * uffs_Version2Str(int ver); +int uffs_GetVersion(void); +int uffs_GetMainVersion(int ver); +int uffs_GetMinorVersion(int ver); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/components/dfs/filesystems/uffs/src/uffs/CMakeLists.txt b/components/dfs/filesystems/uffs/src/uffs/CMakeLists.txt new file mode 100644 index 0000000000..38f85fbfa3 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/CMakeLists.txt @@ -0,0 +1,49 @@ +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 + ) + +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_config.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 + ) + +INCLUDE_DIRECTORIES(${uffs_SOURCE_DIR}/src/inc) +ADD_LIBRARY( uffs STATIC ${libuffs_SRCS} ${libuffs_HEADS} ) diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_badblock.c b/components/dfs/filesystems/uffs/src/uffs/uffs_badblock.c new file mode 100644 index 0000000000..32dbb039d4 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_badblock.c @@ -0,0 +1,216 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_fs.h" +#include "uffs/uffs_config.h" +#include "uffs/uffs_ecc.h" +#include "uffs/uffs_badblock.h" +#include + +#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 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_ERR_SERIOUS, "no free block to replace bad block!"); + return; + } + + //recover block + bc = uffs_BlockInfoGet(dev, dev->bad.block); + + if (bc == NULL) { + uffs_Perror(UFFS_ERR_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); + tag = GET_TAG(bc, page); + buf = uffs_BufClone(dev, NULL); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_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); + + 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_ERR_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, ®ion); + 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_ERR_NOISY, "new bad block %d found, and replaced by %d!", dev->bad.block, good->u.list.block); + 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_ERR_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_ERR_SERIOUS, "Can't add more then one bad block !"); + else + dev->bad.block = block; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_blockinfo.c b/components/dfs/filesystems/uffs/src/uffs/uffs_blockinfo.c new file mode 100644 index 0000000000..120a04df47 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_blockinfo.c @@ -0,0 +1,387 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_blockinfo.h" +#include "uffs/uffs_public.h" +#include "uffs/uffs_os.h" + +#include + +#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_ERR_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_ERR_DEAD, "Block cache buffer require %d but only %d available.", size, dev->mem.blockinfo_pool_size); + return U_FAIL; + } + + uffs_Perror(UFFS_ERR_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_ERR_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->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_FlashReadPageSpare(dev, work->block, i, &(spare->tag), NULL); + if (UFFS_FLASH_HAVE_ERR(ret)) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "page out of range !"); + return U_FAIL; + } + spare = &(work->spares[page]); + if (spare->expired != 0) { + ret = uffs_FlashReadPageSpare(dev, work->block, page, &(spare->tag), NULL); + if (UFFS_FLASH_HAVE_ERR(ret)) { + uffs_Perror(UFFS_ERR_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_ERR_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; + } + + 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->ref_count == 0) { + uffs_Perror(UFFS_ERR_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; +} diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_buf.c b/components/dfs/filesystems/uffs/src/uffs/uffs_buf.c new file mode 100644 index 0000000000..fc1adf72b4 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_buf.c @@ -0,0 +1,1591 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/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 + +#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_ERR_NORMAL, "------------- page buffer inspect ---------" TENDSTR); + uffs_PerrorRaw(UFFS_ERR_NORMAL, "all buffers: " TENDSTR); + for (buf = pb->head; buf; buf = buf->next) { + if (buf->mark != 0) { + uffs_PerrorRaw(UFFS_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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 < MAX_DIRTY_BUF_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 < MAX_DIRTY_BUF_GROUPS; slot++) { + if(_BufFlush(dev, FALSE, slot) != U_SUCC) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_NORMAL, "can't release buf, fail to flush buffer"); + return U_FAIL; + } + + if (dev->mem.free) + dev->mem.free(dev, dev->buf.pool); + + 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_ERR_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 1 + 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); + + 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); + + 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] parent parent serial num + * \param[in] serial serial num + * \param[in] page_id page_id + * \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; + + while (p) { + if( p->parent == parent && + p->serial == serial && + p->page_id == page_id && + p->mark != UFFS_BUF_EMPTY) + { + //they have match one + return p; + } + p = p->next; + } + + return NULL; //buffer not found +} + +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_ERR_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_ERR_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_ERR_SERIOUS, "parent or serial in dirty pages buffer are not the same ?"); + return U_FAIL; + } + if (dirty->mark != UFFS_BUF_DIRTY) { + uffs_Perror(UFFS_ERR_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; + + work = work->next_dirty; + while (work) { + if (work->page_id < buf->page_id) + buf = work; + work = work->next_dirty; + } + return buf; +} + + +/** + * \brief flush buffer with block recover + * + * Scenario: + * 1. get a free (erased) block --> newNode
+ * 2. copy from old block ---> oldNode, or copy from dirty list,
+ * sorted by page_id, to new block. Skips the invalid pages when copy pages.
+ * 3. erased old block. set new info to oldNode, set newNode->block = old block,
+ * and put newNode to erased list.
+ * \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; + + type = dev->buf.dirtyGroup[slot].dirty->type; + parent = dev->buf.dirtyGroup[slot].dirty->parent; + serial = dev->buf.dirtyGroup[slot].dirty->serial; + +retry: + flash_op_err = UFFS_FLASH_NO_ERR; + succRecover = U_FALSE; + + newNode = uffs_TreeGetErasedNode(dev); + if (newNode == NULL) { + uffs_Perror(UFFS_ERR_NOISY, "no enough erased block!"); + goto ext; + } + newBlock = newNode->u.list.block; + newBc = uffs_BlockInfoGet(dev, newBlock); + if (newBc == NULL) { + uffs_Perror(UFFS_ERR_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_BlockInfoLoad(dev, newBc, UFFS_ALL_PAGES); + timeStamp = uffs_GetNextBlockTimeStamp(uffs_GetBlockTimeStamp(dev, bc)); + +// uffs_Perror(UFFS_ERR_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_BLOCK_TS(tag) = timeStamp; + TAG_PARENT(tag) = parent; + TAG_SERIAL(tag) = serial; + TAG_TYPE(tag) = type; + TAG_PAGE_ID(tag) = (u8)i; //now, page_id = page, FIX ME!! if more than 256 pages in a block + + 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) // this could happen when truncating a file + flash_op_err = UFFS_FLASH_NO_ERR; + else + flash_op_err = uffs_FlashWritePageCombine(dev, newBlock, i, buf, tag); + + if (flash_op_err == UFFS_FLASH_BAD_BLK) { + uffs_Perror(UFFS_ERR_NORMAL, "new bad block %d discovered.", newBlock); + break; + } + else if (flash_op_err == UFFS_FLASH_IO_ERR) { + uffs_Perror(UFFS_ERR_NORMAL, "writing to block %d page %d, I/O error ?", (int)newBlock, (int)i); + break; + } + else if (buf->ext_mark & UFFS_BUF_EXT_MARK_TRUNC_TAIL) { + // 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. + uffs_BlockInfoExpire(dev, newBc, i); + succRecover = U_TRUE; + break; + } + } + else { + page = uffs_FindPageInBlockWithPageId(dev, bc, i); + if (page == UFFS_INVALID_PAGE) { + uffs_BlockInfoExpire(dev, newBc, i); + succRecover = U_TRUE; + break; //end of last page, normal break + } + page = uffs_FindBestPageInBlock(dev, bc, page); + + oldTag = GET_TAG(bc, page); + buf = uffs_BufClone(dev, NULL); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "the old block %d is a bad block, but ignore it for now.", bc->block); + } + else { + uffs_Perror(UFFS_ERR_SERIOUS, "I/O error ?"); + uffs_BufFreeClone(dev, buf); + flash_op_err = UFFS_FLASH_IO_ERR; + break; + } + } + buf->data_len = TAG_DATA_LEN(oldTag); + if (buf->data_len > dev->com.pg_data_size) { + uffs_Perror(UFFS_ERR_NOISY, "data length over flow!!!"); + buf->data_len = dev->com.pg_data_size; + } + + buf->type = type; + buf->parent = parent; + buf->serial = serial; + buf->data_len = TAG_DATA_LEN(oldTag); + buf->page_id = TAG_PAGE_ID(oldTag); + + 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); + uffs_BufFreeClone(dev, buf); + if (flash_op_err == UFFS_FLASH_BAD_BLK) { + uffs_Perror(UFFS_ERR_NORMAL, "new bad block %d discovered.", newBlock); + break; + } + else if (flash_op_err == UFFS_FLASH_IO_ERR) { + uffs_Perror(UFFS_ERR_NORMAL, "I/O error ?", newBlock); + break; + } + } + } //end of for + + if (i == dev->attr->pages_per_block) + succRecover = U_TRUE; + + 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, we need to + // deal with it immediately (mark it as 'bad' and put into bad block list). + uffs_BadBlockProcess(dev, newNode); + } + 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_ERR_SERIOUS, "UNKNOW TYPE"); + break; + } + + newNode->u.list.block = bc->block; + uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); + + // 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_BadBlockProcess(dev, newNode); + } + else { + // erase recovered block, put it back to erased block list. + uffs_FlashEraseBlock(dev, bc->block); + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockProcess(dev, newNode); + else + uffs_TreeInsertToErasedListTail(dev, newNode); + } + } + else { + uffs_BlockInfoExpire(dev, newBc, UFFS_ALL_PAGES); + 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_ERR_NORMAL, "still has dirty buffer ?"); + } + + uffs_BlockInfoPut(dev, newBc); +ext: + uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); + 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_ERR_NOISY, "no erased block!"); + goto ext; + } + bc = uffs_BlockInfoGet(dev, node->u.list.block); + if (bc == NULL) { + uffs_Perror(UFFS_ERR_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_InsertToErasedListHead(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 freePages //!< how many free pages left on destination block + ) +{ + u16 page; + uffs_Buf *buf; + uffs_Tags *tag; + URET ret; + int x; + +// uffs_Perror(UFFS_ERR_NOISY, "Flush buffers with Enough Free Page, in block %d", +// bc->block); + ret = U_FAIL; + for (page = dev->attr->pages_per_block - freePages; //page: free page num + dev->buf.dirtyGroup[slot].count > 0; //still has dirty pages? + page++) { + + buf = _FindMinimunPageIdFromDirtyList(dev->buf.dirtyGroup[slot].dirty); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "count > 0, but no dirty pages in list ?"); + goto ext; + } + + //writre the dirty page (id: buf->page_id) to page i (free page) + uffs_BlockInfoLoad(dev, bc, page); + tag = GET_TAG(bc, page); + TAG_BLOCK_TS(tag) = uffs_GetBlockTimeStamp(dev, bc); + TAG_DATA_LEN(tag) = buf->data_len; + TAG_TYPE(tag) = buf->type; + //tag->data_sum = _GetDirOrFileNameSum(dev, buf); + TAG_PARENT(tag) = buf->parent; + TAG_SERIAL(tag) = buf->serial; + TAG_PAGE_ID(tag) = (u8)(buf->page_id); + + x = uffs_FlashWritePageCombine(dev, bc->block, page, buf, tag); + if (x == UFFS_FLASH_IO_ERR) { + uffs_Perror(UFFS_ERR_NORMAL, "I/O error <1>?"); + goto ext; + } + else if (x == UFFS_FLASH_BAD_BLK) { + 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_ERR_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_ERR_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_ERR_SERIOUS, "unknown type."); + return U_FAIL; + } + bc = uffs_BlockInfoGet(dev, block); + if(bc == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "get block info fail."); + return U_FAIL; + } + uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES); + 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, n); + } + 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 < MAX_DIRTY_BUF_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 < MAX_DIRTY_BUF_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 < MAX_DIRTY_BUF_GROUPS) { + if (dev->buf.dirtyGroup[slot].lock > 0) + dev->buf.dirtyGroup[slot].lock--; + else { + uffs_Perror(UFFS_ERR_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 < MAX_DIRTY_BUF_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 < MAX_DIRTY_BUF_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 < MAX_DIRTY_BUF_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_ERR_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_ERR_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 + * \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) +{ + 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_ERR_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_ERR_SERIOUS, "no free page buf!"); + return NULL; + } + } + + bc = uffs_BlockInfoGet(dev, block); + if (bc == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "can't find right page ?"); + return NULL; + } + page = uffs_FindBestPageInBlock(dev, bc, page); + 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))) { + uffs_Perror(UFFS_ERR_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_ERR_NORMAL, "Can't put an NULL buffer!"); + } + else if (buf->ref_count == 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Putting an unused page buffer ? "); + } + else if (buf->ref_count == CLONE_BUF_MARK) { + uffs_Perror(UFFS_ERR_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_ERR_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; //because 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_ERR_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_ERR_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_ERR_SERIOUS, "no free slot ?"); + return U_FAIL; + } + } + } + + memcpy(buf->data + ofs, data, len); + + 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; +} + + + + + + + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_debug.c b/components/dfs/filesystems/uffs/src/uffs/uffs_debug.c new file mode 100644 index 0000000000..13b46667d1 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_debug.c @@ -0,0 +1,144 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_public.h" +#include +#include +#include + + +#if !defined(_UBASE_) +#define ENABLE_DEBUG +//#define OUTPUT_TOFILE +#endif + +#if !defined(_UBASE_) + + +#ifdef OUTPUT_TOFILE +#define DEBUG_LOGFILE "log.txt" +#endif + +void uffs_DebugMessage(int level, const char *prefix, const char *suffix, const char *errFmt, ...) +{ + +#ifdef ENABLE_DEBUG + if (level >= UFFS_DBG_LEVEL) { + + char buf[1024] = {0}; + char *p; + + +#ifdef OUTPUT_TOFILE + FILE *fp = NULL; +#endif + + va_list arg; + + if (strlen(errFmt) > 800) { + // dangerous!! + printf("uffs_Perror buffer is not enough !"); + return; + } + + p = buf; + + if (prefix) { + strcpy(p, prefix); + p += strlen(prefix); + } + + va_start(arg, errFmt); + vsprintf(p, errFmt, arg); + va_end(arg); + + if (suffix) + strcat(p, suffix); + +#ifdef OUTPUT_TOFILE + fp = fopen(DEBUG_LOGFILE, "a+b"); + if (fp) { + fwrite(buf, 1, strlen(buf), fp); + fclose(fp); + } +#else + printf("%s", buf); +#endif + } +#endif //ENABLE_DEBUG +} + +#else + +#define ENABLE_DEBUG + +#include +#include + + +void uffs_Perror( int level, const char *errFmt, ...) +{ +#ifdef ENABLE_DEBUG + va_list args; + if (level >= UFFS_DBG_LEVEL) { + va_start(args, errFmt); + //uffs_vTrace(errFmt, args); + dbg_simple_vprintf(errFmt, args); + va_end(args); + } + dbg_simple_raw(TENDSTR); +#else + level = level; + errFmt = errFmt; +#endif //ENABLE_DEBUG +} + +#endif + +/** + * \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) +{ + printf("ASSERT %s:%d - msg:%s\n", file, line, msg); + while (1); +} diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_device.c b/components/dfs/filesystems/uffs/src/uffs/uffs_device.c new file mode 100644 index 0000000000..fb52f4892a --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_device.c @@ -0,0 +1,94 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_device.h" +#include "uffs/uffs_os.h" +#include "uffs/uffs_public.h" +#include "uffs/uffs_mtb.h" +#include + +#define PFX "dev: " + + + +URET uffs_DeviceInitLock(uffs_Device *dev) +{ + dev->lock.sem = uffs_SemCreate(1); + dev->lock.task_id = UFFS_TASK_ID_NOT_EXIST; + dev->lock.counter = 0; + + return U_SUCC; +} + +URET uffs_DeviceReleaseLock(uffs_Device *dev) +{ + if (dev->lock.sem) { + uffs_SemDelete(dev->lock.sem); + dev->lock.sem = 0; + } + + return U_SUCC; +} + +URET uffs_DeviceLock(uffs_Device *dev) +{ + + uffs_SemWait(dev->lock.sem); + + if (dev->lock.counter != 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Lock device, counter %d NOT zero?!", dev->lock.counter); + } + + dev->lock.counter++; + + return U_SUCC; +} + +URET uffs_DeviceUnLock(uffs_Device *dev) +{ + + dev->lock.counter--; + + if (dev->lock.counter != 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Unlock device, counter %d NOT zero?!", dev->lock.counter); + } + + uffs_SemSignal(dev->lock.sem); + + return U_SUCC; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_ecc.c b/components/dfs/filesystems/uffs/src/uffs/uffs_ecc.c new file mode 100644 index 0000000000..3ad4e212f4 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_ecc.c @@ -0,0 +1,357 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_fs.h" +#include "uffs/uffs_config.h" +#include + +#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(void *data, void *ecc, u16 len) +{ + u8 *pecc = (u8 *)ecc; + u8 *p = (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(void *data, int data_len, void *ecc) +{ + u8 *p_data = (u8 *)data, *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, *p_read_ecc = (u8 *)read_ecc, *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; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_fd.c b/components/dfs/filesystems/uffs/src/uffs/uffs_fd.c new file mode 100644 index 0000000000..cad91346ff --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_fd.c @@ -0,0 +1,532 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include "uffs/uffs_config.h" +#include "uffs/uffs_fs.h" +#include "uffs/uffs_fd.h" +#define PFX "fd: " + + +#define FD_OFFSET 3 //!< just make file handler more like POSIX (0, 1, 2 for stdin/stdout/stderr) + +#define FD2OBJ(fd) (((fd) >= FD_OFFSET && (fd) < MAX_DIR_HANDLE + FD_OFFSET) ? \ + (uffs_Object *)uffs_PoolGetBufByIndex(uffs_GetObjectPool(), (fd) - FD_OFFSET) : NULL ) + +#define OBJ2FD(obj) (uffs_PoolGetIndex(uffs_GetObjectPool(), obj) + FD_OFFSET) + +#define CHK_OBJ(obj, ret) do { \ + if (uffs_PoolVerify(uffs_GetObjectPool(), (obj)) == U_FALSE || \ + uffs_PoolCheckFreeList(uffs_GetObjectPool(), (obj)) == U_TRUE) { \ + uffs_set_error(-UEBADF); \ + return (ret); \ + } \ + } while(0) + +#define CHK_DIR(dirp, ret) do { \ + if (uffs_PoolVerify(&_dir_pool, (dirp)) == U_FALSE || \ + uffs_PoolCheckFreeList(&_dir_pool, (dirp)) == U_TRUE) { \ + uffs_set_error(-UEBADF); \ + return (ret); \ + } \ + } while(0) + +#define CHK_DIR_VOID(dirp) do { \ + if (uffs_PoolVerify(&_dir_pool, (dirp)) == U_FALSE || \ + uffs_PoolCheckFreeList(&_dir_pool, (dirp)) == U_TRUE) { \ + uffs_set_error(-UEBADF); \ + 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; + +/** + * initialise uffs_DIR buffers, called by UFFS internal + */ +URET uffs_InitDirEntryBuf(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_ReleaseDirEntryBuf(void) +{ + return uffs_PoolRelease(&_dir_pool); +} + +uffs_Pool * uffs_GetDirEntryBufPool(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; + + 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); + } + } + + return ret; +} + +int uffs_close(int fd) +{ + int ret = 0; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(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; + } + + return ret; +} + +int uffs_read(int fd, void *data, int len) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = uffs_ReadObject(obj, data, len); + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +int uffs_write(int fd, void *data, int len) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = uffs_WriteObject(obj, data, len); + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +long uffs_seek(int fd, long offset, int origin) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = uffs_SeekObject(obj, offset, origin); + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +long uffs_tell(int fd) +{ + long ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = (long) uffs_GetCurOffset(obj); + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +int uffs_eof(int fd) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = uffs_EndOfFile(obj); + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +int uffs_flush(int fd) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = (uffs_FlushObject(obj) == U_SUCC) ? 0 : -1; + uffs_set_error(-uffs_GetObjectErr(obj)); + + return ret; +} + +int uffs_rename(const char *old_name, const char *new_name) +{ + int err = 0; + int ret = 0; + + ret = (uffs_RenameObject(old_name, new_name, &err) == U_SUCC) ? 0 : -1; + uffs_set_error(-err); + + 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 if (uffs_DeleteObject(name, &err) == U_SUCC) { + ret = 0; + } + else { + ret = -1; + } + + uffs_set_error(-err); + return ret; +} + +int uffs_truncate(int fd, long remain) +{ + int ret; + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + uffs_ClearObjectErr(obj); + ret = (uffs_TruncateObject(obj, remain) == U_SUCC) ? 0 : -1; + uffs_set_error(-uffs_GetObjectErr(obj)); + + 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; + + 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); + 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) +{ + uffs_Object *obj = FD2OBJ(fd); + + CHK_OBJ(obj, -1); + + return do_stat(obj, buf); +} + +int uffs_closedir(uffs_DIR *dirp) +{ + CHK_DIR(dirp, -1); + + uffs_FindObjectClose(&dirp->f); + if (dirp->obj) { + uffs_CloseObject(dirp->obj); + uffs_PutObject(dirp->obj); + } + PutDirEntry(dirp); + + return 0; +} + +uffs_DIR * uffs_opendir(const char *path) +{ + int err = 0; + uffs_DIR *ret = NULL; + uffs_DIR *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); + return ret; +} + +struct uffs_dirent * uffs_readdir(uffs_DIR *dirp) +{ + struct uffs_dirent *ent; + + CHK_DIR(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; + 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; + + return ent; + } + else + return NULL; +} + +void uffs_rewinddir(uffs_DIR *dirp) +{ + CHK_DIR_VOID(dirp); + + uffs_FindObjectRewind(&dirp->f); +} + + +int uffs_mkdir(const char *name, ...) +{ + uffs_Object *obj; + int ret = 0; + int err = 0; + + 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); + 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 if (uffs_DeleteObject(name, &err) == U_SUCC) { + ret = 0; + } + else { + ret = -1; + } + + uffs_set_error(-err); + return ret; +} + + +#if 0 +void uffs_seekdir(uffs_DIR *dirp, long loc) +{ + return ; +} + +long uffs_telldir(uffs_DIR *dirp) +{ + return 0; +} +#endif diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_find.c b/components/dfs/filesystems/uffs/src/uffs/uffs_find.c new file mode 100644 index 0000000000..6c525620de --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_find.c @@ -0,0 +1,360 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +#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); + + 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) { + 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 = EMPTY_NODE; + } + + 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 (f->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; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_flash.c b/components/dfs/filesystems/uffs/src/uffs/uffs_flash.c new file mode 100644 index 0000000000..e59eaa6052 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_flash.c @@ -0,0 +1,674 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/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 + +#define PFX "Flash: " + +#define SPOOL(dev) &((dev)->mem.spare_pool) +#define HEADER(buf) ((struct uffs_MiniHeaderSt *)(buf)->header) + +#define ECC_SIZE(dev) (3 * (dev)->attr->page_data_size / 256) +#define TAG_STORE_SIZE (sizeof(struct uffs_TagStoreSt)) + + +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; + *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 (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 = 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; +} + + +/** + * Initialize UFFS flash interface + */ +URET uffs_FlashInterfaceInit(uffs_Device *dev) +{ + struct uffs_StorageAttrSt *attr = dev->attr; + uffs_Pool *pool = SPOOL(dev); + + if (!dev->ops->IsBadBlock && !dev->ops->ReadPageSpare) { + uffs_Perror(UFFS_ERR_SERIOUS, "flash driver must provide 'IsBadBlock' or 'ReadPageSpare' function!"); + return U_FAIL; + } + + if (!dev->ops->MarkBadBlock && !dev->ops->WritePageSpare && !dev->ops->WriteFullPage) { + uffs_Perror(UFFS_ERR_SERIOUS, "flash driver must provide 'MarkBadBlock' or 'WritePageSpare' or 'WriteFullPage' function!"); + return U_FAIL; + } + + 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_ERR_DEAD, "Spare buffer require %d but only %d available.", UFFS_SPARE_BUFFER_SIZE, dev->mem.spare_pool_size); + memset(pool, 0, sizeof(uffs_Pool)); + return U_FAIL; + } + + uffs_Perror(UFFS_ERR_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); + + if (dev->attr->layout_opt == UFFS_LAYOUT_UFFS) { + /* sanity check */ + if ((dev->attr->data_layout && !dev->attr->ecc_layout) || + (!dev->attr->data_layout && dev->attr->ecc_layout)) { + uffs_Perror(UFFS_ERR_SERIOUS, "Please setup data_layout and ecc_layout, or leave them all NULL !"); + return U_FAIL; + } + + if (!attr->data_layout && !attr->ecc_layout) + InitSpareLayout(dev); + } + + dev->mem.spare_data_size = CalculateSpareDataSize(dev); + + return U_SUCC; +} + +/** + * 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)); + + return U_SUCC; +} + +/** + * unload spare to tag and ecc. + */ +static void UnloadSpare(uffs_Device *dev, const u8 *spare, uffs_Tags *tag, u8 *ecc) +{ + u8 *p_tag = (u8 *)&tag->s; + int tag_size = TAG_STORE_SIZE; + int ecc_size = ECC_SIZE(dev); + 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 (tag) { + 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; + } + + tag->block_status = spare[dev->attr->block_status_offs]; + } +} + +/** + * Read tag and ecc 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 + * \param[out] ecc ecc 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_FlashReadPageSpare(uffs_Device *dev, int block, int page, uffs_Tags *tag, u8 *ecc) +{ + uffs_FlashOps *ops = dev->ops; + struct uffs_StorageAttrSt *attr = dev->attr; + u8 * spare_buf; + int ret = UFFS_FLASH_UNKNOWN_ERR; + UBOOL is_bad = U_FALSE; + + spare_buf = (u8 *) uffs_PoolGet(SPOOL(dev)); + if (spare_buf == NULL) + goto ext; + + if (ops->ReadPageSpareWithLayout) + ret = ops->ReadPageSpareWithLayout(dev, block, page, (u8 *)&tag->s, tag ? TAG_STORE_SIZE : 0, ecc); + else + ret = ops->ReadPageSpare(dev, block, page, spare_buf, 0, dev->mem.spare_data_size); + + + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + is_bad = U_TRUE; + + if (!ops->ReadPageSpareWithLayout) + UnloadSpare(dev, spare_buf, tag, ecc); + + // copy some raw data + if (tag) { + tag->_dirty = tag->s.dirty; + tag->_valid = tag->s.valid; + } + + if (UFFS_FLASH_HAVE_ERR(ret)) + goto ext; + + if (tag) { + if (tag->_valid == 1) //it's not a valid page ? don't need go further + 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_ERR_NORMAL, "A new bad block (%d) is detected.", block); + } + + if (spare_buf) + uffs_PoolPut(SPOOL(dev), spare_buf); + + return ret; +} + +/** + * Read page data to page buf and calculate ecc. + * \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 + * + * \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_FlashReadPage(uffs_Device *dev, int block, int page, uffs_Buf *buf) +{ + uffs_FlashOps *ops = dev->ops; + int size = dev->com.pg_size; + u8 ecc_buf[MAX_ECC_SIZE]; + u8 ecc_store[MAX_ECC_SIZE]; + UBOOL is_bad = U_FALSE; + + int ret; + + // if ecc_opt is UFFS_ECC_HW, flash driver return ecc, + // if ecc_opt is UFFS_ECC_HW_AUTO, flash driver should do ecc correction. + ret = ops->ReadPageData(dev, block, page, buf->header, size, ecc_buf); + + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + is_bad = U_TRUE; + + if (UFFS_FLASH_HAVE_ERR(ret)) + goto ext; + + if (dev->attr->ecc_opt == UFFS_ECC_SOFT || dev->attr->ecc_opt == UFFS_ECC_HW) { + + if (dev->attr->ecc_opt == UFFS_ECC_SOFT) + uffs_EccMake(buf->header, size, ecc_buf); + + // will auto select ops->ReadPageSpareWithLayout() or ops->ReadPageSpare() + ret = uffs_FlashReadPageSpare(dev, block, page, NULL, ecc_store); + + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + is_bad = U_TRUE; + + if (UFFS_FLASH_HAVE_ERR(ret)) + goto ext; + + 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; + } + +ext: + if (is_bad) { + uffs_BadBlockAdd(dev, block); + } + + 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, 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; + + memset(spare, 0xFF, dev->mem.spare_data_size); // initialize as 0xFF. + + // 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; + } +} + +/** + * 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[MAX_ECC_SIZE]; + u8 *spare; + struct uffs_MiniHeaderSt *header; + int ret = UFFS_FLASH_UNKNOWN_ERR; + UBOOL is_bad = U_FALSE; + + uffs_Buf *verify_buf; + + 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; + + // setup tag + tag->s.dirty = TAG_DIRTY; //!< set dirty bit + tag->s.valid = TAG_VALID; //!< set valid bit + 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); + + if (ops->WriteFullPage) { + ret = ops->WriteFullPage(dev, block, page, buf->header, size, (u8 *)&(tag->s), TAG_STORE_SIZE, ecc_buf); + } + else { + ret = ops->WritePageData(dev, block, page, buf->header, size, ecc_buf); + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + is_bad = U_TRUE; + + if (UFFS_FLASH_HAVE_ERR(ret)) + goto ext; + + if (dev->attr->layout_opt == UFFS_LAYOUT_UFFS) { + if (dev->attr->ecc_opt == UFFS_ECC_SOFT || + dev->attr->ecc_opt == UFFS_ECC_HW) { + uffs_FlashMakeSpare(dev, &tag->s, ecc_buf, spare); + } + else + uffs_FlashMakeSpare(dev, &tag->s, NULL, spare); + + ret = ops->WritePageSpare(dev, block, page, spare, 0, dev->mem.spare_data_size, U_TRUE); + } + else { + uffs_Assert(dev->attr->layout_opt == UFFS_LAYOUT_FLASH && ops->WriteFullPage != NULL, + "Flash driver MUST provide 'WriteFullPage()' for UFFS_LAYOUT_FLASH\n"); + } + } + + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + is_bad = U_TRUE; + +#ifdef CONFIG_PAGE_WRITE_VERIFY + if (!UFFS_FLASH_HAVE_ERR(ret)) { + verify_buf = uffs_BufClone(dev, NULL); + if (verify_buf) { + ret = uffs_FlashReadPage(dev, block, page, verify_buf); + if (!UFFS_FLASH_HAVE_ERR(ret)) { + if (memcmp(buf->header, verify_buf->header, size) != 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Page write verify fail (block %d page %d)", block, page); + ret = UFFS_FLASH_BAD_BLK; + } + } + uffs_BufFreeClone(dev, verify_buf); + } + } +#endif +ext: + if (is_bad) + uffs_BadBlockAdd(dev, block); + + if (spare) + uffs_PoolPut(SPOOL(dev), spare); + + return ret; +} + +/** + * mark a clean page as 'dirty' (and 'invalid') + * + * \param[in] dev uffs device + * \param[in] block + * \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, int block, int page) +{ + u8 *spare; + struct uffs_TagStoreSt s; + uffs_FlashOps *ops = dev->ops; + UBOOL is_bad = U_FALSE; + int ret = UFFS_FLASH_UNKNOWN_ERR; + + spare = (u8 *) uffs_PoolGet(SPOOL(dev)); + if (spare == NULL) + goto ext; + + memset(&s, 0xFF, sizeof(s)); + s.dirty = TAG_DIRTY; // set only 'dirty' bit + + if (dev->attr->ecc_opt != UFFS_ECC_NONE) + TagMakeEcc(&s); + + if (dev->attr->layout_opt == UFFS_LAYOUT_UFFS) { + uffs_FlashMakeSpare(dev, &s, NULL, spare); + ret = ops->WritePageSpare(dev, block, page, spare, 0, dev->mem.spare_data_size, U_FALSE); + } + else { + uffs_Assert(ops->WriteFullPage, "Flash driver MUST provide 'WriteFullPage()' for UFFS_LAYOUT_FLASH\n"); + ret = ops->WriteFullPage(dev, block, page, NULL, 0, (u8 *)&s, TAG_STORE_SIZE, NULL); + } + + 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) +{ + u8 status = 0; + int ret; + + uffs_Perror(UFFS_ERR_NORMAL, "Mark bad block: %d", block); + + 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: event EraseBlock return UFFS_FLASH_BAD_BLK, we still process it ... +#endif + + ret = dev->ops->WritePageSpare(dev, block, 0, &status, dev->attr->block_status_offs, 1, U_FALSE); + +#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) +{ + u8 status = 0xFF; + + if (dev->ops->IsBadBlock) /* if flash driver provide 'IsBadBlock' function, then use it. */ + return dev->ops->IsBadBlock(dev, block) == 0 ? U_FALSE : U_TRUE; + + /* otherwise we check the 'status' byte of spare */ + /* check the first page */ + dev->ops->ReadPageSpare(dev, block, 0, &status, dev->attr->block_status_offs, 1); + + if (status == 0xFF) { + /* check the second page */ + dev->ops->ReadPageSpare(dev, block, 1, &status, dev->attr->block_status_offs, 1); + if (status == 0xFF) + return U_FALSE; + } + + return U_TRUE; +} + +/** Erase flash block */ +URET uffs_FlashEraseBlock(uffs_Device *dev, int block) +{ + int ret; + + ret = dev->ops->EraseBlock(dev, block); + + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + uffs_BadBlockAdd(dev, block); + + return UFFS_FLASH_HAVE_ERR(ret) ? U_FAIL : U_SUCC; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_fs.c b/components/dfs/filesystems/uffs/src/uffs/uffs_fs.c new file mode 100644 index 0000000000..2363d3ba73 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_fs.c @@ -0,0 +1,1627 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_fs.h" +#include "uffs/uffs_config.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 +#include + +#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) + +static void do_ReleaseObjectResource(uffs_Object *obj); +static URET do_TruncateObject(uffs_Object *obj, u32 remain, UBOOL dry_run); + + +static int _object_data[sizeof(uffs_Object) * 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); +} + +/** + * 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); +} + +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); + } + } +} + + + +/** + * create a new object and open it if success + */ +URET uffs_CreateObject(uffs_Object *obj, const char *fullname, int oflag) +{ + oflag |= UO_CREATE; + + if (uffs_ParseObject(obj, fullname) == U_SUCC) + uffs_CreateObjectEx(obj, obj->dev, obj->parent, obj->name, obj->name_len, oflag); + + return (obj->err == UENOERR ? U_SUCC : U_FAIL); +} + + + +/** + * 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] == '/') + obj->name_len--; + } + else { + if (name[obj->name_len - 1] == '/') { + obj->err = UENOENT; + goto ext; + } + } + + if (obj->name_len == 0) { + obj->err = UENOENT; + goto ext; + } + + obj->sum = (obj->name_len > 0 ? uffs_MakeSum16(obj->name, obj->name_len) : 0); + + 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, U_TRUE) == U_SUCC) + do_TruncateObject(obj, 0, U_FALSE); + 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_ERR_SERIOUS, "No free serial num!"); + obj->err = UENOMEM; + goto ext_1; + } + + if (obj->dev->tree.erased_count < MINIMUN_ERASED_BLOCK) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "Can't create new buffer when create obj!"); + goto ext_1; + } + + memset(&fi, 0, sizeof(uffs_FileInfo)); + memcpy(fi.name, obj->name, obj->name_len); + fi.name[obj->name_len] = '\0'; + fi.name_len = obj->name_len; + 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_ERR_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_ERR_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_ERR_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_ERR_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, U_TRUE) == U_SUCC) //NOTE: obj->err will be set in do_TruncateObject() if failed. + do_TruncateObject(obj, 0, U_FALSE); + +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; + } + } + else { + obj->err = UENOENT; + } + + return (obj->err == UENOERR ? U_SUCC : U_FAIL); +} + +/** + * 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) +{ + if (obj == NULL) + return U_FAIL; + + if (uffs_ParseObject(obj, name) == U_SUCC) + uffs_OpenObjectEx(obj, obj->dev, obj->parent, obj->name, obj->name_len, oflag); + + 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; + } + } +} + + +static URET do_FlushObject(uffs_Object *obj) +{ + uffs_Device *dev; + URET ret = U_SUCC; + + dev = obj->dev; + if (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; + } + } + + 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) +{ + uffs_Device *dev; + + if(obj->dev == NULL || obj->open_succ != U_TRUE) { + obj->err = UEBADF; + goto ext; + } + + dev = obj->dev; + 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) +{ + uffs_Device *dev; +#ifdef CONFIG_CHANGE_MODIFY_TIME + uffs_Buf *buf; + uffs_FileInfo fi; +#endif + + if(obj->dev == NULL || obj->open_succ != U_TRUE) { + obj->err = UEBADF; + goto ext; + } + + dev = obj->dev; + uffs_ObjectDevLock(obj); + + if (obj->oflag & (UO_WRONLY|UO_RDWR|UO_APPEND|UO_CREATE|UO_TRUNC)) { + +#ifdef CONFIG_CHANGE_MODIFY_TIME + 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); + else + buf = uffs_BufGetEx(dev, UFFS_TYPE_FILE, obj->node, 0); + + if(buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "can't create a new page ?"); + break; + } + ret = uffs_BufWrite(dev, buf, (u8 *)data + wroteSize, 0, size); + uffs_BufPut(dev, buf); + + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "can create a new buf!"); + break; + } + } + else { + buf = uffs_BufGetEx(dev, type, node, page_id); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "can't get buffer ?"); + break; + } + } + + ret = uffs_BufWrite(dev, buf, (u8 *)data + wroteSize, pageOfs, size); + uffs_BufPut(dev, buf); + + if (ret == U_FAIL) { + uffs_Perror(UFFS_ERR_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, 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 = obj->node; + int remain = len; + u16 fdn; + u32 write_start; + TreeNode *dnode; + u32 size; + + 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_ERR_NOISY, "Can't write to an dir object!"); + obj->err = UEACCES; + return 0; + } + + if (obj->pos > fnode->u.file.len) { + return 0; //can't write file out of range + } + + if (obj->oflag == UO_RDONLY) { + obj->err = UEACCES; + return 0; + } + + uffs_ObjectDevLock(obj); + + if (obj->oflag & UO_APPEND) + obj->pos = fnode->u.file.len; + + while (remain > 0) { + write_start = obj->pos + len - remain; + if (write_start > fnode->u.file.len) { + uffs_Perror(UFFS_ERR_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 < MINIMUN_ERASED_BLOCK) { + uffs_Perror(UFFS_ERR_NOISY, "insufficient block in write obj, new block"); + break; + } + size = do_WriteNewBlock(obj, (u8 *)data + len - remain, remain, fnode->u.file.serial, fdn); + + //Flush immediately, so that the new data node will be created and put in the tree. + 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_ERR_SERIOUS, "can't find data node in tree ?"); + obj->err = UEUNKNOWN; + break; + } + size = do_WriteInternalBlock(obj, dnode, fdn, + (u8 *)data + len - remain, remain, + write_start - GetStartOfDataBlock(obj, fdn)); +#ifdef CONFIG_FLUSH_BUF_AFTER_WRITE + uffs_BufFlushGroup(dev, fnode->u.file.serial, fdn); +#endif + if (size == 0) + break; + + remain -= size; + } + } + + obj->pos += (len - remain); + + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockRecover(dev); + + uffs_ObjectDevUnLock(obj); + + return len - remain; +} + +/** + * 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 = obj->node; + 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; + + if (obj->dev == NULL || obj->open_succ == U_FALSE) { + obj->err = UEBADF; + return 0; + } + + if (obj->type == UFFS_TYPE_DIR) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_SERIOUS, "can't get data node in entry!"); + obj->err = UEUNKNOWN; + 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); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_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); + + 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: + * + * \return return the new file pointer position + */ +long uffs_SeekObject(uffs_Object *obj, long offset, int origin) +{ + if (obj->type == UFFS_TYPE_DIR) { + uffs_Perror(UFFS_ERR_NOISY, "Can't seek a dir object!"); + return 0; + } + + uffs_ObjectDevLock(obj); + + switch (origin) { + case USEEK_CUR: + if (obj->pos + offset > obj->node->u.file.len) { + obj->pos = obj->node->u.file.len; + } + else { + obj->pos += offset; + } + break; + case USEEK_SET: + if (offset > (long) obj->node->u.file.len) { + obj->pos = obj->node->u.file.len; + } + else { + obj->pos = offset; + } + break; + case USEEK_END: + if ( offset>0 ) { + obj->pos = obj->node->u.file.len; + } + else if((offset >= 0 ? offset : -offset) > (long) obj->node->u.file.len) { + obj->pos = 0; + } + else { + obj->pos = obj->node->u.file.len + offset; + } + break; + } + + uffs_ObjectDevUnLock(obj); + + return (long) obj->pos; +} + +/** + * 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; +} + +static URET do_TruncateInternalWithBlockRecover(uffs_Object *obj, u16 fdn, u32 remain, UBOOL dry_run) +{ + 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; + + 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; + } + else { + node = uffs_TreeFindDataNode(dev, fnode->u.file.serial, fdn); + if (node == NULL) { + obj->err = UEIOERR; + uffs_Perror(UFFS_ERR_SERIOUS, "can't find data node when truncate obj"); + goto ext; + } + 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 (dry_run == U_TRUE) { + // 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 (page_id > max_page_id) { + obj->err = UEUNKNOWN; + uffs_Perror(UFFS_ERR_SERIOUS, "Overflow"); + 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); + if (buf == NULL) { + obj->err = UENOMEM; + uffs_Perror(UFFS_ERR_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) + buf->data_len = 0; + else { + remain = (remain % dev->com.pg_data_size); + buf->data_len = (remain == 0 ? dev->com.pg_data_size : 0); + } + 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); + +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, U_TRUE) == U_SUCC) + do_TruncateObject(obj, remain, U_FALSE); + uffs_ObjectDevUnLock(obj); + + return (obj->err == UENOERR ? U_SUCC : U_FAIL); +} + + +/** truncate obj without lock device */ +static URET do_TruncateObject(uffs_Object *obj, u32 remain, UBOOL dry_run) +{ + 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; + + 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; + + 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_ERR_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_ERR_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 (dry_run == U_FALSE) + uffs_BufMarkEmpty(dev, buf); //!< discard the buffer + } + } + + if (dry_run == U_FALSE) { + uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); + 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); + + uffs_BlockInfoPut(dev, bc); + fnode->u.file.len = block_start; + } + else { + uffs_BlockInfoPut(dev, bc); + } + flen = block_start; + } + else { + if (do_TruncateInternalWithBlockRecover(obj, fdn, remain, dry_run) == U_SUCC) { + if (dry_run == U_FALSE) + fnode->u.file.len = remain; + flen = remain; + } + } + } + + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockRecover(dev); +ext: + return (obj->err == UENOERR ? U_SUCC : U_FAIL); + +} + + +/** + * \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; + TreeNode *node; + uffs_Device *dev; + u16 block; + uffs_Buf *buf; + URET ret = U_FAIL; + + obj = uffs_GetObject(); + if (obj == NULL) { + if (err) + *err = UEMFILE; + goto err1; + } + + 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 err1; + } + } + + uffs_TruncateObject(obj, 0); + + uffs_ObjectDevLock(obj); + dev = obj->dev; + + 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 err; //have sub dirs ? + } + + node = uffs_TreeFindFileNodeWithParent(dev, obj->serial); + if (node != NULL) { + if (err) + *err = UEACCES; + goto err; //have sub files ? + } + } + + block = GET_BLOCK_FROM_NODE(obj); + node = obj->node; + + // before erase the block, we need to take care of the buffer ... + uffs_BufFlushAll(dev); + + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockRecover(dev); + + buf = uffs_BufFind(dev, obj->parent, obj->serial, 0); + + if (buf) { + //need to expire this buffer ... + if (buf->ref_count != 0) { + //there is other obj for this file still in use ? + uffs_Perror(UFFS_ERR_NORMAL, "Try to delete object but still have buf referenced."); + if (err) + *err = UEACCES; + goto err; + } + + buf->mark = UFFS_BUF_EMPTY; //!< make this buffer expired. + } + + //TODO: need to take care of other obj->node ? + + uffs_BreakFromEntry(dev, obj->type, node); + uffs_FlashEraseBlock(dev, block); + node->u.list.block = block; + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockProcess(dev, node); + else + uffs_TreeInsertToErasedListTail(dev, node); + + ret = U_SUCC; +err: + uffs_ObjectDevUnLock(obj); +err1: + 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); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_NOISY, "new object already exist!"); + if (err) + *err = UEEXIST; + goto ext; + } + + if (uffs_ParseObject(new_obj, new_name) != U_SUCC) { + uffs_Perror(UFFS_ERR_NOISY, "parse new name fail !"); + if (err) + *err = UENOENT; + goto ext; + } + + if (new_obj->name_len == 0) { + uffs_Perror(UFFS_ERR_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_ERR_NOISY, "Can't open old object !"); + if (err) + *err = UEACCES; + goto ext; + } + } + + if (obj->dev != new_obj->dev) { + uffs_Perror(UFFS_ERR_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) uffs_PutObject(new_obj); + + return ret; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_init.c b/components/dfs/filesystems/uffs/src/uffs/uffs_init.c new file mode 100644 index 0000000000..2eb56c458e --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_init.c @@ -0,0 +1,144 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_types.h" +#include "uffs/uffs_public.h" +#include "uffs/uffs_config.h" +#include "uffs/uffs_tree.h" +#include "uffs/uffs_fs.h" +#include "uffs/uffs_badblock.h" +#include + +#define PFX "init: " + +URET uffs_InitDevice(uffs_Device *dev) +{ + URET ret; + + if (dev->mem.init) { + if (dev->mem.init(dev) != U_SUCC) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "Can't initialize flash interface !"); + goto fail; + } + + uffs_Perror(UFFS_ERR_NOISY, "init page buf"); + ret = uffs_BufInit(dev, MAX_PAGE_BUFFERS, MAX_DIRTY_PAGES_IN_A_BLOCK); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_DEAD, "Initialize page buffers fail"); + goto fail; + } + uffs_Perror(UFFS_ERR_NOISY, "init block info cache"); + ret = uffs_BlockInfoInitCache(dev, MAX_CACHED_BLOCK_INFO); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_DEAD, "Initialize block info fail"); + goto fail; + } + + ret = uffs_TreeInit(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "fail to init tree buffers"); + goto fail; + } + + ret = uffs_BuildTree(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_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_ERR_SERIOUS, "fail to release block info."); + goto ext; + } + + ret = uffs_BufReleaseAll(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "fail to release page buffers"); + goto ext; + } + + ret = uffs_TreeRelease(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "fail to release tree buffers!"); + goto ext; + } + + ret = uffs_FlashInterfaceRelease(dev); + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "fail to release tree buffers!"); + goto ext; + } + + if (dev->mem.release) + ret = dev->mem.release(dev); + + if (ret != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "fail to release memory allocator!"); + } + + uffs_DeviceReleaseLock(dev); + +ext: + return ret; + +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_mem.c b/components/dfs/filesystems/uffs/src/uffs/uffs_mem.c new file mode 100644 index 0000000000..27ca45c9fb --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_mem.c @@ -0,0 +1,902 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 native memory allocator + * \author Ricky Zheng, created 23th Feb, 2007 + */ + +#include + +#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_NATIVE_MEMORY_ALLOCATOR > 0 + +#define HEAP_MAGIC_SIZE 8 /* heap magic size, this block is for memory protection */ + + + + +/* the 'BEST FIT' arithmetic, + if not defined, the arithmetic + will be the 'FIRST FIT' */ +#define K_HEAP_ALLOCK_BEST_FIT + + +/* page size may be: 16,32,64,128... */ +#define ALLOC_PAGE_BIT_OFFSET 5 +#define ALLOC_PAGE_SIZE (1 << ALLOC_PAGE_BIT_OFFSET) +#define ALLOC_PAGE_MASK (ALLOC_PAGE_SIZE - 1) +#define ALLOC_THRESHOLD (ALLOC_PAGE_SIZE * 1) + +/* magic mummbers */ +#define HEAP_NODE_FREE 0x123455aa +#define HEAP_NODE_ALLOCED 0xaa551234 + +#define ALLOC_OFFSET (sizeof(int) + sizeof(int) + sizeof(void *)) + +/* Heap memory node type. */ +typedef struct HeapNodeSt { + int mark; /* alloc mark */ + int size; /* Size of this node */ + struct HeapNodeSt *prev_node; /* private node */ + struct HeapNodeSt *prev_free; /* Link to prev free node */ + struct HeapNodeSt *next_free; /* Link to next free node */ +} HeapNode; + + + +/* + p1 |-----------| + |prev_node | NULL + |mark | HEAP_NODE_ALLOCED + |size | p2 - p1 + |prev_free | alloc to user + |next_free | not used. + | | + | | + p2 |-----------| + |prev_node | p1 + |mark | HEAP_NODE_FREE + |size | p3 - p2 + |prev_free | NULL + |next_free | p5 + | | + | | + p3 |-----------| + |prev_node | p2 + |mark | HEAP_NODE_ALLOCED + |size | p4 - p3 + |prev_free | alloc to user + |next_free | not used. + | | + | | + p4 |-----------| + |prev_node | p3 + |mark | HEAP_NODE_ALLOCED + |size | p5 - p4 + |prev_free | alloc to user + |next_free | not used. + | | + | | + p5 |-----------| + |prev_node | p4 + |mark | HEAP_NODE_FREE + |size | p6 - p5 + |prev_free | p2 + |next_free | NULL + | | + | | + p6 |-----------| + +*/ + +static HeapNode* volatile m_heapFreeList = NULL; +static HeapNode * m_heapTail = NULL; +static u32 m_heap_available = 0; +static u32 m_min_heap_avaiable = 0x0fffffff; +static u32 m_kernel_heap_total = 0; + +static void HeapDeleteFromFreeList(HeapNode *node); +static void HeapChainToFreeList(HeapNode *node); +static void *_k_allock_node(HeapNode *node, int size); +//static void * _kmalloc_clear(int size); +static int _kfree(void *block); + +/* + * Delete one node from free list + * + */ +static void HeapDeleteFromFreeList(HeapNode *node) +{ + if(node->next_free) + node->next_free->prev_free = node->prev_free; + if(node->prev_free) + node->prev_free->next_free = node->next_free; + if(node == m_heapFreeList) + m_heapFreeList = node->next_free; +} + +/* + * Chain the node to free list + */ +static void HeapChainToFreeList(HeapNode *node) +{ + node->next_free = NULL; + node->prev_free = NULL; + if(m_heapFreeList == NULL){ + m_heapFreeList = node; + return; + } + else{ + m_heapFreeList->prev_free = node; + node->next_free = m_heapFreeList; + m_heapFreeList = node; + } +} + +/* + * Alloc a block with given node + * If the node is larger than the + * required space plus the space needed for + * a new node plus a defined threshold, then + * we split it. The unused portion is put back into + * the free-list. + * + * Attention: Irq is locked when call this routin, + * so we must unlock irq when return + */ +static void *_k_allock_node(HeapNode *node, int size) +{ + HeapNode *newNode; + + if(node->size >= size + ALLOC_THRESHOLD){ + /* + * we need to split it + */ + newNode = (HeapNode *)((char *)node + size); + newNode->size = node->size - size; + newNode->mark = HEAP_NODE_FREE; + newNode->prev_node = node; + node->size = size; + /* + * chain the newNode to free list + */ + HeapChainToFreeList(newNode); + + /* + * fix the next node + */ + ((HeapNode *)((char *)newNode + newNode->size))->prev_node = newNode; + } + + /* + * allock this block + */ + node->mark = HEAP_NODE_ALLOCED; + + /* + * delete the node from free list + */ + HeapDeleteFromFreeList(node); + + m_heap_available -= node->size; + if(m_min_heap_avaiable > m_heap_available) + m_min_heap_avaiable = m_heap_available; + + uffs_CriticalExit(); /* exit critical */ + + return (void *)((char *)node + ALLOC_OFFSET); +} + +/* + * Allocate a block from heap memory. + * + * This functions allocates a memory block of the specified + * size and returns a pointer to that block. + * + * The actual size of the allocated block is larger than the + * requested size because of space required for maintenance + * information. This additional information is invisible to + * the application. + * + * The routine looks for the smallest block that will meet + * the required size and releases it to the caller. If the + * block being requested is usefully smaller than the smallest + * free block then the block from which the request is being + * met is split in two. The unused portion is put back into + * the free-list. + * + * The contents of the allocated block is unspecified. + * To allocate a block with all bytes set to zero use + * KHeapAllocClear(). + * + * \note Interrupts are automatically enabled, when this + * function returns. + * + * \param size Size of the requested memory block. + * + * \return Pointer to the allocated memory block if the + * function is successful or NULL if the requested + * amount of memory is not m_heap_available. + */ +static void *_kmalloc(int size) +{ + HeapNode *node; +#if defined(K_HEAP_ALLOCK_BEST_FIT) + HeapNode *fit; +#endif + if(size <= 0) + return NULL; /* size is not fit */ + + /* + * adjust size + */ + size += ALLOC_OFFSET; + if(size & ALLOC_PAGE_MASK){ + size += ALLOC_PAGE_SIZE; + size &= ~ALLOC_PAGE_MASK; + } + + uffs_CriticalEnter(); /* enter critical */ + + node = m_heapFreeList; + +#if defined(K_HEAP_ALLOCK_BEST_FIT) + /* + * Walk through the linked list of free nodes and find the best fit. + */ + fit = NULL; + while(node){ + /* + * Found a note that fits? + */ + if(node->size >= size){ + /* + * If it's an exact match, we don't + * search any further. + */ + if(node->size == size){ + fit = node; + break; + } + /* + * We search most fit one + */ + if(fit){ + if(node->size < fit->size) + fit = node; + } + else + fit = node; + } + node = node->next_free; + } + + if(fit){ + if(fit->size >= size) + return _k_allock_node(fit, size); + } +#else + while(node){ + if(node->size >= size) + return _k_allock_node(node, size); + node = node->next_free; + } +#endif + + uffs_CriticalExit(); /* exit critical */ + + return NULL; /* not found available block */ + +} + +#if 0 +/* Allocates an array in memory with elements initialized to 0 */ +static void *_kcalloc(int num, int size) +{ + return _kmalloc_clear(num * size); +} +#endif + +/* Realloc memory. + * if the size of memblock is small then the new required size, + * alloc a new block memory, and copy the contents from the old one, + * and free the old block. + * if the size is zero, free the old block, and return NULL. <2004.5.8> + * if the size of origin block is larger then the new required size, + * then: + * if the gap is larger then ALLOC_PAGE_SIZE, split the node, and return + * the leav memory back to free list. + * if the gap is less then ALLOC_PAGE_SIZE, just return current block. + * If the given block parameter is NULL, _krealloc behaves the same as _kmalloc. + */ +static void *_krealloc(void *block, int size) +{ + HeapNode *node; + HeapNode *newNode; + void *p; /* return pointer */ + int old_data_size; /* old block data size */ + + if(block == NULL){ + return _kmalloc(size); + } + + if(size == 0) { + _kfree(block); + return NULL; + } + + uffs_CriticalEnter(); /* enter critical */ + + node = (HeapNode *)((char *)block - ALLOC_OFFSET); + old_data_size = node->size - ALLOC_OFFSET; + if(node->mark != (int)HEAP_NODE_ALLOCED || old_data_size <= 0) { + uffs_CriticalExit(); /* exit critical */ + return NULL; /*!!!! at this moment, the heap + managment info must be damaged !!!!!*/ + } + + if(old_data_size < size) { + /* new size is larger then origin block, so need alloc new block */ + p = _kmalloc(size); + if(!p) { + uffs_CriticalExit(); /* exit critical */ + return NULL; /* can't alloc a new block memory, fail... */ + } + + /* alloc a new block, and copy contents from origin block, + * and free it finally. + */ + memcpy(p, block, old_data_size); + _kfree(block); + uffs_CriticalExit(); /* exit critical */ + return p; + } + else { + /* adjust size */ + size += ALLOC_OFFSET; + if(size & ALLOC_PAGE_MASK) { + size += ALLOC_PAGE_SIZE; + size &= ~ALLOC_PAGE_MASK; + } + + if(node->size - size < ALLOC_PAGE_SIZE) { + /* the remain memory is too small, so just skip it */ + uffs_CriticalExit(); /* exit critical */ + return block; + } + else { + /* the remain memory is large enough to be splited */ + /* we generate a new 'alloced' node there */ + newNode = (HeapNode *)((char *)node + size); + newNode->prev_node = node; + newNode->mark = HEAP_NODE_ALLOCED; + newNode->size = node->size - size; + + /* split into two node now */ + ((HeapNode *)((char *)node + node->size))->prev_node = newNode; + node->size = size; + + /* put the newNode into free list */ + _kfree((void *)((char *)newNode + ALLOC_OFFSET)); + + uffs_CriticalExit(); /* exit critical */ + return block; + } + } +} + +#if 0 +static void * _kmalloc_clear(int size) +{ + void *p; + + p = _kmalloc(size); + if(p) + memset(p, 0, size); + return p; +} +#endif + +/*! + * \brief Return a block to heap memory. + * + * An application calls this function, when a previously + * allocated memory block is no longer needed. + * + * The heap manager checks, if the released block adjoins any + * other free regions. If it does, then the adjacent free regions + * are joined together to form one larger region. + * + * \note Interrupts are automatically enabled, when this + * function returns. + * + * \param block Points to a memory block previously allocated + * through a call to _kmalloc(). + * + * \return 0 on success, -1 if the caller tried to free + * a block which had been previously released. + */ +static int _kfree(void *block) +{ + HeapNode *node; + HeapNode *prev; + HeapNode *next; + if (block == NULL) { + return -1; //the pointer of the memory is invalid. + } + uffs_CriticalEnter(); /* enter critical */ + + node = (HeapNode *)((char *)block - ALLOC_OFFSET); + if(node->mark != (int)HEAP_NODE_ALLOCED || node->size <= ALLOC_OFFSET) { + uffs_CriticalExit();/* exit critical */ + return -1; /*!!!! at this point, the heap + management info must be damaged !!!!!*/ + } + m_heap_available += node->size; + + prev = node->prev_node; + next = (HeapNode *)((char *)node + node->size); + + if(prev->mark == HEAP_NODE_FREE){ + /* + * If there' s a free node in front of us, merge it. + */ + prev->size += node->size; + next->prev_node = prev; + HeapDeleteFromFreeList(prev); + node = prev; + } + + if(next->mark == HEAP_NODE_FREE){ + /* + * If there' s a free node following us, merge it. + */ + node->size += next->size; + ((HeapNode *)((char *)next + next->size))->prev_node = node; + HeapDeleteFromFreeList(next); + } + + /* + * now, we just chain the node to free list head. + */ + node->mark = HEAP_NODE_FREE; + HeapChainToFreeList(node); + uffs_CriticalExit(); /* exit critical */ + + return 0; +} + + +/*! + * \brief + * Add a new memory region to the free heap. + * + * This function is called during + * initialization. + * + * Applications typically do not call this function. + * + * \param addr Start address of the memory region. + * \param size Number of bytes of the memory region. + */ +void uffs_MemInitHeap(void *addr, int size) +{ + HeapNode *np; + + + if(!((long)addr & 3)){ + addr = (void *)(((char *)addr) + 4); + addr = (void *)(((long)addr) & ~3); + } + size &= ~ALLOC_PAGE_MASK; + if(size < ALLOC_PAGE_SIZE * 3) return; + + uffs_CriticalEnter(); + + /* pre alloc header node, size is ALLOC_PAGE_SIZE */ + np = (HeapNode *)addr; + np->size = ALLOC_PAGE_SIZE; + np->mark = HEAP_NODE_ALLOCED; + np->prev_node = NULL; + + /* pre alloc tail node, size is -1 */ + np = (HeapNode *)((char *)addr + size - ALLOC_PAGE_SIZE); + np->mark = HEAP_NODE_ALLOCED; + np->size = -1; + np->prev_node = (HeapNode *)((char *)addr + ALLOC_PAGE_SIZE); + m_heapTail = np; + + /* Free list head */ + np = (HeapNode *)((char *)addr + ALLOC_PAGE_SIZE); + np->mark = HEAP_NODE_FREE; + np->prev_node = (HeapNode *)addr; + np->size = size - 2 * ALLOC_PAGE_SIZE; + np->next_free = NULL; + np->prev_free = NULL; + m_heapFreeList = np; + m_heap_available = np->size; + m_min_heap_avaiable = m_heap_available; + + m_kernel_heap_total += size; + + uffs_CriticalExit(); +} + +/******************************************************************************************/ + + +static void *__umalloc(uffs_MemAllocator *mem, unsigned int size, HeapHashTable * hash_tbl); +static void *__ucalloc(uffs_MemAllocator *mem, unsigned int num, unsigned int size, HeapHashTable *hash_tbl); +static void *__urealloc(uffs_MemAllocator *mem, void *block, unsigned int size, HeapHashTable *hash_tbl); +static int __ufree(uffs_MemAllocator *mem, void *p, HeapHashTable * hash_tbl); + + +/* release all alloced memory from hash table, + * return alloced pointer nummber. + */ +static int ReleaseHeap(uffs_MemAllocator *mem, HeapHashTable *hash_tbl) +{ + int i; + int count = 0; + HeapMm volatile * node; + + if (hash_tbl == NULL) + return -1; + for (i = 0; i < HEAP_HASH_SIZE; i++){ + while ((node = hash_tbl[i]) != NULL){ + __ufree(mem, node->p, hash_tbl); + count++; + } + } + _kfree(hash_tbl); + + return count; +} + +static void *uffs_malloc(struct uffs_DeviceSt *dev, unsigned int size) +{ + HeapHashTable * hash_tbl; + + if ((int)size < 0) + return NULL; + + hash_tbl = dev->mem.tbl; + if (hash_tbl) { + return __umalloc(&dev->mem, size, hash_tbl); + } + else{ + return NULL; + } +} + + +/* alloc one block with given size, return the block pointer */ +static void *__umalloc(uffs_MemAllocator *mem, unsigned int size, HeapHashTable *hash_tbl) +{ + void *p; + HeapMm *node; + int idx; + + /* calling kernel routin allocate bigger size memory block */ + p = _kmalloc(HEAP_MAGIC_SIZE + size + HEAP_MAGIC_SIZE); + + if (p) { + node = (HeapMm *)_kmalloc(sizeof(HeapMm)); + if (node == NULL) { + _kfree(p); + return NULL; + } + p = (void *)((char *)p + HEAP_MAGIC_SIZE); /* adjust pointer first */ + node->p = p; + node->size = size; + mem->count += size; + + if (mem->maxused < mem->count) + mem->maxused = mem->count; + + node->task_id = uffs_OSGetTaskId(); /* get task id */ + + uffs_CriticalEnter(); + + /* insert node to hash table */ + idx = GET_HASH_INDEX(p); + node->next = hash_tbl[idx]; + hash_tbl[idx] = node; + + uffs_CriticalExit(); + + return p; /* ok, return the pointer */ + } + return NULL; +} + +/* Allocates an array in memory with elements initialized to 0 */ +static void *__ucalloc(uffs_MemAllocator *mem, unsigned int num, unsigned int size, HeapHashTable *hash_tbl) +{ + return __umalloc(mem, num * size, hash_tbl); +} + + +/* realloc one block with given size, return the block pointer */ +static void *__urealloc(uffs_MemAllocator *mem, void *block, unsigned int size, HeapHashTable *hash_tbl) +{ + void *p, *pNew; + HeapMm *prev, *node; + int idx; + + if (block == NULL) { + return __umalloc(mem, size, hash_tbl); + } + + if (size == 0) { + __ufree(mem, block, hash_tbl); + return NULL; + } + + /* calculate hash idx */ + idx = GET_HASH_INDEX(block); + + /* check whether block pointer is alloc from this heap... */ + uffs_CriticalEnter(); + node = hash_tbl[idx]; + prev = NULL; + + while (node){ + if (node->p == block) { + break; /* got it! */ + } + prev = node; + node = node->next; /* search for next node */ + } + + if (!node) { + /* not my duty :-) */ + uffs_CriticalExit(); + return NULL; + } + + /* ok, begin call kernel API to realloc memory */ + + p = (void *)((char *)block - HEAP_MAGIC_SIZE); /* get real pointer which kernel need */ + pNew = _krealloc(p, HEAP_MAGIC_SIZE + size + HEAP_MAGIC_SIZE); + + if (pNew == NULL) { /* realloc fail */ + uffs_CriticalExit(); + return NULL; + } + + if (pNew == p) { + /* new block is the same as the old block */ + uffs_CriticalExit(); + return block; + } + + /* new block is difference with old block, we need to change hash table ... */ + if (prev){ + /* prev is not the first */ + prev->next = node->next; + } + else{ + /* this node is the first, so.. */ + hash_tbl[idx] = node->next; + } + uffs_CriticalExit(); + + node->p = (void *)((char *)pNew + HEAP_MAGIC_SIZE); + node->size = size; + node->task_id = uffs_OSGetTaskId(); + + /* insert node into hash table */ + idx = GET_HASH_INDEX(node->p); + uffs_CriticalEnter(); + node->next = hash_tbl[idx]; + hash_tbl[idx] = node; + uffs_CriticalExit(); + + return node->p; + +} + + +/* free the block, if the pointer(parameter 'p') is + * not valid(allocated by this allocate system) or error occur, return -1, + * else return 0 + */ +static int __ufree(uffs_MemAllocator *mem, void *p, HeapHashTable *hash_tbl) +{ + HeapMm *node, *prev; + + if (p) { /* check the pointer */ + uffs_CriticalEnter(); + node = hash_tbl[GET_HASH_INDEX(p)]; + prev = NULL; + while (node) { + if (node->p == p) { + /* we find the node, so begin to release */ + if (prev) { + /* this node is not the first */ + prev->next = node->next; + } + else { + /* this node is the first node of hash channel */ + hash_tbl[GET_HASH_INDEX(p)] = node->next; + } + + mem->count -= node->size; + + uffs_CriticalExit(); + if (_kfree(node) == -1) /* calling kernel routine release node */ + return -1; /* fail, return -1 */ + + /* calling kernel routine and return */ + return _kfree((void *)((char *)p - HEAP_MAGIC_SIZE)); + } + prev = node; + node = node->next; /* search for next node */ + } + uffs_CriticalExit(); + } + + return -1; +} + +static URET uffs_free(struct uffs_DeviceSt *dev, void *block) +{ + HeapHashTable *hash_tbl; + hash_tbl = dev->mem.tbl; + + if (hash_tbl) { + if (__ufree(&dev->mem, block, hash_tbl) < 0) { + uffs_Perror(UFFS_ERR_SERIOUS, "Try to free unmanaged memory ?"); + return U_FAIL; + } + } + + return U_SUCC; +} + +URET uffs_MemInitNativeAllocator(uffs_Device *dev) +{ + uffs_MemAllocator *mem = &dev->mem; + + memset(mem->tbl, 0, sizeof(mem->tbl)); + mem->malloc = uffs_malloc; + mem->free = uffs_free; + mem->blockinfo_pool_size = 0; + mem->pagebuf_pool_size = 0; + mem->tree_nodes_pool_size = 0; + + return U_SUCC; +} + + +URET uffs_MemReleaseNativeAllocator(uffs_Device *dev) +{ + int count; + URET ret = U_SUCC; + + if (dev) { + count = ReleaseHeap(&dev->mem, dev->mem.tbl); + if (count < 0) { + uffs_Perror(UFFS_ERR_SERIOUS, "Release native memory allocator fail!"); + ret = U_FAIL; + } + else if (count > 0) { + uffs_Perror(UFFS_ERR_NORMAL, "Find %d block memory leak!", count); + } + } + + return ret; +} + +/** + * \brief Setup the memory allocator to native memory allocator + * + * \param allocator memory allocator to be setup + */ +void uffs_MemSetupNativeAllocator(uffs_MemAllocator *allocator) +{ + memset(allocator, 0, sizeof(uffs_MemAllocator)); + allocator->init = uffs_MemInitNativeAllocator; + allocator->release = uffs_MemReleaseNativeAllocator; +} + +#endif //CONFIG_USE_NATIVE_MEMORY_ALLOCATOR + +#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0 +#include +static void * sys_malloc(struct uffs_DeviceSt *dev, unsigned int size) +{ + uffs_Perror(UFFS_ERR_NORMAL, "system memory alloc %d bytes", size); + return malloc(size); +} + +static URET sys_free(struct uffs_DeviceSt *dev, void *p) +{ + free(p); + return U_SUCC; +} + +void uffs_MemSetupSystemAllocator(uffs_MemAllocator *allocator) +{ + allocator->malloc = sys_malloc; + allocator->free = sys_free; +} +#endif //CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR + +#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_ERR_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_ERR_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 = (unsigned 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_ERR_NOISY, "System static memory: %d bytes", allocator->buf_size); + +} + +#endif + + + + + + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_mtb.c b/components/dfs/filesystems/uffs/src/uffs/uffs_mtb.c new file mode 100644 index 0000000000..f669dea94f --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_mtb.c @@ -0,0 +1,247 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_types.h" +#include "uffs/uffs_public.h" +#include "uffs/uffs_config.h" +#include "uffs/uffs_tree.h" +#include "uffs/uffs_mtb.h" +#include "uffs/uffs_fd.h" +#include + +#define PFX "mtb: " + +static struct uffs_MountTableEntrySt *g_mtb_head = NULL; + +uffs_MountTable * uffs_GetMountTable(void) +{ + return g_mtb_head; +} + +int uffs_RegisterMountTable(uffs_MountTable *mtab) +{ + uffs_MountTable *work = g_mtb_head; + + if (mtab == NULL) + return -1; + + if (work == NULL) { + g_mtb_head = mtab; + return 0; + } + + while (work) { + if (mtab == work) { + /* already registered */ + return 0; + } + if (work->next == NULL) { + work->next = mtab; + mtab->next = NULL; + return 0; + } + work = work->next; + } + + return -1; +} + + +URET uffs_InitMountTable(void) +{ + struct uffs_MountTableEntrySt *tbl = uffs_GetMountTable(); + struct uffs_MountTableEntrySt *work; + int dev_num = 0; + + for (work = tbl; work; work = work->next) { + uffs_Perror(UFFS_ERR_NOISY, "init device for mount point %s ...", work->mount); + if (work->dev->Init(work->dev) == U_FAIL) { + uffs_Perror(UFFS_ERR_SERIOUS, "init device for mount point %s fail", work->mount); + return U_FAIL; + } + + work->dev->par.start = work->start_block; + if (work->end_block < 0) { + work->dev->par.end = work->dev->attr->total_blocks + work->end_block; + } + else { + work->dev->par.end = work->end_block; + } + uffs_Perror(UFFS_ERR_NOISY, "mount partiton: %d,%d", + work->dev->par.start, work->dev->par.end); + + if (uffs_InitDevice(work->dev) != U_SUCC) { + uffs_Perror(UFFS_ERR_SERIOUS, "init device fail !"); + return U_FAIL; + } + work->dev->dev_num = dev_num++; + } + + if (uffs_InitObjectBuf() == U_SUCC) { + if (uffs_InitDirEntryBuf() == U_SUCC) { + return U_SUCC; + } + } + + return U_FAIL; +} + +URET uffs_ReleaseMountTable(void) +{ + struct uffs_MountTableEntrySt *tbl = uffs_GetMountTable(); + struct uffs_MountTableEntrySt *work; + + for (work = tbl; work; work = work->next) { + uffs_ReleaseDevice(work->dev); + work->dev->Release(work->dev); + } + + if (uffs_ReleaseObjectBuf() == U_SUCC) { + if (uffs_ReleaseDirEntryBuf() == U_SUCC) { + return U_SUCC; + } + } + + return U_FAIL; +} + + + +/** + * 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) +{ + struct uffs_MountTableEntrySt *devTab = uffs_GetMountTable(); + + while (devTab) { + if (strcmp(mount, devTab->mount) == 0) { + devTab->dev->ref_count++; + return devTab->dev; + } + devTab = devTab->next; + } + + 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) +{ + struct uffs_MountTableEntrySt *devTab = uffs_GetMountTable(); + + while (devTab) { + if (strlen(devTab->mount) == len && strncmp(mount, devTab->mount, len) == 0) { + devTab->dev->ref_count++; + return devTab->dev; + } + devTab = devTab->next; + } + + 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) +{ + struct uffs_MountTableEntrySt * devTab = uffs_GetMountTable(); + + while (devTab) { + if (devTab->dev == dev) { + return devTab->mount; + } + devTab = devTab->next; + } + + return NULL; +} + +void uffs_PutDevice(uffs_Device *dev) +{ + dev->ref_count--; +} + + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_pool.c b/components/dfs/filesystems/uffs/src/uffs/uffs_pool.c new file mode 100644 index 0000000000..0e05856ed8 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_pool.c @@ -0,0 +1,343 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_types.h" +#include "uffs/uffs_os.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; + + uffs_Assert(pool, "pool missing"); + uffs_Assert(mem, "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"); + + pool->mem = (u8 *)mem; + pool->buf_size = buf_size; + pool->num_bufs = num_bufs; + pool->sem = uffs_SemCreate(1); + + 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) +{ + uffs_Assert(pool, "pool missing"); + + uffs_SemDelete(pool->sem); + pool->sem = 0; + + 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; + + uffs_Assert(pool, "pool missing"); + + 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; + + uffs_Assert(pool, "pool missing"); + + 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; + + uffs_Assert(pool, "pool missing"); + + 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; + + uffs_Assert(pool, "pool missing"); + + 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) +{ + uffs_Assert(pool, "pool missing"); + uffs_Assert(index >= 0 && index < pool->num_bufs, "index out of range"); + + 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) +{ + uffs_Assert(pool, "pool missing"); + uffs_Assert(p >= (void *) pool->mem && + p < (void *) (pool->mem + pool->num_bufs * pool->buf_size), + "pointer out of range"); + + 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++); + + return i < 32 ? 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 (e == NULL && 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; + } + } + } + + 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; +} \ No newline at end of file diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_public.c b/components/dfs/filesystems/uffs/src/uffs/uffs_public.c new file mode 100644 index 0000000000..b5a2cdfc97 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_public.c @@ -0,0 +1,533 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_types.h" +#include "uffs/uffs_config.h" +#include "uffs/uffs_core.h" +#include "uffs/uffs_device.h" +#include "uffs/uffs_os.h" + +#include + +#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_ERR_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_ERR_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 + */ +u16 uffs_FindBestPageInBlock(uffs_Device *dev, uffs_BlockInfo *bc, u16 page) +{ + int i; + int best; + uffs_Tags *tag, *tag_old; + + if (page == dev->attr->pages_per_block - 1) + return page; + + uffs_BlockInfoLoad(dev, bc, page); //load old page + tag_old = GET_TAG(bc, page); + + for (i = dev->attr->pages_per_block - 1; i > page; i--) { + uffs_BlockInfoLoad(dev, bc, i); + tag = GET_TAG(bc, i); + if (TAG_PAGE_ID(tag) == TAG_PAGE_ID(tag_old)) { + if (TAG_PARENT(tag) == TAG_PARENT(tag_old) && + TAG_SERIAL(tag) == TAG_SERIAL(tag_old) && + TAG_IS_DIRTY(tag) && //0: dirty, 1:clear + TAG_IS_VALID(tag_old)) { //0: valid, 1:invalid + break; + } + } + } + best = i; + +#if 0 + if (TAG_PAGE_ID(tag_old) == page) { + //well, try to speed up by probing the last page .... + uffs_BlockInfoLoad(dev, bc, dev->attr->pages_per_block - 1); + tag = GET_TAG(bc, dev->attr->pages_per_block - 1); + if (TAG_IS_VALID(tag) && + TAG_IS_DIRTY(tag) && + TAG_PAGE_ID(tag) == dev->attr->pages_per_block - 1) { + return page; + } + } + + uffs_BlockInfoLoad(dev, bc, UFFS_ALL_PAGES); + best = page; + //the better page must be ahead of page, so ...i = page + 1; i < ... + for (i = page + 1; i < dev->attr->pages_per_block; i++) { + tag = GET_TAG(bc, i); + if (TAG_PAGE_ID(tag) == TAG_PAGE_ID(tag_old)) { + if (TAG_PARENT(tag) == TAG_PARENT(tag_old) && + TAG_SERIAL(tag) == TAG_SERIAL(tag_old) && + TAG_IS_DIRTY(tag) && //0: dirty, 1:clear + TAG_IS_VALID(tag_old)) { //0: valid, 1:invalid + if (i > best) + best = i; + } + } + } +#endif + + return best; +} + +/** + * \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_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_DIRTY(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 +} + + +/** + * Find first valid page from a block, just used in mounting a partition + */ +u16 uffs_FindFirstValidPage(uffs_Device *dev, uffs_BlockInfo *bc) +{ + u16 i; + + for (i = 0; i < dev->attr->pages_per_block; i++) { + if (uffs_BlockInfoLoad(dev, bc, i) == U_SUCC) + return i; + } + return UFFS_INVALID_PAGE; +} + + +/** + * 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) +{ + u8 ret = 0; + const u8 *data = (const u8 *)p; + + if (!p) + return 0; + + while (len > 0) { + ret += *data++; + len--; + } + + return ret; +} + +/** + * 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) +{ + u8 ret_lo = 0; + u8 ret_hi = 0; + const u8 *data = (const u8 *)p; + + if (!p) + return 0; + + while (len > 0) { + ret_lo += *data; + ret_hi ^= *data; + data++; + len--; + } + + return (ret_hi << 8) | ret_lo; +} + +/** + * 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); + //tag->data_sum = uffs_MakeSum16(fi->name, fi->name_len); + + buf = uffs_BufGet(dev, parent, serial, 0); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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; + + // TODO: Need to speed up this procedure! + // First try the last page. will hit it if it's the full loaded file/data block. + uffs_BlockInfoLoad(dev, bc, lastPage); + tag = GET_TAG(bc, lastPage); + + if (type == UFFS_TYPE_FILE) { + if(TAG_PAGE_ID(tag) == (lastPage - 1) && + TAG_DATA_LEN(tag) == dev->com.pg_data_size) { + size = dev->com.pg_data_size * (dev->attr->pages_per_block - 1); + return size; + } + } + if (type == UFFS_TYPE_DATA) { + if(TAG_PAGE_ID(tag) == lastPage && + TAG_DATA_LEN(tag) == dev->com.pg_data_size) { + size = dev->com.pg_data_size * dev->attr->pages_per_block; + 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 (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 (page_id == TAG_PAGE_ID(tag)) { + page = uffs_FindBestPageInBlock(dev, bc, i); + 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; + + 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 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_DIRTY(tag) && + !TAG_IS_VALID(tag)) { + return U_TRUE; + } + + return U_FALSE; +} + +/** + * \brief Is this block the last block of file ? (no free pages, and full filled with full page_id) + */ +UBOOL uffs_IsDataBlockReguFull(uffs_Device *dev, uffs_BlockInfo *bc) +{ + uffs_Tags *tag; + uffs_BlockInfoLoad(dev, bc, dev->attr->pages_per_block - 1); + + tag = GET_TAG(bc, dev->attr->pages_per_block - 1); + + if (TAG_PAGE_ID(tag) == (dev->attr->pages_per_block - 1) && + TAG_DATA_LEN(tag) == dev->com.pg_data_size) { + 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 = dev->ops->ReadPageData(dev, block, page, (u8 *)header, sizeof(struct uffs_MiniHeaderSt), NULL); + + dev->st.page_header_read_count++; + + return UFFS_FLASH_HAVE_ERR(ret) ? U_FAIL : U_SUCC; +} + +#if 0 +/** \brief transfer the standard uffs_Tags to uffs_Tags_8 + * \param[in] tag standard uffs_Tags + * \param[out] tag_8 small tag to fit into 8 bytes spare space + */ +void uffs_TransferToTag8(uffs_Tags *tag, uffs_Tags_8 *tag_8) +{ + tag_8->dirty = tag->dirty; + tag_8->valid = tag->valid; + tag_8->type = tag->type; + tag_8->block_ts = tag->block_ts; + tag_8->page_id = tag->page_id; + tag_8->parent = tag->parent & 0xFF; + tag_8->serial = tag->serial & 0xFF; + tag_8->data_len = tag->data_len & 0xFF; + tag_8->data_sum = tag->data_sum; + tag_8->block_status = tag->block_status; +} + +/** \brief transfer the small uffs_Tags_8 to standard uffs_Tags + * \param[out] tag standard uffs_Tags + * \param[in] tag_8 small tag to fit into 8 bytes spare space + */ +void uffs_TransferFromTag8(uffs_Tags *tag, uffs_Tags_8 *tag_8) +{ + tag->dirty = tag_8->dirty; + tag->valid = tag_8->valid; + tag->type = tag_8->type; + tag->block_ts = tag_8->block_ts; + tag->page_id = tag_8->page_id; + tag->parent = tag_8->parent; + tag->serial = tag_8->serial; + tag->data_len = tag_8->data_len; + tag->data_sum = tag_8->data_sum; + tag->block_status = tag_8->block_status; +} +#endif + + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_tree.c b/components/dfs/filesystems/uffs/src/uffs/uffs_tree.c new file mode 100644 index 0000000000..fded8f0097 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_tree.c @@ -0,0 +1,1164 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_public.h" +#include "uffs/uffs_os.h" +#include "uffs/uffs_pool.h" +#include "uffs/uffs_config.h" +#include "uffs/uffs_flash.h" +#include "uffs/uffs_badblock.h" + +#include + +#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); + +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_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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)) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_NORMAL, "Process unclean block (%d vs %d)", block, block_alt); + + if (block_alt == INVALID_UFFS_SERIAL) { + uffs_Perror(UFFS_ERR_SERIOUS, "invalid block ?"); + return U_FAIL; + } + + bc_alt = uffs_BlockInfoGet(dev, block_alt); + if (bc_alt == NULL) { + uffs_Perror(UFFS_ERR_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_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); + 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); + uffs_BlockInfoExpire(dev, bc_alt, UFFS_ALL_PAGES); + node->u.list.block = block_save; + if (HAVE_BADBLOCK(dev)) + uffs_BadBlockProcess(dev, node); + else + uffs_TreeInsertToErasedListTail(dev, node); + + uffs_BlockInfoPut(dev, bc_alt); //put back bc_alt because we don't need it anymore. + + node = node_alt; //use node_alt to store new informations in following + 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_ERR_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); + 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); + uffs_BlockInfoExpire(dev, bc, UFFS_ALL_PAGES); + + 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: scaning page 0 is not necessary. + + 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_DIRTY(tag) || TAG_IS_VALID(tag)) // stop if we reach a dirty or valid page + break; + + if (uffs_LoadMiniHeader(dev, bc->block, page, &header) == U_FAIL) + return U_FAIL; + + if (header.status != 0xFF) { + // ok, page data is not clean ! mark it as dirty. + uffs_Perror(UFFS_ERR_NORMAL, "unclean page found, block %d page %d", bc->block, page); + uffs_FlashMarkDirtyPage(dev, bc->block, page); + } + } + + return U_SUCC; +} + + +static URET _BuildTreeStepOne(uffs_Device *dev) +{ + int block_lt; + uffs_BlockInfo *bc; + TreeNode *node; + struct uffs_TreeSt *tree; + uffs_Pool *pool; + struct uffs_MiniHeaderSt header; + URET ret = U_SUCC; + struct BlockTypeStatSt st = {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_ERR_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); +// uffs_Perror(UFFS_ERR_NORMAL, "loop"); + if (bc == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "step one:fail to get block info"); + ret = U_FAIL; + break; + } + node = (TreeNode *)uffs_PoolGet(pool); + if (node == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_NORMAL, "found bad block %d", block_lt); + } + else if (uffs_IsPageErased(dev, bc, 0) == U_TRUE) { //@ read one spare: 0 + //just need to check page 0 to know whether the block is erased + // Check the mini header status + + if (uffs_LoadMiniHeader(dev, block_lt, 0, &header) == U_FAIL) { + uffs_Perror(UFFS_ERR_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 spare 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_ERR_NORMAL, "New bad block (%d) discovered.", block_lt); + uffs_BadBlockProcess(dev, node); + } + else { + uffs_TreeInsertToErasedListTail(dev, node); + } + } + else { + + ret = _ScanAndFixUnCleanPage(dev, bc); + if (ret == U_FAIL) + break; + + ret = _BuildValidTreeNode(dev, node, bc, &st); + //uffs_Perror(UFFS_ERR_NOISY, "valid block done!"); + if (ret == U_FAIL) + break; + + } + uffs_BlockInfoPut(dev, bc); + } //end of for + + if(ret == U_FAIL) + uffs_BlockInfoPut(dev, bc); + + uffs_Perror(UFFS_ERR_NORMAL, "DIR %d, FILE %d, DATA %d", st.dir, st.file, st.data); + + return ret; +} + +static URET _BuildTreeStepTwo(uffs_Device *dev) +{ + //Random the start point of erased block to implement ware leveling + u32 startCount = 0; + u32 endPoint; + TreeNode *node; + + uffs_Perror(UFFS_ERR_NOISY, "build tree step two"); + + endPoint = uffs_GetCurDateTime() % dev->tree.erased_count; + while (startCount < endPoint) { + node = uffs_TreeGetErasedNode(dev); + if (node == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "No erased block ?"); + return U_FAIL; + } + uffs_TreeInsertToErasedListTail(dev, node); + 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; +} + +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); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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_ERR_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) + return i; + } + } + return INVALID_UFFS_SERIAL; +} + +TreeNode * uffs_TreeGetErasedNode(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; +} + +static void _InsertToEntry(uffs_Device *dev, u16 *entry, int hash, TreeNode *node) +{ + struct uffs_TreeSt *tree = &(dev->tree); + + node->hash_next = entry[hash]; +#ifdef CONFIG_TREE_NODE_USE_DOUBLE_LINK + node->hash_prev = EMPTY_NODE; + if (entry[hash] != EMPTY_NODE) { + FROM_IDX(entry[hash], TPOOL(dev))->hash_prev = TO_IDX(node, TPOOL(dev)); + } +#endif + entry[hash] = TO_IDX(node, TPOOL(dev)); +} + + +#ifndef CONFIG_TREE_NODE_USE_DOUBLE_LINK +TreeNode * _FindPrevNodeFromEntry(uffs_Device *dev, u16 start, u16 find) +{ + TreeNode *work; + while (start != EMPTY_NODE) { + work = FROM_IDX(start, &(dev->mem.tree_pool)); + if (work->hash_next == find) { + return work; + } + } + return NULL; +} +#endif + +/** + * 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_ERR_SERIOUS, "unknown type when break..."); + return; + } +#ifdef CONFIG_TREE_NODE_USE_DOUBLE_LINK + 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; + } +#else + if ((work = _FindPrevNodeFromEntry(dev, *entry, TO_IDX(node, &(dev->mem.tree_pool)))) != NULL) { + work->hash_next = node->hash_next; + } +#endif + + 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++; +} + +void uffs_TreeInsertToErasedListTail(uffs_Device *dev, TreeNode *node) +{ + struct uffs_TreeSt *tree; + tree = &(dev->tree); + + 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_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; + } +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_utils.c b/components/dfs/filesystems/uffs/src/uffs/uffs_utils.c new file mode 100644 index 0000000000..e65ceb049d --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_utils.c @@ -0,0 +1,195 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/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 +#include + +#define PFX "util: " + +#ifdef CONFIG_ENABLE_BAD_BLOCK_VERIFY +static void _ForceFormatAndCheckBlock(uffs_Device *dev, int block) +{ + u8 *pageBuf; + int pageSize; + int i, j; + uffs_Buf *buf; + UBOOL bad = U_TRUE; + int ret; + + buf = uffs_BufClone(dev, NULL); + if (buf == NULL) { + uffs_Perror(UFFS_ERR_SERIOUS, "Alloc page buffer fail ! Format stoped."); + goto ext; + } + + pageSize = dev->com.pg_data_size; + pageBuf = buf->data; + + + //step 1: Erase, fully fill with 0x0, and check + ret = dev->ops->EraseBlock(dev, block); + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + goto bad_out; + + memset(pageBuf, 0, pageSize); + for (i = 0; i < dev->attr->pages_per_block; i++) { + ret = dev->ops->WritePageData(dev, block, i, pageBuf, pageSize, NULL); + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + goto bad_out; + ret = dev->ops->WritePageSpare(dev, block, i, pageBuf, 0, dev->attr->spare_size, U_TRUE); + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + goto bad_out; + } + for (i = 0; i < dev->attr->pages_per_block; i++) { + memset(pageBuf, 0xFF, pageSize); + dev->ops->ReadPageData(dev, block, i, pageBuf, pageSize, NULL); + for (j = 0; j < pageSize; j++) { + if(pageBuf[j] != 0) + goto bad_out; + } + memset(pageBuf, 0xFF, dev->attr->spare_size); + dev->ops->ReadPageSpare(dev, block, i, pageBuf, 0, dev->attr->spare_size); + for (j = 0; j < dev->attr->spare_size; j++) { + if(pageBuf[j] != 0) + goto bad_out; + } + } + + //step 2: Erase, and check + ret = dev->ops->EraseBlock(dev, block); + if (UFFS_FLASH_IS_BAD_BLOCK(ret)) + goto bad_out; + + for (i = 0; i < dev->attr->pages_per_block; i++) { + memset(pageBuf, 0, pageSize); + dev->ops->ReadPageData(dev, block, i, pageBuf, pageSize, NULL); + for (j = 0; j < pageSize; j++) { + if(pageBuf[j] != 0xFF) + goto bad_out; + } + memset(pageBuf, 0, dev->attr->spare_size); + dev->ops->ReadPageSpare(dev, block, i, pageBuf, 0, dev->attr->spare_size); + for (j = 0; j < dev->attr->spare_size; j++) { + if(pageBuf[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); + + return; +} +#endif + + + +URET uffs_FormatDevice(uffs_Device *dev) +{ + u16 i, slot; + + if (dev == NULL) + return U_FAIL; + + if (dev->ops == NULL) + return U_FAIL; + + + if (uffs_BufIsAllFree(dev) == U_FALSE) { + uffs_Perror(UFFS_ERR_NORMAL, "some page still in used!"); + return U_FAIL; + } + + for (slot = 0; slot < MAX_DIRTY_BUF_GROUPS; slot++) { + if (dev->buf.dirtyGroup[slot].count > 0) { + uffs_Perror(UFFS_ERR_SERIOUS, "there still have dirty pages!"); + return U_FAIL; + } + } + + uffs_BufSetAllEmpty(dev); + + + if (uffs_BlockInfoIsAllFree(dev) == U_FALSE) { + uffs_Perror(UFFS_ERR_NORMAL, "there still have block info cache ? fail to format"); + return U_FAIL; + } + + uffs_BlockInfoExpireAll(dev); + + for (i = dev->par.start; 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 (uffs_TreeRelease(dev) == U_FAIL) { + return U_FAIL; + } + + if (uffs_TreeInit(dev) == U_FAIL) { + return U_FAIL; + } + + if (uffs_BuildTree(dev) == U_FAIL) { + return U_FAIL; + } + + return U_SUCC; +} + diff --git a/components/dfs/filesystems/uffs/src/uffs/uffs_version.c b/components/dfs/filesystems/uffs/src/uffs/uffs_version.c new file mode 100644 index 0000000000..9e257ce2dd --- /dev/null +++ b/components/dfs/filesystems/uffs/src/uffs/uffs_version.c @@ -0,0 +1,67 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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/uffs_version.h" +#include "uffs/uffs_config.h" + +#include +#define PFX "ver: " + + +static char version_buf[8]; + +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; +} diff --git a/components/dfs/filesystems/uffs/src/utils/CMakeLists.txt b/components/dfs/filesystems/uffs/src/utils/CMakeLists.txt new file mode 100644 index 0000000000..4168d94478 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/utils/CMakeLists.txt @@ -0,0 +1,10 @@ +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) + diff --git a/components/dfs/filesystems/uffs/src/utils/mkuffs.c b/components/dfs/filesystems/uffs/src/utils/mkuffs.c new file mode 100644 index 0000000000..b69df8e332 --- /dev/null +++ b/components/dfs/filesystems/uffs/src/utils/mkuffs.c @@ -0,0 +1,497 @@ +/* + This file is part of UFFS, the Ultra-low-cost Flash File System. + + Copyright (C) 2005-2009 Ricky Zheng + + 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 +#include +#include +#include "uffs/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" + +#if CONFIG_USE_STATIC_MEMORY_ALLOCATOR > 0 +int main() +{ + printf("Static memory allocator is not supported.\n"); + 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); + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 +static int conf_memory_pool_size_kb = 800; /* default allocate 100k memory. */ +static void *memory_pool = NULL; +#endif + +static int conf_command_line_mode = 0; +static int conf_verbose_mode = 0; + +static int conf_exec_script = 0; +static char script_file_name[256]; + +#define DEFAULT_EMU_FILENAME "uffsemfile.bin" +const char * conf_emu_filename = DEFAULT_EMU_FILENAME; + + +/* default basic parameters of the NAND device */ +int conf_pages_per_block = 32; +int conf_pages_data_size = 512; +int conf_pages_spare_size = 16; +int conf_status_byte_offset = 5; +int conf_total_blocks = 128; + +#define PAGE_SIZE (conf_pages_data_size + conf_pages_spare_size) +#define BLOCK_DATA_SIZE (conf_pages_per_block * conf_pages_data_size) +#define TOTAL_DATA_SIZE (conf_total_blocks * BLOCK_DATA_SIZE) +#define BLOCK_SIZE (conf_pages_per_block * PAGE_SIZE) +#define TOTAL_SIZE (BLOCK_SIZE * conf_total_blocks) + +#define MAX_MOUNT_TABLES 10 +#define MAX_MOUNT_POINT_NAME 32 + +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 struct uffs_StorageAttrSt emu_storage = {0}; +static struct uffs_FileEmuSt emu_private = {0}; + + + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 +BOOL cmdMeminfo(const char *tail) +{ + const char *mount = "/"; + int i; + HeapMm *mm; + int count = 0; + int blocks = 0; + + uffs_Device *dev; + + if (tail) + mount = cli_getparam(tail, NULL); + + dev = uffs_GetDeviceFromMountPoint(mount); + + if (!dev) { + printf("can't get device from mount point %s\n", mount); + return TRUE; + } + + for (i = 0; i < HEAP_HASH_SIZE; i++) { + mm = dev->mem.tbl[i]; + while (mm) { + printf("%d, ", mm->size); + count += mm->size; + blocks++; + mm = mm->next; + } + } + printf("\n>>> total allocated %d blocks (%d bytes), max %d bytes. <<<\n", blocks, count, dev->mem.maxused); + + uffs_PutDevice(dev); + + return TRUE; +} +#endif + + +static struct cli_commandset basic_cmdset[] = +{ +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + { cmdMeminfo, "mem", "", "show native memory allocator infomation" }, +#endif + { NULL, NULL, NULL, NULL } +}; + + +static void setup_emu_storage(struct uffs_StorageAttrSt *attr) +{ + attr->total_blocks = conf_total_blocks; /* total blocks */ + attr->page_data_size = conf_pages_data_size; /* page data size */ + attr->spare_size = conf_pages_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 = UFFS_ECC_SOFT; /* let UFFS handle the ECC */ + attr->layout_opt = UFFS_LAYOUT_UFFS; /* let UFFS handle layout */ +} + +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]); + + if(bIsFileSystemInited) return -4; + bIsFileSystemInited = 1; + +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + // init protected heap for native memory allocator + memory_pool = malloc(conf_memory_pool_size_kb * 1024); + if (memory_pool) + uffs_MemInitHeap(memory_pool, conf_memory_pool_size_kb * 1024); + else { + uffs_Perror(UFFS_ERR_SERIOUS, "Can't alloc memory (size = %dKB) for uffs.", conf_memory_pool_size_kb); + return -1; + } +#endif + + setup_emu_storage(&emu_storage); + setup_emu_private(&emu_private); + emu_storage._private = &emu_private; + + while (mtbl->dev) { + mtbl->dev->attr = &emu_storage; +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + uffs_MemSetupNativeAllocator(&mtbl->dev->mem); +#endif + +#if CONFIG_USE_SYSTEM_MEMORY_ALLOCATOR > 0 + uffs_MemSetupSystemAllocator(&mtbl->dev->mem); +#endif + uffs_fileem_setup_device(mtbl->dev); + uffs_RegisterMountTable(mtbl); + mtbl++; + } + + return uffs_InitMountTable() == U_SUCC ? 0 : -1; +} + +static int release_uffs_fs(void) +{ + int ret; + ret = uffs_ReleaseMountTable(); +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + if (memory_pool) { + free(memory_pool); + memory_pool = NULL; + } +#endif + 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]; + + 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_pages_data_size) < 1) + usage++; + if (conf_pages_data_size <= 0 || (conf_pages_data_size % 512)) + usage++; + } + else if (!strcmp(arg, "-s") || !strcmp(arg, "--spare-size")) { + if (++iarg >= argc) + usage++; + else if (sscanf(argv[iarg], "%i", &conf_pages_spare_size) < 1) + usage++; + if (conf_pages_spare_size < 16 || (conf_pages_spare_size % 4)) + 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 = 1; + } + 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 { + strcpy(script_file_name, argv[iarg]); + conf_exec_script = 1; + } + } +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + else if (!strcmp(arg, "-a") || !strcmp(arg, "--alloc")) { + if (++iarg > argc) + usage++; + else if (sscanf(argv[iarg], "%d", &conf_memory_pool_size_kb) < 1) + usage++; + if (conf_memory_pool_size_kb <= 0) + usage++; + } +#endif + else { + printf("Unknown option: %s, try %s --help\n", arg, argv[0]); + return -1; + } + } + else { + printf("Unexpected parameter: %s, try %s --help\n", arg, argv[0]); + return -1; + } + } + + if (usage) { + printf("Usage: %s [options]\n", argv[0]); + printf(" -h --help show usage\n"); + printf(" -c --command-line command line mode\n"); + printf(" -v --verbose verbose mode\n"); + printf(" -f --file uffs image file\n"); + printf(" -p --page-size page data size, default=512\n"); + printf(" -s --spare-size page spare size, default=16\n"); + printf(" -o --status-offset status byte offset, default=5\n"); + printf(" -b --block-pages pages per block\n"); + printf(" -t --total-blocks total blocks\n"); + printf(" -m --mount , for example: -m /,0,-1\n"); + printf(" -i --id-man set manufacture ID, default=0xEC\n"); + printf(" -e --exec execute a script file\n"); +#if CONFIG_USE_NATIVE_MEMORY_ALLOCATOR > 0 + printf(" -a --alloc allocate size(KB) of memory for uffs, default 100\n"); +#endif + printf("\n"); + + return -1; + } + + if (m_idx == 0) { + 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) { + printf ("Mount point: %s, start: %d, end: %d\n", m->mount, m->start_block, m->end_block); + m++; + } +} + +static void print_params(void) +{ + printf("uffs image file: %s\n", conf_emu_filename); + printf("page size: %d\n", conf_pages_data_size); + printf("page spare size: %d\n", conf_pages_spare_size); + printf("pages per block: %d\n", conf_pages_per_block); + printf("total blocks: %d\n", conf_total_blocks); +} + +static void exec_script() +{ + char line_buf[256]; + char *p; + FILE *fp; + + fp = fopen(script_file_name, "r"); + if (fp) { + memset(line_buf, 0, sizeof(line_buf)); + while (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; + } + if (conf_verbose_mode) + printf("%s\r\n", line_buf); + cliInterpret(line_buf); + memset(line_buf, 0, sizeof(line_buf)); + } + fclose(fp); + } +} + +int main(int argc, char *argv[]) +{ + + int ret; + + if (parse_options(argc, argv) < 0) { + return -1; + } + + if (conf_verbose_mode) { + print_mount_points(); + print_params(); + #if 0 + printf("TreeNode size: %d\n", sizeof(TreeNode)); + printf("struct BlockListSt: %d\n", sizeof(struct BlockListSt)); + printf("struct DirhSt: %d\n", sizeof(struct DirhSt)); + printf("struct FilehSt: %d\n", sizeof(struct FilehSt)); + printf("struct FdataSt: %d\n", sizeof(struct FdataSt)); + #endif + } + + 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_add_commandset(basic_cmdset); + if (conf_command_line_mode) { + if (conf_exec_script) { + exec_script(); + } + cliMain(); + } + else { + if (conf_exec_script) { + exec_script(); + } + else { + cliMain(); + } + } + + release_uffs_fs(); + + return 0; +} +#endif + + + diff --git a/components/dfs/filesystems/uffs/tools/chomp_uffs_perror.rb b/components/dfs/filesystems/uffs/tools/chomp_uffs_perror.rb new file mode 100644 index 0000000000..e8b9460ae5 --- /dev/null +++ b/components/dfs/filesystems/uffs/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 diff --git a/components/dfs/filesystems/uffs/tools/format_code.rb b/components/dfs/filesystems/uffs/tools/format_code.rb new file mode 100644 index 0000000000..7847db236a --- /dev/null +++ b/components/dfs/filesystems/uffs/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 diff --git a/components/dfs/filesystems/uffs/v1.3.2-4 b/components/dfs/filesystems/uffs/v1.3.2-4 new file mode 100644 index 0000000000..e69de29bb2