From 7b76fb6ecd572c72f537b32122a2f144a57b5115 Mon Sep 17 00:00:00 2001 From: "bernard.xiong@gmail.com" Date: Thu, 2 Sep 2010 23:55:14 +0000 Subject: [PATCH] add UFFS 1.3.2-4 (http://sourceforge.net/projects/uffs) git-svn-id: https://rt-thread.googlecode.com/svn/trunk@888 bbd45198-f89e-11dd-88c7-29a3b14d5316 --- components/dfs/filesystems/uffs/AUTHORS | 1 + .../dfs/filesystems/uffs/CMakeLists.txt | 3 + components/dfs/filesystems/uffs/COPYING | 339 ++++ components/dfs/filesystems/uffs/Doxyfile | 275 +++ components/dfs/filesystems/uffs/README | 213 +++ components/dfs/filesystems/uffs/TODO | 5 + .../uffs/doc/Understanding-UFFS.odp | Bin 0 -> 364916 bytes .../uffs/doc/Understanding-UFFS.pdf | Bin 0 -> 258120 bytes .../uffs/doc/uffs-serial-num-relationship.JPG | Bin 0 -> 26858 bytes .../dfs/filesystems/uffs/src/CMakeLists.txt | 4 + .../filesystems/uffs/src/emu/CMakeLists.txt | 5 + .../dfs/filesystems/uffs/src/emu/cmdline.c | 265 +++ .../dfs/filesystems/uffs/src/emu/cmdline.h | 64 + .../filesystems/uffs/src/emu/helper_cmds.c | 854 +++++++++ .../dfs/filesystems/uffs/src/emu/test_cmds.c | 172 ++ .../filesystems/uffs/src/emu/uffs_fileem.c | 475 +++++ .../filesystems/uffs/src/emu/uffs_fileem.h | 55 + .../filesystems/uffs/src/emu/uffs_os_posix.c | 104 ++ .../uffs/src/example/CMakeLists.txt | 12 + .../src/example/flash-interface-example.c | 310 ++++ .../uffs/src/example/static-mem-allocate.c | 161 ++ .../dfs/filesystems/uffs/src/inc/uffs/uffs.h | 139 ++ .../uffs/src/inc/uffs/uffs_badblock.h | 70 + .../uffs/src/inc/uffs/uffs_blockinfo.h | 107 ++ .../filesystems/uffs/src/inc/uffs/uffs_buf.h | 174 ++ .../uffs/src/inc/uffs/uffs_config.h | 277 +++ .../filesystems/uffs/src/inc/uffs/uffs_core.h | 59 + .../uffs/src/inc/uffs/uffs_device.h | 191 ++ .../filesystems/uffs/src/inc/uffs/uffs_ecc.h | 90 + .../filesystems/uffs/src/inc/uffs/uffs_fd.h | 150 ++ .../filesystems/uffs/src/inc/uffs/uffs_find.h | 74 + .../uffs/src/inc/uffs/uffs_flash.h | 274 +++ .../filesystems/uffs/src/inc/uffs/uffs_fs.h | 137 ++ .../filesystems/uffs/src/inc/uffs/uffs_mem.h | 130 ++ .../filesystems/uffs/src/inc/uffs/uffs_mtb.h | 90 + .../filesystems/uffs/src/inc/uffs/uffs_os.h | 65 + .../filesystems/uffs/src/inc/uffs/uffs_pool.h | 92 + .../uffs/src/inc/uffs/uffs_public.h | 243 +++ .../filesystems/uffs/src/inc/uffs/uffs_tree.h | 221 +++ .../uffs/src/inc/uffs/uffs_types.h | 156 ++ .../uffs/src/inc/uffs/uffs_utils.h | 85 + .../uffs/src/inc/uffs/uffs_version.h | 54 + .../filesystems/uffs/src/uffs/CMakeLists.txt | 49 + .../filesystems/uffs/src/uffs/uffs_badblock.c | 216 +++ .../uffs/src/uffs/uffs_blockinfo.c | 387 ++++ .../dfs/filesystems/uffs/src/uffs/uffs_buf.c | 1591 ++++++++++++++++ .../filesystems/uffs/src/uffs/uffs_debug.c | 144 ++ .../filesystems/uffs/src/uffs/uffs_device.c | 94 + .../dfs/filesystems/uffs/src/uffs/uffs_ecc.c | 357 ++++ .../dfs/filesystems/uffs/src/uffs/uffs_fd.c | 532 ++++++ .../dfs/filesystems/uffs/src/uffs/uffs_find.c | 360 ++++ .../filesystems/uffs/src/uffs/uffs_flash.c | 674 +++++++ .../dfs/filesystems/uffs/src/uffs/uffs_fs.c | 1627 +++++++++++++++++ .../dfs/filesystems/uffs/src/uffs/uffs_init.c | 144 ++ .../dfs/filesystems/uffs/src/uffs/uffs_mem.c | 902 +++++++++ .../dfs/filesystems/uffs/src/uffs/uffs_mtb.c | 247 +++ .../dfs/filesystems/uffs/src/uffs/uffs_pool.c | 343 ++++ .../filesystems/uffs/src/uffs/uffs_public.c | 533 ++++++ .../dfs/filesystems/uffs/src/uffs/uffs_tree.c | 1164 ++++++++++++ .../filesystems/uffs/src/uffs/uffs_utils.c | 195 ++ .../filesystems/uffs/src/uffs/uffs_version.c | 67 + .../filesystems/uffs/src/utils/CMakeLists.txt | 10 + .../dfs/filesystems/uffs/src/utils/mkuffs.c | 497 +++++ .../uffs/tools/chomp_uffs_perror.rb | 25 + .../dfs/filesystems/uffs/tools/format_code.rb | 24 + components/dfs/filesystems/uffs/v1.3.2-4 | 0 66 files changed, 16377 insertions(+) create mode 100644 components/dfs/filesystems/uffs/AUTHORS create mode 100644 components/dfs/filesystems/uffs/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/COPYING create mode 100644 components/dfs/filesystems/uffs/Doxyfile create mode 100644 components/dfs/filesystems/uffs/README create mode 100644 components/dfs/filesystems/uffs/TODO create mode 100644 components/dfs/filesystems/uffs/doc/Understanding-UFFS.odp create mode 100644 components/dfs/filesystems/uffs/doc/Understanding-UFFS.pdf create mode 100644 components/dfs/filesystems/uffs/doc/uffs-serial-num-relationship.JPG create mode 100644 components/dfs/filesystems/uffs/src/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/src/emu/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/src/emu/cmdline.c create mode 100644 components/dfs/filesystems/uffs/src/emu/cmdline.h create mode 100644 components/dfs/filesystems/uffs/src/emu/helper_cmds.c create mode 100644 components/dfs/filesystems/uffs/src/emu/test_cmds.c create mode 100644 components/dfs/filesystems/uffs/src/emu/uffs_fileem.c create mode 100644 components/dfs/filesystems/uffs/src/emu/uffs_fileem.h create mode 100644 components/dfs/filesystems/uffs/src/emu/uffs_os_posix.c create mode 100644 components/dfs/filesystems/uffs/src/example/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/src/example/flash-interface-example.c create mode 100644 components/dfs/filesystems/uffs/src/example/static-mem-allocate.c create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_badblock.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_blockinfo.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_buf.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_config.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_core.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_device.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_ecc.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_fd.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_find.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_flash.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_fs.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_mem.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_mtb.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_os.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_pool.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_public.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_tree.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_types.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_utils.h create mode 100644 components/dfs/filesystems/uffs/src/inc/uffs/uffs_version.h create mode 100644 components/dfs/filesystems/uffs/src/uffs/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_badblock.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_blockinfo.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_buf.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_debug.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_device.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_ecc.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_fd.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_find.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_flash.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_fs.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_init.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_mem.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_mtb.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_pool.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_public.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_tree.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_utils.c create mode 100644 components/dfs/filesystems/uffs/src/uffs/uffs_version.c create mode 100644 components/dfs/filesystems/uffs/src/utils/CMakeLists.txt create mode 100644 components/dfs/filesystems/uffs/src/utils/mkuffs.c create mode 100644 components/dfs/filesystems/uffs/tools/chomp_uffs_perror.rb create mode 100644 components/dfs/filesystems/uffs/tools/format_code.rb create mode 100644 components/dfs/filesystems/uffs/v1.3.2-4 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 0000000000000000000000000000000000000000..2cd17358181986a93f4bbe1de589c4ac330a6552 GIT binary patch literal 364916 zcmbTd1yo$kwl3N@1SdE&5+Jy{TW}5T1Pku22_8InaCaJ~ad!yr?(Xj7b^g8gx#!$5 z-hEPIt*%-ARn=OxzM3^#Sq=sk8vsBA037r*1=v597c&C@fWJ2+3Set#Yv$tVU}ogt zU}I@wn`7gV{8xyJmz#^1i$_e7o5{h>JWN?p3iTb~J4iWH8R<_d z0049hM7BhPhWySr7^FfxC}$NZF+jx_@gXDtYc8rF3IJ5cAio&FLBeDv(kco7fY(O= z;Cm1N@C1qaegFWtvH$=_UjP8UUjP7}LwfUP0RVs+LFSXFnuo!uepfwJL)7dic2*uV z>P`Ig0E!8#Bt;P@9>BRds10mjeRjUw90f0fA)zr_RQOq{==RN9ozeqo1DZ18rE>k- zpVy-1cm8787&NdDZ^IO}bIuh9I#`5U84(*gxN`{c#iho9O7~yEzp*}%0eJ8JlP*H; zzysYk7wWDYrujGj-wbMY`h5Qs{u>~U5ut8)JM8#V^hW_={MgX%v2XUWq2u+`@Xu57PXB(# zpV#wrwN8ZRcw3?o%SsDVvg+Xzrb`-l+2>=!KkvvV`@j8pUQeqzywn<8_j^9w&-(VY z?Ex?Aa8{hB_19x!hasom0w4a=|hrT zsdDnVSB!x)@17+2bASGP?97CFtB^|nnFS4Gq&ZtRK2|6!8Mw#vNCRztQl`>JdJF#M zANz8h_2*@s8Bb_K$!C!ZrK5YybyG3nI_BGpNjzpoiykhz@x(?K6b4R!sDRJH%`g4C z8nIkH3EpBwlo?d72eO?yX**dDithEdSgIFm8Fi5xiG(&@5VR3H zY@+EhmyyE47}rtwgw*}KuFDMGs=u7pw`oHGlE7-9R4#TjF1;~=*QnFzjNbb{Wv|d{fyW;I(I>9yj7t-5!`nucM=IJ+fUD%T*p9?hpjO zZ6^}5GDILmf5yg=_M?rP7^NWRm0Ru0k=uER%ez4zwPS)Q_LDjn?ww1p)xIRd|4=n4 zU2+;6x#hZD1-#MA=V4=7Ny0v7n*K7b(z?IkMbNB~U-5*atMqESdcgr*S;HySv`DGg z7@dlz<;uS&z(84zJEXH0?UBy8^P9NRgGKm|RX58n7)ANWi#S@6?Qo#u!RP#B5AC}} z$%jnp1)Um3`ph}8{zaoqG{=4YBXfoCjHum<`DcRU%i=a|c^>vPoaFVIEhlLA4zx76 zM|N!SML}Rb_~SfYHcd8aJhCud_7RC>9mVAg95I|c%yN;p&Wj=;a`X{>cb!ovMAe2W z!}Btms5^@ZT1nw5tAvgI^mF55;H8DX4$JR3xYy*HUyQoCv46XxGUcUJX%Fl3M8P>SnR%fCv zBYoArc8Pq=MM8a=-as6sE@>W3zmF}9+ll3VCqD)dXWWpIy-q)2dAU(imfIzCA*e3N zoTrG6vmUd3uNcVsVm$;d^uXn4ZtZ-Dn^+Cbq!gVlRD0dj#xhZC7)f%ml3BFFKKBK? zrNWG2`!2zOQ*f{dz;5@A_&{)otS;EC*5-=(bmHr=6~5;_&~<)tRm{E;r)FXSZqdNZ zTCH%!uI^67Z9)HBL0t|F!;E1at0&rHLXrE9XK2%jBwmmYi!FP{O_s57K&?j0g#*)~X16{Fk7=6v|Q@zZN!Cj%Z`4CD-GXSS=CGI&$AkSm4b#lLuTIsK| z$Rv&`=+r_UH?8VWYRZqM3SVtcU3-^sBI$Av{x4p zgrfJSFT(Ah|FrV1{8knXvNA~}oD6q0nhPH(&={=Ls@(28M@1S-$)ROPNr$`3jX#x| z!GW;*0Phn|oac;EJWGUxZurkna@alzd8z)XFUV};wm_n+f>)xz3V zIG>eH7CQs?ZQZP}lb`Uoo!L|H+@vfQe?$o4eC?XIW~E?nvO}NpaD+L4z0XGSs9Igk z0Vy)K^CKLeoz#47IQJ;x7!47cZnsNIGPjQSMQ0`ct^wWOs+Yoz2D<@MkS;LJmHYB2 zvj(*;OP%nY&32|`B+kkOiEe{9F18%3IK!4Nwd4V_H`U=lyN4cK33);badVVUiscT(ilC>u~0dZR19;>aNiKRc&RiB;Ct z8|?3@7UJ?Fo|P#e*u^G;0BPSPk;-pC{=C+eo9~c~xV?_G!M1%(w?~i|Nz-&ghvP|x zR!BG!jICD{YkUkhVdt@Pe$uNj_ z5ar!uVP=|YCcn)nZ1t*Vf@;(0EYS8$r6zBUtHCYU`O+;8PLD4QTz=;gH1Qd-ydc_kW)@9NqI@1J#1MHf__OBEe^3U1ygdn5#T} z@uW!_utA93ahFM(O7*Qqhg0X#iGO^2AHN3J_}I6FrzmKD;i>1PqxIR((o-KR#rT*~ znS0&sA1Zo&_`g%l|0gQ?Khyi>o;!g8VahhXQui7Jx)APd-_`KqP<_kh!pYhSv$}Zz zw8rb3OViPeIj8vh?wX@Jv=FyADB3?nC3TejlpiTd8h3v<={w!B+2otdNz`a)x+iQ3 zJUgJ272-@B9scRBXios1I4sIWV$AeJT25vLMxu#_{w|(|!N0EY6l-W2j zD*dT1_&Xvw0kvKkLC@8LG~vAC>K8(aO1Z!1rJBAZ40{qA=WGchNRslSnGF?RG%L$91xAE;w2w1;PBF9S>J; z)P~P(SwnLtVz!`x)g_0+-;LlBKIe~t+cQ_oAZM+5P;v|MC5K=G5vke69XHy*VWzX{ zqA}QQV(1PwBf5JhTKZ=?DtT)|_WfwAFYWZ1$j+Bfc-PsKaaq0%{uyHm!81TQPmhh& zM2UHj_&&E=JcXU_$rEed&u1H;I5p6k0*QuXJaVgDPLVfo`iSX}1P&i~ZFb*B`IwVu znQ!gQWRMv3M0`MU`z`;AD!*?42*y$y7~48O$uBY5C?qu1+#7k{DY&GVzOn5TY6`n6 zu=-;99@oyq3&@xJN-{5TCx#+?2w{!Z645w;sE-GSK_bm+v z(!yv`EAJ~evWCAB9Y|v99MAv`Lnkf)JS5P0AB)p@&i|BJSxTPJQAMV+#9ar4@Pk%k z!&UDGpvAU*S;cROos!>LwqjaC}6ux$uFB?C9YmTt4Dz3jMY&7yirvE0h)C)OKY z(co|*$c7lMK9&&IHXo%G*1*}=TK)%Xa{*-`lt0-$q0H=3p3k0yu=w~m4D>d>g6KiCk>MU1uPTh@H zvw8Tk=6)gx=VyNc)cjzl-|^t|gn>1;5_2gVTc}8>?rSWefjJGG$%Ue-`dV$9?fGCA+Z8FNEF^@;wno8PY!(?c4gJr){+7 zM*n>F=OBnQs3z!Wd@%#tNp&LybRH@aY?a8p5BKaij3mH`Jxa&#a)KQmFJe1ZzN)FW z0mV9Se>+#B*BD&rzuKb8v0hBo5+$mS>!IC`){jiDOqbAv0kJgJdf!dXaPQ_k6MFh& z-PJcEbpPBErE6T0FK*tuH#T2#`{3H%LillfPFby4fdjRGwbCI)Z#li>*rd9bjyaMp zwYk^r<$KJ}jd&Z|RX9@j1ahia=6C&!7mc?_aiN*2enY6%O+^eEPbVaf;$YRh3DoKU zK{l*61mL$(c5jUjMLpEyJaf$aicE}f||>@X{YaK1&}X`M^$G} z2)`8~MTfLZln_e~LZ*57ik!M5`S_K+` zlde0?m1?##U%&9~r*x+^9Qk5mfA%R68hn{p6Y2!p&BjhSLH;AXGVg>?Vv&TTc)yQy zP+-*#4p*MJLdn2!MS$Y(ZIu{JQ4{MHT)( zKrKBd6}&WyA;0X#6e|`ySrDRMr~SZEiR_c(fu69sJvL-@R01U}g>|>l@6zsJb-xj~ z|7n~+9qX8Q*5WgbX*L;OH@8O4ICceH#!~$#&7tx{NWD+%?5!9Hjvc743*1@N6L5;q z>~{io0bmu~o35m6MwiRQ6h}{x4w}a-j`W&y_xg|*mMpV%d4FH-!x^;F>6Uw^KUFVD zkBmmHduV3zc(V-6`S)mlv7YC9sy=*xnzf`vD(?Hbk8A^s401PjBL?~5>MqYRM#2s8 zr=ksT+nIUCfl9*Vi!Y3pM14nI%VRGVG_h#)^0I~UL{vwzYRWTSs-9+|&?*7no; zydR)B@F)(@eGoa&0ty~dmA-WfGa9X2xP2Yz5*h}dn^~KO+HRul;5yUpwAo}t)&MQA z?RWHyk*)=oYi^585ylFNmy$fy!CLpt@P0#zpsuw!jY}vTpCMQk`VKhF2RY_wE5Ozy|$G2yBe=PeQqpx zlwvlexv6AVX>qA7C|eo03XkOKxC{Ljsj1H3T|ld=%ceoO3D0SSK*2(UF!px9g2Z!` z-$Jgbs@pkAK?22i7}w4b^L6P8l^nL5aM>Oxb_d*?bY~wjBR3T$N4#;z?w4^Au>fcp zE;!mgrT+4bM zG)FhuGCZSg=}QdZ+=^G%+duQTSV)cCFr=f;+w?>q2=j)NPpmM`H%u0t1dzxd*EL-t?>;=}Tk>5jm{hmumudgC5%(m7fMiZ(TX28J4$3%7wYwEp-mwkiEeQP|eew$# zB5e74{^{$(Qc3}0e^;RMat{d8bm}b+hQ`TIdJ2`~p;>FDm z^Ul%Z=o%gmuC0c;pABsR=^;(G>#HH9{UyK9jSmmi1|wYxET!GI zQQ!Q_FZX1$cDD8nox8Lgs+BWmIZ;j*<#z#g_6|h52U_eAbwoEfl+jt$Vjn}N7m@Ifch{)~PH-nQ^nDel z{&$Mn%KTeF-D!eD23nt=9ee(acfV+lix#+8gRT!fetLIZi+vU#?k5+E)vI>F73dkr zfEvgvczADbEwROmaQ2Lz%*Xyb8HVSPWPChB;i)w;=Md|%(l{gk=mn(1ISJw}NC&87 z+93-Bdq&9P*=BCQ@-`)QoKmQTO5rY~k|A=JNv}8?lEMD0AtjGdMn&{S;NxHc`0vAN zshuJTQ&RfmQRpEh2cOaDHuNv6g8;LUyNZiTT)g%jY95)HLE|SvbA8`?=!o9bXihNb z!3Jb8UOQ&6bBj8Pu%rj?&8W$~gw(a#Kr3iU8x##}hEd(THK)uRM3dCAdW_6PGY$sN zO~-oaey!hcob1+4p!bJvK@_14E_==6wFD#LN7&^na3D~?pcoY zubNw_DjO98ujae&H%wHjlzY>-3Qu0XJ**B%6JL`w7ned97@}C*Lf4VR(sZ5mL<1kk zIPF!UgA-^sq-UY)fXey@LUe=!d?33Ya>|XWl*0@i!T||4<#}hy(yi;~>R|9hy_cCN zeg@?TmyX$n<<=_GV6CfltNv{G=lYLBN$}0FrpNiOxdT8%==iy#h30xyC@qT3S=TBF1f{c9JDjw7vj)of8dA=f`v#6u=R z=GirPIz0?);QCaWM+l)2N4G;sSbduc*6xcnYGFNvhqVo=NuH+qM{>N4hoK6#TH^B9 zg*hHzZL_nq$**s0eO<~u;4YV(l%8!`HKI#|EV5y|M|<~} zTISZrR}W44i9P3i4k;@-BpMn6O{OX)opT_8JXX_@V;S(Ct=kmxRdJiX;&4n5{}3l9 zpt2~jT4&HiozkI1huw9^o)3&hCNZ1zdxc?}8=YqmL?CaG*(iP;7JI>DM<;t3ZnfzY z&XjL#!EfL8{=l@Z8qS~4u0_6wcSfWxc!O_)R=#4^s`D79F+2P}gdQ)nHUzUGqzX0SB=LFTYe7~d99l_HoGYJtLz^>vn z?G$WHA3;{>|1Hgf$Ev+9T14tR1+AMmi_tz(IgAVbH>aG1ty7gwGF z$=mhhM}Cnm#L_b=)keT zW$Wo8CgS%Qqp@ne+j{kx$pnR!S`e;lR6&FY-t6!1+BSHi;yQ~TdOY4lGfPRVCG`3izwj70(d*^>Xm+wo|Fp5 z78%Z>To4O*0Fnn=ALGlrh<<6)SWh718#N_gt%rsj=^&~zA*c@0Q^KgY2%lDQ;T@ONC|A8(}*-zl@$mi@|R_xp|vy zy)Ky41o8OS=0*RuT-vE;%vm**x2vPLgV&wz!Q$Bawkn~7cFL5$Dw_IR+GAc#GQ4*P z6Fwr<2q%9>3=2H5wg{w}=m)?uEp4!Uwagn#Lx1P&Ln6ZiM9M;o^Q z9?TXs2Z6a^>|x6cj;?^e8|hqPT0FK=tiBytQJvM7fM5;rap0rG@u1odTjF?v154tt zxQ4^VCrP^n;?Xbg+eGPXwD3+#mg~&>3>B=6%)v(OaC=ESP?$anjhu)rQGw0C8t)EV z!bk+ZV+ZByYe*im1Z|ESPo!+JZ6%lfOT_WeX&D}j`?+(aGWUT)Jj zmsM7${*3K!d`$Z(f}8j_k&R%%IPKi~#NkG^b+ic=@XEHz(tq=w3vP9wq4aBZMsmuFc>d&kHss5D0{IT%ARN0~!9y6O7o57uI(22Ut z;@qAW3&goj$FEHPVO4YuYYh=}o1Y=)_gMk=|D4{h@BUBL7xUpS9cvx%Dlq^H1c2Hf1+XL3EhH!t)x|&@my?E3uvuqP_m$-ode^8ja%qwxJ=Yfz&qqoUS z>~1@}zVf6#WKfxJz6xG_gdF<1LTF;02DOyF?W1~Sete#bF}h+j^s*>GlXL7YyY9@p zG34yh2ehMUR!dcvu$?_kK1U`&^1O+L@SCjZqfrETn6@MSxKKb4V)sQ#sH_ahN;;H- z0vMu=s8`%~tk-|}=t6Nz!QC>!g|vEVvx@UEP~|>zH}f>fEf4Z&jQHxHw+D7^+kk2x zFGL4q7Qu@_+g53nI)3*yLNyWm;ux^vmKo9)SJTt*kAv!qI$#n}1cDP8S;7?QkFTW` z3f)X00#zwgE#E`74guAa->qxHKqjD`N`awLqMv374hpT2Pvr6mXS`rVv4#?F@S{Hq zK>~@{@v<3&xSdUVT>xR%OswmqddtDI!TQ%}ni+csl={g77Yu`P183kiS9S1r5YXj| z+2B|9nH|<2ii4a;EmaH5my2*5wy8YoXPgjBXI8*XEF-1hrZQ*tqtHeKyz%%mMIy6& zeaUegzX+BF3;pGzzYUHRYph;}A^NcCT4UV4Urf#XJVXkE_~Ol=%kR3m0H`QaowpWh zLq?WzY4M8O{vEbY#b_AMI5dY353m?1qe_aOXL12OVHUq%&}jox{1&(IZGntoGDF>8 zmUF+LL$R4^RB$a8-FU)X`E)a4qbC?j&1?C1N*RPcJ>nn;r616@|Y*-P{wiX1g!ipQAWnPAB_i{VRtHTHDBzd z4Nd~JOjMJP=rk}Gl>24QqWx-M0*^gRG#6obAovd~desFqkNQ1~7Wiz@t@pyNj1pFc z-;8$IwqDE9Z1wGX<#l_BXBBIi1YrM=}()LOr#0guALG`vqDGQC)VRpG& zpt;_^%<Nr(f2;&Fl0|yvv%xKQ5**2A6S+16B zXZN|CZrNQAXKzkVV#OdO$SDi6sOk$AaC@APZa>uBbOe@9?2GnsCbCO9njWF~()r>r zD8&HJ92?pkvV;kqB-1h~Y-OX?#z=KbQQV!g7^6hcOu)U@3^QM}{Q41-uA%)X67mzS zwQ7jC65SaHd7{ei_bQbl8h&#tAs1dLC0&>s^N ze|ZPMfEZQ&$A^sTG9U;50}cK6f;d>n1&0U+2MY`L4gmok5#=2U3i3N-WK=XP3{*5s zG-PB9TnxzsM!CX-Jf2lTnJJ)sDIpl zdIlL|SU7kDM5K4fP>@0UkNa2he@nywKtsX6K*PfP#SN*(_wVGuV!~mOvx>sM|7?Wt z!4aFyKQ;%ELae$AM`iMYlHJ%T0O=hr9zFpf)kkU?S~?C+E^Z!PKJiZyl2Xz#vZ`w8 z8k$<#Iwq!OU(GEnt(;w4-P}Dqy#j-RLqdOqg~!DwBqk;QN=ePl%P%M_DlRFlsjaIA zH#9aiclY%6^$!dV4Npza%+Ad(Kz4mw+dI2^`v-?dmsi&}w|Dmsk58Bo$k4E`Ft7-J zAwxmCLv9#MSU7T4cr4M+2u6cdq`Lfz`XA8$ z?=T1a{{sDAnEwm%pH2WKBA^)H3l~C3ZwEH3Fcc6Pe->W$4K50$UHcZo-ae z&JkNuv8ptcnF?JSjf==yV)F}p*!`vN{LF-JV}@9Mq#{h5;|NC@a|+SyJ2&r;|Qla6nznNBjYp7=~!zO-Wc@G8~o@ zy-G}kyBQj*CqHU_z9_qsy3LF@%0B>uxcpiO z&HBpoPQiBVfz)8y<(I46DAV4!;xB)k*=`%DaryW2IoX!n)v3)sMWrq1YARN8zfMm- zV*EUDgVrMn`tBI%b~xvcQEsun`xEMIg7YTpgkbku_hXgxLQ6qGZ^c4q|7fa1- zRD+?_Blni@fUkR>lN?i7>1#meKCT0AG?^gcnC|7MxG#JR?yMh^Wm5EzO{L3r4h(V@ z=<6XVscRdaM##0S`t-geexhD{j2Iv05jr*{o?ylJcE&*e!d7!zIKA95%^U&amN1j! zeZObdD{J)Z`FeG}L_4>Zt&hGe_Gy=--Fg&!P zu!rl-jEZ0rlahn`D90kbsQf|Eg@^Ia3o*NT$cNWt0?<&$nRM~wWmr_zO9-oiqrOIe z0I(Mq&qJ3c>N7<9(aQ>Ehm)zF1`Y~;#Z3044Xu*%RHpjzzfG4Fq=E2yxM`^53&sl3 zF*A=zOa1_?8>|!-EFSSR=Wc-}vQ0BL_u7l;fegL1O=q310-5zgR(}9UFQTQoL*92C_Nzo+D?k~UC# zW2ZS2H&;JBr1jvIUk}DqNpYbel6`r@VV_I{{rzOX`pYGY} z#OGF-FKjxhvfktMBf9IxqQZVfSEg{3U&^aACmdK*DX7%OA4Z=UduhMTkh;6`hB{5S zM_**mwCBs3q$nK5U9F$}4fBQFKp(xU4cc3-*cFkfvVhJr&LM#H1h3@fkPCrxLm1kF z$=AS})x^<}@-y?3;Oa!oPbD%s^9JG1`ZwR+wKDIPk)rRI$KLJp0EL?#CKk#iNW0e& z1?O%rUKGf7aGRVE@NaMeFG+ir&;#qd8O7#Bz?Ou&svoq~Dn!p)mJ+)M81pJpaDuoo zEaoorv_@EByfJ$>r5O~r$5_XS=jXC&TxU}M0D3*`%Qx$$?d&*I*;jhwq!r!Z^#p=k z&m%8&5T8a)n&%K+6te#SI)&fH(xlM`f$UptMUk95HY9i<_rDRp=g^ZMes`YF3@Kgx z)k^J-lQ6OJnk(s)G%2;miacTh43@8F;sd~Mv zS1_AbpJJP^tkgW}geL0b0yvNA#^e8&GaM#M+_R<&yN~$6{NtXTwV2}+0_d0J51dgdzFupz#<3hJdbBNs zL20jTywpHpK6`N;9#8d%#4)e}h4eIeQyboUgqT;C4*O)6aP()!@rhaIY<^OdMyp3cq%*C&MV3!)DfDaFD|J7LM~Gu_7$I92w*9~WC&xbV z;v*+*3oq2w<{|qn?pCcY%~3%$rM89~IHcz&fie5a@q9#@8YkZ1%4f2!Vh-Wq~Lkv3B64?^}W0of7s*2yO`1BI1rMY@FkHrsG<(pzapj>t3~WnSoVO+z}jx zlC7l$o-sgVByyWVz`TT5W$NV2g|ehuW4>fRzJD&cc1`x$Y;+C3| z3>isb!=AP7XWK$??5`7W_FN3aWcyZ?6<(1fx7_?Rv%&l-FS1@0wccA=R2R1|$%GD% zAQB7mB?q>aXpx=R!CO5y9K2=IDGqOI>dTNqSGX{(<#-ZhG*Da?x1ph|Bl!1DpNhtQ zQMGPobEM`{eSdZNQS!8?>z5DGAyY9l{BE_FiF!XhZf~}-lZb4EySt^|W*I^(CPDid znH)rSKk!Q;m=dr zYl;&r9<1s;!zPc-caoH45@GBTP&rUi#T0zK^y-z*!t_f`bAP(xj_`& z*n^T~Vg4MRM(oda9}{PGCL~x4sO>__wi`yJI(W5ze!f-j)E zLyki^DSFoKJS6zZt4ItOdOJJBqqwF?o$~E7natCR_gW9uTY}I}=bu|=%5^*qq|58Q z9SpQTiwMa_{2YLd#A5G)Z0@xtZ^lEsMq9*-5BL)JYq2#=`}@}na=xqn9OmsupO{mb z{Kf38=5u(-J&*}EZJ#^QKD6b%O1Pxy9dURvML7d&ZbCzLt`6sb9~j_VXPUa}+NLCO zX&5X&sjW~@XF6)sF3xbdVXtn9551@d?Qu9AK55}*lwUxNP#>M8JOQ#j!8#k0wmq$y z8nMOp922|9cA?yl@uQEv;>>{QUpd; zA3vT}Pg*oHl@-14`tn_I1**EYs>^&8k)|hr1v?-Nd;I}CJ&!{j&U@jVtXCW;6))=O zA9IIKD{$4?eSqdR#vaCx4q>RIz8=*Gr9H`HxxCA8zlh&NOM|a3c&+TY7d!g0RBn;I z%p!f)w55n{mvLc98~HeX=QEY2aIv!KLQ}c6MGK@#RDR4O!$5k98EyhucVE2-Uv_pd z*xWpPjo#Dxz+Vspi&dOQ9@C&?K+kbXpTUGz=nLObCy+^2Ylv(qgi|-wKHaq2JdTQj9tJ)*U zwKr#)21k6dJ7vHa|2EEf7Mu$+$`RjA5((7|TxO(F>YbG#yL5HB@NoWysj-_M>A9(?6TbpS)<=}u!d&z!9?$X=c8Ag7e;R7VwbONzG$Izvg(55@5{bjIEk;z zjXSsFNgavSCEME6CVqyb$H{YtU)C*C7&X+NdBe%bvB3LFjxV1~O^U2!z3%A-ucEm7 zqBhY9R(d>hSC{&*)8I17;W>j-G@XpDlub!I&JC4c@E^>|wLI1s4W7EkzSVUGs68uQ zo5MufEL)F%3!SlfMi0Ovf@>?SDBnNB3|2OvE$v1nkGOiQf1JiyrN zZw9rHOeBgBAO9g*xWLRqT+f%@fbexL&8*axH)LMz#|S*s*q(gUDjx}|p?Em!%z3Np zrO>ots7zzkw2Lj@8bAKj23M3l*5d~FG#qMpn>bU+QJYYym)1T)0$WF5Jn-r@7|)%z zQ_uG9a4Jr+1*p_h8P2svFw04_JW8t2>{6RVW2=~GGqShohCA%UmPZ{Q;0w`^DhOk` z9x&q$kITHxG+5?_Dv83?=y$S-WpCh$@Br5mPlm&f44$=G!{%QNdf#=v<+fivuySSw zKC>QhCpQr4@kMCM5&OB&nJrBT3EAxU6g{d*Is0g~!P)H(lF(fqG+*x3J26qlD`mP_ z%s2o95kc#y$6Q|h(NF2kK|i&ZMt4W>7WNw5DVaW~l8D%+#VNu|*m-K~#YWj~pEBty zY5Cg}uA37_FKgL7qQPG-c)_bqvpr3i*9^`>2-@Whgk<+K=1^PO~P@}fk)WO)O;9B;Oo1%8hQ3vc8IF)o%K z>gd%-0E6BWB+zteqp4NHRIc`Wo3FL=ry?sjZN80-R9ETI3OsFWN6u3jhVR!0HPlp7 zbp+fzADnX|W$u)gQ+W1L&oEOod^7aAZnIYoRE#)LrYtG$@0Fi-2*mi@*@+q_vkeOn zJo<8W%LJmruTa_l(JBEN%318g8O?XA#(L8u^leqh(mzUznG7pGcrn@*h8L^=cKM(K_S_Rzwb-*7?>+-kzD7?v&&etek|FmU|?z@|7lX4q2@Lxk2%QM>As{f^Uq zNo+!qNk79es=B#XLbc;Znqt_8B^D zT*$j8tH`#TW*Uh3Zf1Rxe|nn#yrmR2dQscWhZCHc!_ge|+wMd3CQc+nWgX4Xrek#A zTrS$Vs=Uf>0C?c@CEg~7A*mm+W$KKIxcN4S5o>8DKc6Bmy^Pp*^V*cyE5>R+?M~xT z(tpE~(;n?OdD03@+OdR&x=cf!f$6d2mTt};O29dfc>U2s{p%)2OFFyXL_7VJt#GXm ze}T?JlmUC?dULPlXA#!Al1=QG+h8v)J{1iO?mgd*&GDHS1Rt50ejQ+YPQGpm$D(Ko z)sNliEk6x|QYi|OSzV=B6iu0Oq4Ya<7Ru^=*k344NU&(xQ61r#^^-GutE8D7;B9N2 z%IMhh8n9xyib@et6+!6CT_w^*TS%o~!!iRo*23~ojmUoJ;Qq1y(9Pg&g-%*aW*dS< z84%FYGF@TIZ2NOX+#!wYM)MEAd||r&8@i!UNhzGsn75M7`{`2~-D+MnUTJl%lr%6tOlPGu@-k+3MHHBKkSmO`=3B7B zxar!kbg(jrK-%lcdp^%Ge7YCv+O+vhx0<;gcK8PKpy_!LA?r(_J!>-MDp&t=$xvIL zZ>ecT*!2T80C&7(oQG^%$bVBo=vjF>W(oGg-dEcI^hB<7T%$~B-s9VfkIQk@C-mCu z&i%r+q#<7V>)ko7EU#x7IDHctWAIl+R%qT0U-*jeD*yJX^0shisA*u-u@C>IRlHuy1CuUmX z=5KSdrwcC<$EBuG?mj;I9~4?HzIdPGHf8#=Ouk;pJ<{~kZGlP=3}_eg7KPv+xYfZA zQ8`xJnFKA9#@8u?x(_cY{X59R@r?j{ZZc@>)V(*dqoOT@2u|JOm`ATRAyK2qel$Fi zF1KRRlT;PNA%$y-kISm<6ZTk3Tav6f-7=>K*3&P-<#JYxl{bgO3Iq^)RR^h?@^u#% zLG_qMzxOY;69U$o{G#q93x~~{z0Br}!#S~SCMZswm(xOtd+oTd$@Vv^a>~~N-hLC( zXBtb~N~{FY&T&B|nZGK-8``NAEJ~Sk2Uy3g+^^aOa2)tVk zU4@I()ph?|hvJ?OY9e#=wt8)TLzv>mC7hT#t>DDZo!_^Oa4a-nM&_#`vBLa`2jiz? zbVR@Zs=Bq7x6xEj&sw}>DPuw&dqx(kZ=|>XWu@)H-CWpXV?VWDaTV?8b7Y$Q6?51+ z*f01}Y2*A3VX6F-bH7c_l7bCbz|Cc*u=t4Y3VS)79-gzQ1ivEhBe*3;0nF{qUDKCb z4VwD{poOEPjZ+K7w-dS9n5S~PX_^&f(qv{IXlK&rJRcBL++8$7DZ#GD{%&^Fzrp4O z&39kP-g=389(wDoFUGjr9puBRffAJ-LL7hyOZdx$-}n@F^6(A!wb)hg1OKUHw{`Fg zi@?6e#h;V#O;uWHxlCYBO?58nV ztl1CN9ZUPY2Gq}H9m&q-O!*qTXX4#?mPCqMvGgQ7$5RofAE`s=iz%0e(-2p?zOZ%3 zKRwSmPaNf7$IGCHoxVsF$;MTT{HnsgSB*w*g#39{yV-YlEljA3IZ3p0d{t=Rx#)&7 zD|UL%D>;C399dSy*Ik}{2*Y-`0$2=5ahFwJ52ffTM-%B{R{4XyYpexviwGbW7DqErnh(Ewa`S~SzzYMyC?TM=i2#p ztVIdulB3Dx2VI>HtmaNigXKlJVORlz@}S4% zr3!d+Uya9}imc>sExzorV(qm#D@`sNb)0HxmErAt;5caGfh(vS>q|yaY2b-vnS-8{ z$}{}L{i_}6uD(9#y!4a|bVH^f-M^n#%q!HAGTrhlT&DrTcKhdP3rlBA74)QZ{(bO* z^i%Wgg9k$bE9GeDb-tgU9}5=~_4hC6&iAq3myOdrz8yz`ix?}Fs#_Xc8cx$j==S=> zdAnoV$*c4KJoqudNfU6JhpJ+sr!%v�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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..489022b3dca4d57284d3dc2cdad83d488a1218c6 GIT binary patch literal 258120 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%0D(r{s-DvG|+rtG&T)i{Cn-b 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 z[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

xpB^reWMH98HERr%(=_5D{=3FpJy7x__y} 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

IIgJ1m)V>QInRCgfv@2A83$0 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 M^a}00UHIUH||9 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2cdbbcd2b2a4e6182fc96780f017f5e77467d4eb GIT binary patch literal 26858 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

$s5UkQcXnD2HGFh< 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 literal 0 HcmV?d00001 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 -- GitLab

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