diff --git a/base/scripts/mount.tb b/base/scripts/mount.tb
index c2ed8a8..22ade13 100644
--- a/base/scripts/mount.tb
+++ b/base/scripts/mount.tb
@@ -4,3 +4,4 @@ setlogcmds yes
# MOUNTPOINT FILESYSTEM DEVICE NAME DO FORMATTING
$fs mount -mp uhome -fs LittleFS -dev atasd0mp2 -fmt no
+$fs mount -mp sys -fs FAT16 -dev atasd0mp1 -fmt no
diff --git a/kernel/Makefile b/kernel/Makefile
index dede7e9..9143887 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -22,6 +22,8 @@ CFLAGS += -I. \
-DLFS_NO_WARN \
-DLFS_NO_ERROR \
-DFASTLZ_USE_MEMMOVE=0 \
+ -DFATFS_MAX_OPEN_FILES=128 \
+ -D"FAT_PRINTF(a)"
include arch/$(ARCH)/$(ARCH).mk
@@ -45,6 +47,8 @@ SRCFILES += $(call GRABSRC, \
fs/kvfs \
fs/littlefs \
fs/portlfs \
+ fs/fatfs \
+ fs/portfatfs \
baseimg \
proc \
std \
diff --git a/kernel/fs/fatfs/LICENSE b/kernel/fs/fatfs/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/kernel/fs/fatfs/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state 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 3 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, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU 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. But first, please read
+.
diff --git a/kernel/fs/fatfs/README.md b/kernel/fs/fatfs/README.md
new file mode 100644
index 0000000..1c34688
--- /dev/null
+++ b/kernel/fs/fatfs/README.md
@@ -0,0 +1,167 @@
+### FAT16/32 File System Library
+
+Github: [http://github.com/ultraembedded/fat_io_lib](https://github.com/ultraembedded/fat_io_lib)
+
+#### Intro
+
+Designed for low memory embedded systems back in 2003, this project is a multi-purpose platform independent C code implementation of a FAT16 & FAT32 driver with read & write support.
+
+The library provides stdio like interface functions such as fopen(), fgetc(), fputc(), fread(), fwrite() etc, allowing existing applications to be ported easily using a familiar API.
+The project is aimed at applications which require file system support such as MP3 players, data loggers, etc and has a low memory footprint with customizable build options to enable it to run on platforms such as the Atmel AVR, ARM & PIC microcontrollers.
+
+The source code is available for free under GPL license, or alternatively, with a commercial compatible license for a small donation.
+
+This library has been used in many open source projects including;
+* Aleph - Open source sound computer.
+* IV:MP - Grand Theft Auto: IV multiplayer game mod.
+* hxcfloppyemu - HxC Floppy Drive Emulator.
+
+#### Features
+
+* Standard file I/O API (fopen(), fread(), fwrite(), etc)
+* FAT16/FAT32 support (read + write)
+* Long filename support (optional)
+* Format function (optional)
+* Directory listing (optional)
+* Buffering & caching for higher performance (optional)
+
+#### API
+
+The following file IO API is provided:
+
+* fopen
+* fclose
+* fread
+* fwrite
+* fputc
+* fputs
+* fgetc
+* fflush
+* fgetpos
+* fseek
+* ftell
+* feof
+* remove
+
+Just add sector read & write functions for the media/platform you are using for a complete file system!
+
+#### Testing
+
+Each release of the project is tested using self verifying test benches to ensure validity and to protect against regressions (not currently released).
+
+#### Commercial
+
+If you would like to use this code in a commercial project with a closed source compatible license, please contact me.
+
+#### Configuration
+See the following defines in src/fat_opts.h:
+
+```
+FATFS_IS_LITTLE_ENDIAN [1/0]
+ Which endian is your system? Set to 1 for little endian, 0 for big endian.
+
+FATFS_MAX_LONG_FILENAME [260]
+ By default, 260 characters (max LFN length). Increase this to support greater path depths.
+
+FATFS_MAX_OPEN_FILES
+ The more files you wish to have concurrently open, the greater this number should be.
+ This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).
+
+FAT_BUFFER_SECTORS
+ Minimum is 1, more increases performance.
+ This defines how many FAT sectors can be buffered per FAT_BUFFER entry.
+
+FAT_BUFFERS
+ Minimum is 1, more increases performance.
+ This defines how many FAT buffer entries are available.
+ Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE
+
+FATFS_INC_WRITE_SUPPORT
+ Support file write functionality.
+
+FAT_SECTOR_SIZE
+ Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).
+
+FAT_PRINTF
+ A define that allows the File IO library to print to console/stdout.
+ Provide your own printf function if printf not available.
+
+FAT_CLUSTER_CACHE_ENTRIES
+ Size of cluster chain cache (can be undefined if not required).
+ Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+ Improves access speed considerably.
+
+FATFS_INC_LFN_SUPPORT [1/0]
+ Enable/Disable support for long filenames.
+
+FATFS_DIR_LIST_SUPPORT [1/0]
+ Include support for directory listing.
+
+FATFS_INC_TIME_DATE_SUPPORT [1/0]
+ Use time/date functions provided by time.h to update creation & modification timestamps.
+
+FATFS_INC_FORMAT_SUPPORT
+ Include support for formatting disks (FAT16 only).
+
+FAT_PRINTF_NOINC_STDIO
+ Disable use of printf & inclusion of stdio.h
+```
+
+
+#### Interfacing to storage media
+```
+-----------------------------------------------------------------
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+-----------------------------------------------------------------
+Params:
+ Sector: 32-bit sector number
+ Buffer: Target buffer to read n sectors of data into.
+ Sector_count: Number of sectors to read
+
+Return:
+ int, 1 = success, 0 = failure.
+
+Description:
+ Application/target specific disk/media read function.
+ Sector number (sectors are usually 512 byte pages) to read.
+
+-----------------------------------------------------------------
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)
+-----------------------------------------------------------------
+
+Params:
+ Sector: 32-bit sector number
+ Buffer: Target buffer to write n sectors of data from.
+ Sector_count: Number of sectors to write.
+
+Return:
+ int, 1 = success, 0 = failure.
+
+Description:
+ Application/target specific disk/media write function.
+ Sector number (sectors are usually 512 byte pages) to write to.
+
+
+-----------------------------------------------------------------
+Use the following API to attach the media IO functions to the File IO library;
+
+fl_attach_media(media_read, media_write);
+```
+
+#### History
+
+* v2.6.11 - Fix compilation with GCC on 64-bit machines
+* v2.6.10 - Added support for FAT32 format.
+* v2.6.9 - Added support for time & date handling.
+* v2.6.8 - Fixed error with FSINFO sector write.
+* v2.6.7 - Added fgets(). Fixed C warnings, removed dependency on some string.h functions.
+* v2.6.6 - Massive read + write performance improvements.
+* v2.6.5 - Bug fixes for big endian systems.
+* v2.6.4 - Further bug fixes and performance improvements for write operations.
+* v2.6.3 - Performance improvements, FAT16 formatting support. Various bug fixes
+* v2.6 - Basic support for FAT16 added
+* v2.5 - Code cleaned up. Many bugs fixed. Thread safety functions added.
+* v2.x - Write support added as well as better stdio like API.
+* v1.0 - Rewrite of all code to enable multiple files to be opened and provides a better file API.
+* v0.1b - fopen(), fgetc(), fopenDIR() using new software stack for IDE drives and FAT32 access.
+* v0.1a - First release; fopen(), fgetc() unbuffered reads.... (27/12/03)
\ No newline at end of file
diff --git a/kernel/fs/fatfs/fat_access.c b/kernel/fs/fatfs/fat_access.c
new file mode 100644
index 0000000..f5d8990
--- /dev/null
+++ b/kernel/fs/fatfs/fat_access.c
@@ -0,0 +1,905 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_context.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fat_ctx *ctx, struct fatfs *fs)
+{
+ uint8 num_of_fats;
+ uint16 reserved_sectors;
+ uint32 FATSz;
+ uint32 root_dir_sectors;
+ uint32 total_sectors;
+ uint32 data_sectors;
+ uint32 count_of_clusters;
+ uint8 valid_partition = 0;
+
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have a read function (write function is optional)
+ if (!fs->disk_io.read_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // MBR: Sector 0 on the disk
+ // NOTE: Some removeable media does not have this.
+
+ // Load MBR (LBA 0) into the 512 byte buffer
+ if (!fs->disk_io.read_media(ctx, 0, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make Sure 0x55 and 0xAA are at end of sector
+ // (this should be the case regardless of the MBR or boot sector)
+ if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Now check again using the access function to prove endian conversion function
+ if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+ return FAT_INIT_ENDIAN_ERROR;
+
+ // Verify packed structures
+ if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+ return FAT_INIT_STRUCT_PACKING;
+
+ // Check the partition type code
+ switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+ {
+ case 0x0B:
+ case 0x06:
+ case 0x0C:
+ case 0x0E:
+ case 0x0F:
+ case 0x05:
+ valid_partition = 1;
+ break;
+ case 0x00:
+ valid_partition = 0;
+ break;
+ default:
+ if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+ valid_partition = 1;
+ break;
+ }
+
+ // Read LBA Begin for the file system
+ if (valid_partition)
+ fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+ // Else possibly MBR less disk
+ else
+ fs->lba_begin = 0;
+
+ // Load Volume 1 table into sector buffer
+ // (We may already have this in the buffer if MBR less drive!)
+ if (!fs->disk_io.read_media(ctx, fs->lba_begin, fs->currentsector.sector, 1))
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Make sure there are 512 bytes per cluster
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+ return FAT_INIT_INVALID_SECTOR_SIZE;
+
+ // Load Parameters of FAT partition
+ fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+ reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+ num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+ fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ // For FAT32 (which this may be)
+ fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+ fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+ if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+ return FAT_INIT_INVALID_SIGNATURE;
+
+ // Calculate the root dir sectors
+ root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+ FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+ else
+ FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+ if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+ total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+ else
+ total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+ data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+ // Find out which version of FAT this is...
+ if (fs->sectors_per_cluster != 0)
+ {
+ count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+ if(count_of_clusters < 4085)
+ // Volume is FAT12
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+ else if(count_of_clusters < 65525)
+ {
+ // Clear this FAT32 specific param
+ fs->rootdir_first_cluster = 0;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+ return FAT_INIT_OK;
+ }
+ else
+ {
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+ return FAT_INIT_OK;
+ }
+ }
+ else
+ return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+ else
+ return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fat_ctx *ctx, struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.read_media(ctx, lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fat_ctx *ctx, struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+ return fs->disk_io.write_media(ctx, lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+ uint32 sector_to_read = 0;
+ uint32 cluster_to_read = 0;
+ uint32 cluster_chain = 0;
+ uint32 i;
+ uint32 lba;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+ {
+ if (offset < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+ else
+ return 0;
+ }
+ // FAT16/32 Other
+ else
+ {
+ // Set start of cluster chain to initial value
+ cluster_chain = start_cluster;
+
+ // Find parameters
+ cluster_to_read = offset / fs->sectors_per_cluster;
+ sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+ // Follow chain to find cluster to read
+ for (i=0; idisk_io.read_media(ctx, lba, target, 1);
+ // Else read sector if not already loaded
+ else if (lba != fs->currentsector.address)
+ {
+ fs->currentsector.address = lba;
+ return fs->disk_io.read_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16, there are a limited amount of sectors in root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Read from disk
+ return fs->disk_io.read_media(ctx, lba, target, 1);
+ }
+ else
+ {
+ // Calculate read address
+ fs->currentsector.address = lba;
+
+ // Read from disk
+ return fs->disk_io.read_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate read address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(ctx, lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Read from disk
+ return fs->disk_io.read_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // FAT16 Root directory
+ if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+ {
+ uint32 lba;
+
+ // In FAT16 we cannot extend the root dir!
+ if (sector < fs->rootdir_sectors)
+ lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+ else
+ return 0;
+
+ // User target buffer passed in
+ if (target)
+ {
+ // Write to disk
+ return fs->disk_io.write_media(ctx, lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = lba;
+
+ // Write to disk
+ return fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ // FAT16/32 Other
+ else
+ {
+ // User target buffer passed in
+ if (target)
+ {
+ // Calculate write address
+ uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(ctx, lba, target, 1);
+ }
+ else
+ {
+ // Calculate write address
+ fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+ // Write to disk
+ return fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+ FAT_PRINTF(("FAT details:\r\n"));
+ FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+ FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+ FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+ FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+ FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+ // NOTE: On FAT16 this will be 0 which has a special meaning...
+ return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ struct fat_dir_entry *directoryEntry;
+
+ fatfs_lfn_cache_init(&lfn, 1);
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ long_filename = fatfs_lfn_cache_get(&lfn);
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(long_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ // Compare names to see if they match
+ if (fatfs_compare_names(short_filename, name_to_find))
+ {
+ memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+ return 1;
+ }
+
+ fatfs_lfn_cache_init(&lfn, 0);
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+ else
+#endif
+ // Normal Entry, only 8.3 Text
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ return 1;
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+ time_t time_now;
+ struct tm * time_info;
+ uint16 fat_time;
+ uint16 fat_date;
+
+ // Get system time
+ time(&time_now);
+
+ // Convert to local time
+ time_info = localtime(&time_now);
+
+ // Convert time to FAT format
+ fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+ // Convert date to FAT format
+ fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+ // Update requested fields
+ if (create)
+ {
+ directoryEntry->CrtTime[1] = fat_time >> 8;
+ directoryEntry->CrtTime[0] = fat_time >> 0;
+ directoryEntry->CrtDate[1] = fat_date >> 8;
+ directoryEntry->CrtDate[0] = fat_date >> 0;
+ }
+
+ if (modify)
+ {
+ directoryEntry->WrtTime[1] = fat_time >> 8;
+ directoryEntry->WrtTime[0] = fat_time >> 0;
+ directoryEntry->WrtDate[1] = fat_date >> 8;
+ directoryEntry->WrtDate[0] = fat_date >> 0;
+ }
+
+ if (access)
+ {
+ directoryEntry->LstAccDate[1] = fat_time >> 8;
+ directoryEntry->LstAccDate[0] = fat_time >> 0;
+ directoryEntry->LstAccDate[1] = fat_date >> 8;
+ directoryEntry->LstAccDate[0] = fat_date >> 0;
+ }
+
+ return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+ {
+ directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ int x=0;
+ struct fat_dir_entry *directoryEntry;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, Cluster, x++, 0)) // If sector read was successfull
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if (fatfs_entry_lfn_text(directoryEntry) )
+ ;
+
+ // If Invalid record found delete any long file name information collated
+ else if (fatfs_entry_lfn_invalid(directoryEntry) )
+ ;
+
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if (fatfs_entry_sfn_only(directoryEntry) )
+ {
+ if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+ {
+ // Mark as deleted
+ directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update access / modify time & date
+ fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+ // Update sfn entry
+ memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+ // Write sector back
+ return fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+ }
+ } // End of if
+ }
+ else
+ break;
+ } // End of while loop
+
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+ dirls->cluster = StartCluster;
+ dirls->sector = 0;
+ dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fat_ctx *ctx, struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+ uint8 i,item;
+ uint16 recordoffset;
+ struct fat_dir_entry *directoryEntry;
+ char *long_filename = NULL;
+ char short_filename[13];
+ struct lfn_cache lfn;
+ int dotRequired = 0;
+ int result = 0;
+
+ // Initialise LFN cache first
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ while (1)
+ {
+ // If data read OK
+ if (fatfs_sector_reader(ctx, fs, dirls->cluster, dirls->sector, 0))
+ {
+ // Maximum of 16 directory entries
+ for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Increase directory offset
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Text Found
+ if ( fatfs_entry_lfn_text(directoryEntry) )
+ fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+ // If Invalid record found delete any long file name information collated
+ else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ // Normal SFN Entry and Long text exists
+ else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+ {
+ // Get text
+ long_filename = fatfs_lfn_cache_get(&lfn);
+ strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME-1);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ // Normal Entry, only 8.3 Text
+ else
+#endif
+ if ( fatfs_entry_sfn_only(directoryEntry) )
+ {
+ fatfs_lfn_cache_init(&lfn, 0);
+
+ memset(short_filename, 0, sizeof(short_filename));
+
+ // Copy name to string
+ for (i=0; i<8; i++)
+ short_filename[i] = directoryEntry->Name[i];
+
+ // Extension
+ dotRequired = 0;
+ for (i=8; i<11; i++)
+ {
+ short_filename[i+1] = directoryEntry->Name[i];
+ if (directoryEntry->Name[i] != ' ')
+ dotRequired = 1;
+ }
+
+ // Dot only required if extension present
+ if (dotRequired)
+ {
+ // If not . or .. entry
+ if (short_filename[0]!='.')
+ short_filename[8] = '.';
+ else
+ short_filename[8] = ' ';
+ }
+ else
+ short_filename[8] = ' ';
+
+ fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+ if (fatfs_entry_is_dir(directoryEntry))
+ entry->is_dir = 1;
+ else
+ entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Get time / dates
+ entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+ entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+ entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+ entry->write_time = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+ entry->write_date = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+ entry->size = FAT_HTONL(directoryEntry->FileSize);
+ entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+ // Next starting position
+ dirls->offset = item + 1;
+ result = 1;
+ return 1;
+ }
+ }// end of for
+
+ // If reached end of the dir move onto next sector
+ dirls->sector++;
+ dirls->offset = 0;
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+#endif
diff --git a/kernel/fs/fatfs/fat_access.h b/kernel/fs/fatfs/fat_access.h
new file mode 100644
index 0000000..6ef0cbe
--- /dev/null
+++ b/kernel/fs/fatfs/fat_access.h
@@ -0,0 +1,135 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+struct fat_ctx;
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK 0
+#define FAT_INIT_MEDIA_ACCESS_ERROR (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE (-2)
+#define FAT_INIT_INVALID_SIGNATURE (-3)
+#define FAT_INIT_ENDIAN_ERROR (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE (-6)
+#define FAT_INIT_STRUCT_PACKING (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (struct fat_ctx *ctx, uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(struct fat_ctx *ctx, uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+ // User supplied function pointers for disk IO
+ fn_diskio_read read_media;
+ fn_diskio_write write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+ uint8 sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+ uint32 address;
+ int dirty;
+ uint8 * ptr;
+
+ // Next in chain of sector buffers
+ struct fat_buffer *next;
+};
+
+typedef enum eFatType
+{
+ FAT_TYPE_16,
+ FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+ // Filesystem globals
+ uint8 sectors_per_cluster;
+ uint32 cluster_begin_lba;
+ uint32 rootdir_first_cluster;
+ uint32 rootdir_first_sector;
+ uint32 rootdir_sectors;
+ uint32 fat_begin_lba;
+ uint16 fs_info_sector;
+ uint32 lba_begin;
+ uint32 fat_sectors;
+ uint32 next_free_cluster;
+ uint16 root_entry_count;
+ uint16 reserved_sectors;
+ uint8 num_of_fats;
+ tFatType fat_type;
+
+ // Disk/Media API
+ struct disk_if disk_io;
+
+ // [Optional] Thread Safety
+ void (*fl_lock)(void);
+ void (*fl_unlock)(void);
+
+ // Working buffer
+ struct fat_buffer currentsector;
+
+ // FAT Buffer
+ struct fat_buffer *fat_buffer_head;
+ struct fat_buffer fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+ uint32 sector;
+ uint32 cluster;
+ uint8 offset;
+};
+
+struct fs_dir_ent
+{
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 is_dir;
+ uint32 cluster;
+ uint32 size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ uint16 access_date;
+ uint16 write_time;
+ uint16 write_date;
+ uint16 create_date;
+ uint16 create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fat_ctx *ctx, struct fatfs *fs);
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int fatfs_sector_reader(struct fat_ctx *ctx, struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int fatfs_sector_read(struct fat_ctx *ctx, struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_sector_write(struct fat_ctx *ctx, struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int fatfs_read_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int fatfs_write_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void fatfs_show_details(struct fatfs *fs);
+uint32 fatfs_get_root_cluster(struct fatfs *fs);
+uint32 fatfs_get_file_entry(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int fatfs_sfn_exists(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname);
+int fatfs_update_file_length(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int fatfs_mark_file_deleted(struct fat_ctx *ctx, struct fatfs *fs, uint32 Cluster, char *shortname);
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int fatfs_list_directory_next(struct fat_ctx *ctx, struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/kernel/fs/fatfs/fat_cache.c b/kernel/fs/fatfs/fat_cache.c
new file mode 100644
index 0000000..de77e6a
--- /dev/null
+++ b/kernel/fs/fatfs/fat_cache.c
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ int i;
+
+ for (i=0;icluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+ file->cluster_cache_data[i] = 0;
+ }
+#endif
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ {
+ *pNextCluster = file->cluster_cache_data[slot];
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+ if (file->cluster_cache_idx[slot] == clusterIdx)
+ file->cluster_cache_data[slot] = nextCluster;
+ else
+ {
+ file->cluster_cache_idx[slot] = clusterIdx;
+ file->cluster_cache_data[slot] = nextCluster;
+ }
+#endif
+
+ return 1;
+}
diff --git a/kernel/fs/fatfs/fat_cache.h b/kernel/fs/fatfs/fat_cache.h
new file mode 100644
index 0000000..348d5d3
--- /dev/null
+++ b/kernel/fs/fatfs/fat_cache.h
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/kernel/fs/fatfs/fat_context.h b/kernel/fs/fatfs/fat_context.h
new file mode 100644
index 0000000..1b4447f
--- /dev/null
+++ b/kernel/fs/fatfs/fat_context.h
@@ -0,0 +1,17 @@
+#ifndef FAT_CONTEXT_H_
+#define FAT_CONTEXT_H_
+
+#include "fat_defs.h"
+#include "fat_filelib.h"
+
+struct fat_ctx {
+ FL_FILE _files[FATFS_MAX_OPEN_FILES];
+ int _filelib_init;
+ int _filelib_valid;
+ struct fatfs _fs;
+ struct fat_list _open_file_list;
+ struct fat_list _free_file_list;
+ void *extra;
+};
+
+#endif // FAT_CONTEXT_H_
diff --git a/kernel/fs/fatfs/fat_defs.h b/kernel/fs/fatfs/fat_defs.h
new file mode 100644
index 0000000..5fe8d6a
--- /dev/null
+++ b/kernel/fs/fatfs/fat_defs.h
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+// FAT32 Offsets
+// Name Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT 0 // Length = 3
+#define BS_OEMNAME 3 // Length = 8
+#define BPB_BYTSPERSEC 11 // Length = 2
+#define BPB_SECPERCLUS 13 // Length = 1
+#define BPB_RSVDSECCNT 14 // Length = 2
+#define BPB_NUMFATS 16 // Length = 1
+#define BPB_ROOTENTCNT 17 // Length = 2
+#define BPB_TOTSEC16 19 // Length = 2
+#define BPB_MEDIA 21 // Length = 1
+#define BPB_FATSZ16 22 // Length = 2
+#define BPB_SECPERTRK 24 // Length = 2
+#define BPB_NUMHEADS 26 // Length = 2
+#define BPB_HIDDSEC 28 // Length = 4
+#define BPB_TOTSEC32 32 // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM 36 // Length = 1
+#define BS_FAT_BOOTSIG 38 // Length = 1
+#define BS_FAT_VOLID 39 // Length = 4
+#define BS_FAT_VOLLAB 43 // Length = 11
+#define BS_FAT_FILSYSTYPE 54 // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32 36 // Length = 4
+#define BPB_FAT32_EXTFLAGS 40 // Length = 2
+#define BPB_FAT32_FSVER 42 // Length = 2
+#define BPB_FAT32_ROOTCLUS 44 // Length = 4
+#define BPB_FAT32_FSINFO 48 // Length = 2
+#define BPB_FAT32_BKBOOTSEC 50 // Length = 2
+#define BS_FAT32_DRVNUM 64 // Length = 1
+#define BS_FAT32_BOOTSIG 66 // Length = 1
+#define BS_FAT32_VOLID 67 // Length = 4
+#define BS_FAT32_VOLLAB 71 // Length = 11
+#define BS_FAT32_FILSYSTYPE 82 // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12 1
+#define FAT_TYPE_FAT16 2
+#define FAT_TYPE_FAT32 3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION 510
+#define SIGNATURE_VALUE 0xAA55
+#define PARTITION1_TYPECODE_LOCATION 450
+#define FAT32_TYPECODE1 0x0B
+#define FAT32_TYPECODE2 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION 454
+#define PARTITION1_SIZE_LOCATION 458
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define FAT_SFN_SIZE_FULL 11
+#define FAT_SFN_SIZE_PARTIAL 8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY 0x01
+#define FILE_ATTR_HIDDEN 0x02
+#define FILE_ATTR_SYSTEM 0x04
+#define FILE_ATTR_SYSHID 0x06
+#define FILE_ATTR_VOLUME_ID 0x08
+#define FILE_ATTR_DIRECTORY 0x10
+#define FILE_ATTR_ARCHIVE 0x20
+#define FILE_ATTR_LFN_TEXT 0x0F
+#define FILE_HEADER_BLANK 0x00
+#define FILE_HEADER_DELETED 0xE5
+#define FILE_TYPE_DIR 0x10
+#define FILE_TYPE_FILE 0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT 11
+#define FAT_TIME_HOURS_MASK 0x1F
+#define FAT_TIME_MINUTES_SHIFT 5
+#define FAT_TIME_MINUTES_MASK 0x3F
+#define FAT_TIME_SECONDS_SHIFT 0
+#define FAT_TIME_SECONDS_MASK 0x1F
+#define FAT_TIME_SECONDS_SCALE 2
+#define FAT_DATE_YEAR_SHIFT 9
+#define FAT_DATE_YEAR_MASK 0x7F
+#define FAT_DATE_MONTH_SHIFT 5
+#define FAT_DATE_MONTH_MASK 0xF
+#define FAT_DATE_DAY_SHIFT 0
+#define FAT_DATE_DAY_MASK 0x1F
+#define FAT_DATE_YEAR_OFFSET 1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER 0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER 0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+ uint8 Name[11];
+ uint8 Attr;
+ uint8 NTRes;
+ uint8 CrtTimeTenth;
+ uint8 CrtTime[2];
+ uint8 CrtDate[2];
+ uint8 LstAccDate[2];
+ uint16 FstClusHI;
+ uint8 WrtTime[2];
+ uint8 WrtDate[2];
+ uint16 FstClusLO;
+ uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/kernel/fs/fatfs/fat_filelib.c b/kernel/fs/fatfs/fat_filelib.c
new file mode 100644
index 0000000..df7b999
--- /dev/null
+++ b/kernel/fs/fatfs/fat_filelib.c
@@ -0,0 +1,1595 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+#include "fat_format.h"
+#include "fat_context.h"
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT(ctx) { if ((ctx)->_filelib_init==0) fl_init(ctx); }
+
+#define FL_LOCK(a) do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a) do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void _fl_init(struct fat_ctx *ctx);
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(struct fat_ctx *ctx)
+{
+ // Allocate free file
+ struct fat_node *node = fat_list_pop_head(&ctx->_free_file_list);
+
+ // Add to open list
+ if (node)
+ fat_list_insert_last(&ctx->_open_file_list, node);
+
+ return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(struct fat_ctx *ctx, FL_FILE* file)
+{
+ struct fat_node *node;
+
+ // Compare open files
+ fat_list_for_each(&ctx->_open_file_list, node)
+ {
+ FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+ // If not the current file
+ if (openFile != file)
+ {
+ // Compare path and name
+ if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+ return 1;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(struct fat_ctx *ctx, FL_FILE* file)
+{
+ // Remove from open list
+ fat_list_remove(&ctx->_open_file_list, &file->list_node);
+
+ // Add to free list
+ fat_list_insert_last(&ctx->_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+// Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(struct fat_ctx *ctx, char *path, uint32 *pathCluster)
+{
+ int levels;
+ int sublevel;
+ char currentfolder[FATFS_MAX_LONG_FILENAME];
+ struct fat_dir_entry sfEntry;
+ uint32 startcluster;
+
+ // Set starting cluster to root cluster
+ startcluster = fatfs_get_root_cluster(&ctx->_fs);
+
+ // Find number of levels
+ levels = fatfs_total_path_levels(path);
+
+ // Cycle through each level and get the start sector
+ for (sublevel=0;sublevel<(levels+1);sublevel++)
+ {
+ if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+ return 0;
+
+ // Find clusteraddress for folder (currentfolder)
+ if (fatfs_get_file_entry(ctx, &ctx->_fs, startcluster, currentfolder,&sfEntry))
+ {
+ // Check entry is folder
+ if (fatfs_entry_is_dir(&sfEntry))
+ startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ *pathCluster = startcluster;
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(struct fat_ctx *ctx, char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+ int i;
+
+ // Allocate a new file handle
+ file = _allocate_file(ctx);
+ if (!file)
+ return 0;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ // Check if file already open
+ if (_check_file_open(ctx, file))
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&ctx->_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(ctx, file->path, &file->parentcluster))
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(ctx, &ctx->_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the folder (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(ctx, &ctx->_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ // Erase new directory cluster
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;i_fs.sectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(ctx, &ctx->_fs, file->startcluster, i, file->file_data_sector))
+ {
+ _free_file(ctx, file);
+ return 0;
+ }
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(ctx, &ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return 0;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&ctx->_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return 0;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(ctx, &ctx->_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(ctx, &ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return 0;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_fat_purge(ctx, &ctx->_fs);
+
+ _free_file(ctx, file);
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(struct fat_ctx *ctx, const char *path)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+
+ // Allocate a new file handle
+ file = _allocate_file(ctx);
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(ctx, file))
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0]==0)
+ file->parentcluster = fatfs_get_root_cluster(&ctx->_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(ctx, file->path, &file->parentcluster))
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+ }
+
+ // Using dir cluster address search for filename
+ if (fatfs_get_file_entry(ctx, &ctx->_fs, file->parentcluster, file->filename,&sfEntry))
+ // Make sure entry is file not dir!
+ if (fatfs_entry_is_file(&sfEntry))
+ {
+ // Initialise file details
+ memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+ file->filelength = FAT_HTONL(sfEntry.FileSize);
+ file->bytenum = 0;
+ file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&ctx->_fs, file);
+
+ fatfs_fat_purge(ctx, &ctx->_fs);
+
+ return file;
+ }
+
+ _free_file(ctx, file);
+ return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(struct fat_ctx *ctx, const char *filename)
+{
+ FL_FILE* file;
+ struct fat_dir_entry sfEntry;
+ char shortFilename[FAT_SFN_SIZE_FULL];
+ int tailNum = 0;
+
+ // No write access?
+ if (!ctx->_fs.disk_io.write_media)
+ return NULL;
+
+ // Allocate a new file handle
+ file = _allocate_file(ctx);
+ if (!file)
+ return NULL;
+
+ // Clear filename
+ memset(file->path, '\0', sizeof(file->path));
+ memset(file->filename, '\0', sizeof(file->filename));
+
+ // Split full path into filename and directory path
+ if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // Check if file already open
+ if (_check_file_open(ctx, file))
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // If file is in the root dir
+ if (file->path[0] == 0)
+ file->parentcluster = fatfs_get_root_cluster(&ctx->_fs);
+ else
+ {
+ // Find parent directory start cluster
+ if (!_open_directory(ctx, file->path, &file->parentcluster))
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+ }
+
+ // Check if same filename exists in directory
+ if (fatfs_get_file_entry(ctx, &ctx->_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ file->startcluster = 0;
+
+ // Create the file space for the file (at least one clusters worth!)
+ if (!fatfs_allocate_free_space(ctx, &ctx->_fs, 1, &file->startcluster, 1))
+ {
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+#if FATFS_INC_LFN_SUPPORT
+ // Generate a short filename & tail
+ tailNum = 0;
+ do
+ {
+ // Create a standard short filename (without tail)
+ fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+ // If second hit or more, generate a ~n tail
+ if (tailNum != 0)
+ fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+ // Try with no tail if first entry
+ else
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already or not
+ if (fatfs_sfn_exists(ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+ break;
+
+ tailNum++;
+ }
+ while (tailNum < 9999);
+
+ // We reached the max number of duplicate short file names (unlikely!)
+ if (tailNum == 9999)
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(ctx, &ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return NULL;
+ }
+#else
+ // Create a standard short filename (without tail)
+ if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // Copy to SFN space
+ memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+ // Check if entry exists already
+ if (fatfs_sfn_exists(&ctx->_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(&ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return NULL;
+ }
+#endif
+
+ // Add file to disk
+ if (!fatfs_add_file_entry(ctx, &ctx->_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+ {
+ // Delete allocated space
+ fatfs_free_cluster_chain(ctx, &ctx->_fs, file->startcluster);
+
+ _free_file(ctx, file);
+ return NULL;
+ }
+
+ // General
+ file->filelength = 0;
+ file->bytenum = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Quick lookup for next link in the chain
+ file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+ file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+ fatfs_cache_init(&ctx->_fs, file);
+
+ fatfs_fat_purge(ctx, &ctx->_fs);
+
+ return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(struct fat_ctx *ctx, FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+ uint32 Sector = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 i;
+ uint32 lba;
+
+ // Find cluster index within file & sector with cluster
+ ClusterIdx = offset / ctx->_fs.sectors_per_cluster;
+ Sector = offset - (ClusterIdx * ctx->_fs.sectors_per_cluster);
+
+ // Limit number of sectors read to the number remaining in this cluster
+ if ((Sector + count) > ctx->_fs.sectors_per_cluster)
+ count = ctx->_fs.sectors_per_cluster - Sector;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(ctx, &ctx->_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&ctx->_fs, file, i, nextCluster);
+ }
+
+ Cluster = nextCluster;
+ }
+
+ // Record current cluster lookup details (if valid)
+ if (Cluster != FAT32_LAST_CLUSTER)
+ {
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+ }
+
+ // If end of cluster chain then return false
+ if (Cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Calculate sector address
+ lba = fatfs_lba_of_cluster(&ctx->_fs, Cluster) + Sector;
+
+ // Read sector of file
+ if (fatfs_sector_read(ctx, &ctx->_fs, lba, buffer, count))
+ return count;
+ else
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(struct fat_ctx *ctx)
+{
+ int i;
+
+ fat_list_init(&ctx->_free_file_list);
+ fat_list_init(&ctx->_open_file_list);
+
+ // Add all file objects to free list
+ for (i=0;i_free_file_list, &ctx->_files[i].list_node);
+
+ ctx->_filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(struct fat_ctx *ctx, void (*lock)(void), void (*unlock)(void))
+{
+ ctx->_fs.fl_lock = lock;
+ ctx->_fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(struct fat_ctx *ctx, fn_diskio_read rd, fn_diskio_write wr)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ ctx->_fs.disk_io.read_media = rd;
+ ctx->_fs.disk_io.write_media = wr;
+
+ // Initialise FAT parameters
+ if ((res = fatfs_init(ctx, &ctx->_fs)) != FAT_INIT_OK)
+ {
+ FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+ return res;
+ }
+
+ ctx->_filelib_valid = 1;
+ return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(struct fat_ctx *ctx)
+{
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ FL_LOCK(&ctx->_fs);
+ fatfs_fat_purge(ctx, &ctx->_fs);
+ FL_UNLOCK(&ctx->_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(struct fat_ctx *ctx, const char *path, const char *mode)
+{
+ int i;
+ FL_FILE* file;
+ uint8 flags = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (!ctx->_filelib_valid)
+ return NULL;
+
+ if (!path || !mode)
+ return NULL;
+
+ // Supported Modes:
+ // "r" Open a file for reading.
+ // The file must exist.
+ // "w" Create an empty file for writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a" Append to a file.
+ // Writing operations append data at the end of the file.
+ // The file is created if it does not exist.
+ // "r+" Open a file for update both reading and writing.
+ // The file must exist.
+ // "w+" Create an empty file for both reading and writing.
+ // If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+ // "a+" Open a file for reading and appending.
+ // All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+ // You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+ // will move it back to the end of file.
+ // The file is created if it does not exist.
+
+ for (i=0;i<(int)strlen(mode);i++)
+ {
+ switch (mode[i])
+ {
+ case 'r':
+ case 'R':
+ flags |= FILE_READ;
+ break;
+ case 'w':
+ case 'W':
+ flags |= FILE_WRITE;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ break;
+ case 'a':
+ case 'A':
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ break;
+ case '+':
+ if (flags & FILE_READ)
+ flags |= FILE_WRITE;
+ else if (flags & FILE_WRITE)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_ERASE;
+ flags |= FILE_CREATE;
+ }
+ else if (flags & FILE_APPEND)
+ {
+ flags |= FILE_READ;
+ flags |= FILE_WRITE;
+ flags |= FILE_APPEND;
+ flags |= FILE_CREATE;
+ }
+ break;
+ case 'b':
+ case 'B':
+ flags |= FILE_BINARY;
+ break;
+ }
+ }
+
+ file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+ // No write support!
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+ // No write access - remove write/modify flags
+ if (!ctx->_fs.disk_io.write_media)
+ flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+ FL_LOCK(&ctx->_fs);
+
+ // Read
+ if (flags & FILE_READ)
+ file = _open_file(ctx, path);
+
+ // Create New
+#if FATFS_INC_WRITE_SUPPORT
+ if (!file && (flags & FILE_CREATE))
+ file = _create_file(ctx, path);
+#endif
+
+ // Write Existing (and not open due to read or create)
+ if (!(flags & FILE_READ))
+ if ((flags & FILE_CREATE) && !file)
+ if (flags & (FILE_WRITE | FILE_APPEND))
+ file = _open_file(ctx, path);
+
+ if (file)
+ file->flags = flags;
+
+ FL_UNLOCK(&ctx->_fs);
+ return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(struct fat_ctx *ctx, FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+ uint32 SectorNumber = 0;
+ uint32 ClusterIdx = 0;
+ uint32 Cluster = 0;
+ uint32 LastCluster = FAT32_LAST_CLUSTER;
+ uint32 i;
+ uint32 lba;
+ uint32 TotalWriteCount = count;
+
+ // Find values for Cluster index & sector within cluster
+ ClusterIdx = offset / ctx->_fs.sectors_per_cluster;
+ SectorNumber = offset - (ClusterIdx * ctx->_fs.sectors_per_cluster);
+
+ // Limit number of sectors written to the number remaining in this cluster
+ if ((SectorNumber + count) > ctx->_fs.sectors_per_cluster)
+ count = ctx->_fs.sectors_per_cluster - SectorNumber;
+
+ // Quick lookup for next link in the chain
+ if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ // Else walk the chain
+ else
+ {
+ // Starting from last recorded cluster?
+ if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+ {
+ i = file->last_fat_lookup.ClusterIdx;
+ Cluster = file->last_fat_lookup.CurrentCluster;
+ }
+ // Start searching from the beginning..
+ else
+ {
+ // Set start of cluster chain to initial value
+ i = 0;
+ Cluster = file->startcluster;
+ }
+
+ // Follow chain to find cluster to read
+ for ( ;i_fs, file, i, &nextCluster))
+ {
+ // Scan file linked list to find next entry
+ nextCluster = fatfs_find_next_cluster(ctx, &ctx->_fs, Cluster);
+
+ // Push entry into cache
+ fatfs_cache_set_next_cluster(&ctx->_fs, file, i, nextCluster);
+ }
+
+ LastCluster = Cluster;
+ Cluster = nextCluster;
+
+ // Dont keep following a dead end
+ if (Cluster == FAT32_LAST_CLUSTER)
+ break;
+ }
+
+ // If we have reached the end of the chain, allocate more!
+ if (Cluster == FAT32_LAST_CLUSTER)
+ {
+ // Add some more cluster(s) to the last good cluster chain
+ if (!fatfs_add_free_space(ctx, &ctx->_fs, &LastCluster, (TotalWriteCount + ctx->_fs.sectors_per_cluster -1) / ctx->_fs.sectors_per_cluster))
+ return 0;
+
+ Cluster = LastCluster;
+ }
+
+ // Record current cluster lookup details
+ file->last_fat_lookup.CurrentCluster = Cluster;
+ file->last_fat_lookup.ClusterIdx = ClusterIdx;
+ }
+
+ // Calculate write address
+ lba = fatfs_lba_of_cluster(&ctx->_fs, Cluster) + SectorNumber;
+
+ if (fatfs_sector_write(ctx, &ctx->_fs, lba, buf, count))
+ return count;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(struct fat_ctx *ctx, void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (file)
+ {
+ FL_LOCK(&ctx->_fs);
+
+ // If some write data still in buffer
+ if (file->file_data_dirty)
+ {
+ // Write back current sector before loading next
+ if (_write_sectors(ctx, file, file->file_data_address, file->file_data_sector, 1))
+ file->file_data_dirty = 0;
+ }
+
+ FL_UNLOCK(&ctx->_fs);
+ }
+#endif
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(struct fat_ctx *ctx, void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (file)
+ {
+ FL_LOCK(&ctx->_fs);
+
+ // Flush un-written data to file
+ fl_fflush(ctx, f);
+
+ // File size changed?
+ if (file->filelength_changed)
+ {
+#if FATFS_INC_WRITE_SUPPORT
+ // Update filesize in directory
+ fatfs_update_file_length(ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+ file->filelength_changed = 0;
+ }
+
+ file->bytenum = 0;
+ file->filelength = 0;
+ file->startcluster = 0;
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ file->filelength_changed = 0;
+
+ // Free file handle
+ _free_file(ctx, file);
+
+ fatfs_fat_purge(ctx, &ctx->_fs);
+
+ FL_UNLOCK(&ctx->_fs);
+ }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(struct fat_ctx *ctx, void *f)
+{
+ int res;
+ uint8 data = 0;
+
+ res = fl_fread(ctx, &data, 1, 1, f);
+ if (res == 1)
+ return (int)data;
+ else
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(struct fat_ctx *ctx, char *s, int n, void *f)
+{
+ int idx = 0;
+
+ // Space for null terminator?
+ if (n > 0)
+ {
+ // While space (+space for null terminator)
+ while (idx < (n-1))
+ {
+ int ch = fl_fgetc(ctx, f);
+
+ // EOF / Error?
+ if (ch < 0)
+ break;
+
+ // Store character read from stream
+ s[idx++] = (char)ch;
+
+ // End of line?
+ if (ch == '\n')
+ break;
+ }
+
+ if (idx > 0)
+ s[idx] = '\0';
+ }
+
+ return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(struct fat_ctx *ctx, void * buffer, int size, int length, void *f )
+{
+ uint32 sector;
+ uint32 offset;
+ int copyCount;
+ int count = size * length;
+ int bytesRead = 0;
+
+ FL_FILE *file = (FL_FILE *)f;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (buffer==NULL || file==NULL)
+ return -1;
+
+ // No read permissions
+ if (!(file->flags & FILE_READ))
+ return -1;
+
+ // Nothing to be done
+ if (!count)
+ return 0;
+
+ // Check if read starts past end of file
+ if (file->bytenum >= file->filelength)
+ return -1;
+
+ // Limit to file size
+ if ( (file->bytenum + count) > file->filelength )
+ count = file->filelength - file->bytenum;
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesRead < count)
+ {
+ // Read whole sector, read from media directly into target buffer
+ if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+ {
+ // Read as many sectors as possible into target buffer
+ uint32 sectorsRead = _read_sectors(ctx, file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+ if (sectorsRead)
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsRead;
+ offset = 0;
+ }
+ else
+ break;
+ }
+ else
+ {
+ // Do we need to re-read the sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(ctx, file);
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(ctx, file, sector, file->file_data_sector, 1))
+ // Read failed - out of range (probably)
+ break;
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (count - bytesRead))
+ copyCount = (count - bytesRead);
+
+ // Copy to application buffer
+ memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+
+ // Increase total read count
+ bytesRead += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+ }
+
+ return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek(struct fat_ctx *ctx, void *f, long offset, int origin )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res = -1;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (!file)
+ return -1;
+
+ if (origin == SEEK_END && offset != 0)
+ return -1;
+
+ FL_LOCK(&ctx->_fs);
+
+ // Invalidate file buffer
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+
+ if (origin == SEEK_SET)
+ {
+ file->bytenum = (uint32)offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+
+ res = 0;
+ }
+ else if (origin == SEEK_CUR)
+ {
+ // Positive shift
+ if (offset >= 0)
+ {
+ file->bytenum += offset;
+
+ if (file->bytenum > file->filelength)
+ file->bytenum = file->filelength;
+ }
+ // Negative shift
+ else
+ {
+ // Make shift positive
+ offset = -offset;
+
+ // Limit to negative shift to start of file
+ if ((uint32)offset > file->bytenum)
+ file->bytenum = 0;
+ else
+ file->bytenum-= offset;
+ }
+
+ res = 0;
+ }
+ else if (origin == SEEK_END)
+ {
+ file->bytenum = file->filelength;
+ res = 0;
+ }
+ else
+ res = -1;
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(struct fat_ctx *ctx, void *f , uint32 * position)
+{
+ FL_FILE *file = (FL_FILE *)f;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&ctx->_fs);
+
+ // Get position
+ *position = file->bytenum;
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(struct fat_ctx *ctx, void *f)
+{
+ uint32 pos = 0;
+
+ fl_fgetpos(ctx, f, &pos);
+
+ return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(struct fat_ctx *ctx, void *f)
+{
+ FL_FILE *file = (FL_FILE *)f;
+ int res;
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&ctx->_fs);
+
+ if (file->bytenum == file->filelength)
+ res = EOF;
+ else
+ res = 0;
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(struct fat_ctx *ctx, int c, void *f)
+{
+ uint8 data = (uint8)c;
+ int res;
+
+ res = fl_fwrite(ctx, &data, 1, 1, f);
+ if (res == 1)
+ return c;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(struct fat_ctx *ctx, const void * data, int size, int count, void *f )
+{
+ FL_FILE *file = (FL_FILE *)f;
+ uint32 sector;
+ uint32 offset;
+ uint32 length = (size*count);
+ uint8 *buffer = (uint8 *)data;
+ uint32 bytesWritten = 0;
+ uint32 copyCount;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ if (!file)
+ return -1;
+
+ FL_LOCK(&ctx->_fs);
+
+ // No write permissions
+ if (!(file->flags & FILE_WRITE))
+ {
+ FL_UNLOCK(&ctx->_fs);
+ return -1;
+ }
+
+ // Append writes to end of file
+ if (file->flags & FILE_APPEND)
+ file->bytenum = file->filelength;
+ // Else write to current position
+
+ // Calculate start sector
+ sector = file->bytenum / FAT_SECTOR_SIZE;
+
+ // Offset to start copying data from first sector
+ offset = file->bytenum % FAT_SECTOR_SIZE;
+
+ while (bytesWritten < length)
+ {
+ // Whole sector or more to be written?
+ if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+ {
+ uint32 sectorsWrote;
+
+ // Buffered sector, flush back to disk
+ if (file->file_data_address != 0xFFFFFFFF)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(ctx, file);
+
+ file->file_data_address = 0xFFFFFFFF;
+ file->file_data_dirty = 0;
+ }
+
+ // Write as many sectors as possible
+ sectorsWrote = _write_sectors(ctx, file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+ copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector+= sectorsWrote;
+ offset = 0;
+
+ if (!sectorsWrote)
+ break;
+ }
+ else
+ {
+ // We have upto one sector to copy
+ copyCount = FAT_SECTOR_SIZE - offset;
+
+ // Only require some of this sector?
+ if (copyCount > (length - bytesWritten))
+ copyCount = (length - bytesWritten);
+
+ // Do we need to read a new sector?
+ if (file->file_data_address != sector)
+ {
+ // Flush un-written data to file
+ if (file->file_data_dirty)
+ fl_fflush(ctx, file);
+
+ // If we plan to overwrite the whole sector, we don't need to read it first!
+ if (copyCount != FAT_SECTOR_SIZE)
+ {
+ // NOTE: This does not have succeed; if last sector of file
+ // reached, no valid data will be read in, but write will
+ // allocate some more space for new data.
+
+ // Get LBA of sector offset within file
+ if (!_read_sectors(ctx, file, sector, file->file_data_sector, 1))
+ memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+ }
+
+ file->file_data_address = sector;
+ file->file_data_dirty = 0;
+ }
+
+ // Copy from application buffer into sector buffer
+ memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+ // Mark buffer as dirty
+ file->file_data_dirty = 1;
+
+ // Increase total read count
+ bytesWritten += copyCount;
+
+ // Increment file pointer
+ file->bytenum += copyCount;
+
+ // Move onto next sector and reset copy offset
+ sector++;
+ offset = 0;
+ }
+ }
+
+ // Write increased extent of the file?
+ if (file->bytenum > file->filelength)
+ {
+ // Increase file size to new point
+ file->filelength = file->bytenum;
+
+ // We are changing the file length and this
+ // will need to be writen back at some point
+ file->filelength_changed = 1;
+ }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // If time & date support is enabled, always force directory entry to be
+ // written in-order to update file modify / access time & date.
+ file->filelength_changed = 1;
+#endif
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(struct fat_ctx *ctx, const char * str, void *f)
+{
+ int len = (int)strlen(str);
+ int res = fl_fwrite(ctx, str, 1, len, f);
+
+ if (res == len)
+ return len;
+ else
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove(struct fat_ctx *ctx, const char * filename )
+{
+ FL_FILE* file;
+ int res = -1;
+
+ FL_LOCK(&ctx->_fs);
+
+ // Use read_file as this will check if the file is already open!
+ file = (FL_FILE*)fl_fopen(ctx, (char*)filename, "r");
+ if (file)
+ {
+ // Delete allocated space
+ if (fatfs_free_cluster_chain(ctx, &ctx->_fs, file->startcluster))
+ {
+ // Remove directory entries
+ if (fatfs_mark_file_deleted(ctx, &ctx->_fs, file->parentcluster, (char*)file->shortfilename))
+ {
+ // Close the file handle (this should not write anything to the file
+ // as we have not changed the file since opening it!)
+ fl_fclose(ctx, file);
+
+ res = 0;
+ }
+ }
+ }
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(struct fat_ctx *ctx, const char *path)
+{
+ int res;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ FL_LOCK(&ctx->_fs);
+ res =_create_directory(ctx, (char*)path);
+ FL_UNLOCK(&ctx->_fs);
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(struct fat_ctx *ctx, const char *path)
+{
+ FL_DIR dirstat;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ FL_LOCK(&ctx->_fs);
+
+ FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+ if (fl_opendir(ctx, path, &dirstat))
+ {
+ struct fs_dir_ent dirent;
+
+ while (fl_readdir(ctx, &dirstat, &dirent) == 0)
+ {
+#if FATFS_INC_TIME_DATE_SUPPORT
+ int d,m,y,h,mn,s;
+ fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+ fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+ FAT_PRINTF(("%02d/%02d/%04d %02d:%02d ", d,mn,y,h,m));
+#endif
+
+ if (dirent.is_dir)
+ {
+ FAT_PRINTF(("%s \r\n", dirent.filename));
+ }
+ else
+ {
+ FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+ }
+ }
+
+ fl_closedir(ctx, &dirstat);
+ }
+
+ FL_UNLOCK(&ctx->_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(struct fat_ctx *ctx, const char* path, FL_DIR *dir)
+{
+ int levels;
+ int res = 1;
+ uint32 cluster = FAT32_INVALID_CLUSTER;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ FL_LOCK(&ctx->_fs);
+
+ levels = fatfs_total_path_levels((char*)path) + 1;
+
+ // If path is in the root dir
+ if (levels == 0)
+ cluster = fatfs_get_root_cluster(&ctx->_fs);
+ // Find parent directory start cluster
+ else
+ res = _open_directory(ctx, (char*)path, &cluster);
+
+ if (res)
+ fatfs_list_directory_start(&ctx->_fs, dir, cluster);
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(struct fat_ctx *ctx, FL_DIR *dirls, fl_dirent *entry)
+{
+ int res = 0;
+
+ // If first call to library, initialise
+ CHECK_FL_INIT(ctx);
+
+ FL_LOCK(&ctx->_fs);
+
+ res = fatfs_list_directory_next(ctx, &ctx->_fs, dirls, entry);
+
+ FL_UNLOCK(&ctx->_fs);
+
+ return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(struct fat_ctx *ctx, FL_DIR* dir)
+{
+ // Not used
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(struct fat_ctx *ctx, const char *path)
+{
+ int res = 0;
+ FL_DIR dir;
+
+ if (fl_opendir(ctx, path, &dir))
+ {
+ res = 1;
+ fl_closedir(ctx, &dir);
+ }
+
+ return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(struct fat_ctx *ctx, uint32 volume_sectors, const char *name)
+{
+ return fatfs_format(ctx, &ctx->_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(struct fat_ctx *ctx)
+{
+ return &ctx->_fs;
+}
+#endif
diff --git a/kernel/fs/fatfs/fat_filelib.h b/kernel/fs/fatfs/fat_filelib.h
new file mode 100644
index 0000000..2d85138
--- /dev/null
+++ b/kernel/fs/fatfs/fat_filelib.h
@@ -0,0 +1,148 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+ #define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+ #define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+ #define SEEK_SET 0
+#endif
+
+#ifndef EOF
+ #define EOF (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct fat_ctx;
+
+struct cluster_lookup
+{
+ uint32 ClusterIdx;
+ uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+ uint32 parentcluster;
+ uint32 startcluster;
+ uint32 bytenum;
+ uint32 filelength;
+ int filelength_changed;
+ char path[FATFS_MAX_LONG_FILENAME];
+ char filename[FATFS_MAX_LONG_FILENAME];
+ uint8 shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+ uint32 cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+ uint32 cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+ // Cluster Lookup
+ struct cluster_lookup last_fat_lookup;
+
+ // Read/Write sector buffer
+ uint8 file_data_sector[FAT_SECTOR_SIZE];
+ uint32 file_data_address;
+ int file_data_dirty;
+
+ // File fopen flags
+ uint8 flags;
+#define FILE_READ (1 << 0)
+#define FILE_WRITE (1 << 1)
+#define FILE_APPEND (1 << 2)
+#define FILE_BINARY (1 << 3)
+#define FILE_ERASE (1 << 4)
+#define FILE_CREATE (1 << 5)
+
+ struct fat_node list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void fl_init(struct fat_ctx *ctx);
+void fl_attach_locks(struct fat_ctx *ctx, void (*lock)(void), void (*unlock)(void));
+int fl_attach_media(struct fat_ctx *ctx, fn_diskio_read rd, fn_diskio_write wr);
+void fl_shutdown(struct fat_ctx *ctx);
+
+// Standard API
+void* fl_fopen(struct fat_ctx *ctx, const char *path, const char *modifiers);
+void fl_fclose(struct fat_ctx *ctx, void *file);
+int fl_fflush(struct fat_ctx *ctx, void *file);
+int fl_fgetc(struct fat_ctx *ctx, void *file);
+char * fl_fgets(struct fat_ctx *ctx, char *s, int n, void *f);
+int fl_fputc(struct fat_ctx *ctx, int c, void *file);
+int fl_fputs(struct fat_ctx *ctx, const char * str, void *file);
+int fl_fwrite(struct fat_ctx *ctx, const void * data, int size, int count, void *file );
+int fl_fread(struct fat_ctx *ctx, void * data, int size, int count, void *file );
+int fl_fseek(struct fat_ctx *ctx, void *file , long offset , int origin );
+int fl_fgetpos(struct fat_ctx *ctx, void *file , uint32 * position);
+long fl_ftell(struct fat_ctx *ctx, void *f);
+int fl_feof(struct fat_ctx *ctx, void *f);
+int fl_remove(struct fat_ctx *ctx, const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status FL_DIR;
+typedef struct fs_dir_ent fl_dirent;
+
+FL_DIR* fl_opendir(struct fat_ctx *ctx, const char* path, FL_DIR *dir);
+int fl_readdir(struct fat_ctx *ctx, FL_DIR *dirls, fl_dirent *entry);
+int fl_closedir(struct fat_ctx *ctx, FL_DIR* dir);
+
+// Extensions
+void fl_listdirectory(struct fat_ctx *ctx, const char *path);
+int fl_createdirectory(struct fat_ctx *ctx, const char *path);
+int fl_is_dir(struct fat_ctx *ctx, const char *path);
+
+int fl_format(struct fat_ctx *ctx, uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(struct fat_ctx *ctx);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE FL_FILE
+
+#define fopen(a,b) fl_fopen(a, b)
+#define fclose(a) fl_fclose(a)
+#define fflush(a) fl_fflush(a)
+#define fgetc(a) fl_fgetc(a)
+#define fgets(a,b,c) fl_fgets(a, b, c)
+#define fputc(a,b) fl_fputc(a, b)
+#define fputs(a,b) fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d) fl_fread(a, b, c, d)
+#define fseek(a,b,c) fl_fseek(a, b, c)
+#define fgetpos(a,b) fl_fgetpos(a, b)
+#define ftell(a) fl_ftell(a)
+#define feof(a) fl_feof(a)
+#define remove(a) fl_remove(a)
+#define mkdir(a) fl_createdirectory(a)
+#define rmdir(a) 0
+
+#endif
+
+#endif
diff --git a/kernel/fs/fatfs/fat_format.c b/kernel/fs/fatfs/fat_format.c
new file mode 100644
index 0000000..96bc93f
--- /dev/null
+++ b/kernel/fs/fatfs/fat_format.c
@@ -0,0 +1,532 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+ uint32 sectors;
+ uint8 sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+ { 32680, 2}, // 16MB - 1K
+ { 262144, 4}, // 128MB - 2K
+ { 524288, 8}, // 256MB - 4K
+ { 1048576, 16}, // 512MB - 8K
+ { 2097152, 32}, // 1GB - 16K
+ { 4194304, 64}, // 2GB - 32K
+ { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+ { 0 , 0 } // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+ { 532480, 1}, // 260MB - 512b
+ { 16777216, 8}, // 8GB - 4K
+ { 33554432, 16}, // 16GB - 8K
+ { 67108864, 32}, // 32GB - 16K
+ { 0xFFFFFFFF, 64},// >32GB - 32K
+ { 0 , 0 } // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+ int i;
+
+ if (!is_fat32)
+ {
+ for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table16[i].sectors)
+ return _cluster_size_table16[i].sectors_per_cluster;
+ }
+ else
+ {
+ for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+ if (sectors <= _cluster_size_table32[i].sectors)
+ return _cluster_size_table32[i].sectors_per_cluster;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fat_ctx *ctx, struct fatfs *fs, uint32 lba, int count)
+{
+ int i;
+
+ // Zero sector first
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ for (i=0;idisk_io.write_media(ctx, lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+ uint32 total_clusters;
+ int i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // OEM Name & Jump Code
+ fs->currentsector.sector[0] = 0xEB;
+ fs->currentsector.sector[1] = 0x3C;
+ fs->currentsector.sector[2] = 0x90;
+ fs->currentsector.sector[3] = 0x4D;
+ fs->currentsector.sector[4] = 0x53;
+ fs->currentsector.sector[5] = 0x44;
+ fs->currentsector.sector[6] = 0x4F;
+ fs->currentsector.sector[7] = 0x53;
+ fs->currentsector.sector[8] = 0x35;
+ fs->currentsector.sector[9] = 0x2E;
+ fs->currentsector.sector[10] = 0x30;
+
+ // Bytes per sector
+ fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+ fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+ // Get sectors per cluster size for the disk
+ fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+ if (!fs->sectors_per_cluster)
+ return 0; // Invalid disk size
+
+ // Sectors per cluster
+ fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+ // Reserved Sectors
+ if (!is_fat32)
+ fs->reserved_sectors = 8;
+ else
+ fs->reserved_sectors = 32;
+ fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+ fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+ // Number of FATS
+ fs->num_of_fats = 2;
+ fs->currentsector.sector[16] = fs->num_of_fats;
+
+ // Max entries in root dir (FAT16 only)
+ if (!is_fat32)
+ {
+ fs->root_entry_count = 512;
+ fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+ fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+ }
+ else
+ {
+ fs->root_entry_count = 0;
+ fs->currentsector.sector[17] = 0;
+ fs->currentsector.sector[18] = 0;
+ }
+
+ // [FAT16] Total sectors (use FAT32 count instead)
+ fs->currentsector.sector[19] = 0x00;
+ fs->currentsector.sector[20] = 0x00;
+
+ // Media type
+ fs->currentsector.sector[21] = 0xF8;
+
+
+ // FAT16 BS Details
+ if (!is_fat32)
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+ fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+ fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+ // Sectors per track
+ fs->currentsector.sector[24] = 0x00;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads
+ fs->currentsector.sector[26] = 0x00;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x20;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ // Drive number
+ fs->currentsector.sector[36] = 0x00;
+
+ // Reserved
+ fs->currentsector.sector[37] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[38] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[39] = 0x12;
+ fs->currentsector.sector[40] = 0x34;
+ fs->currentsector.sector[41] = 0x56;
+ fs->currentsector.sector[42] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+43] = name[i];
+ else
+ fs->currentsector.sector[i+43] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[54] = 'F';
+ fs->currentsector.sector[55] = 'A';
+ fs->currentsector.sector[56] = 'T';
+ fs->currentsector.sector[57] = '1';
+ fs->currentsector.sector[58] = '6';
+ fs->currentsector.sector[59] = ' ';
+ fs->currentsector.sector[60] = ' ';
+ fs->currentsector.sector[61] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+ // FAT32 BS Details
+ else
+ {
+ // Count of sectors used by the FAT table (FAT16 only)
+ fs->currentsector.sector[22] = 0;
+ fs->currentsector.sector[23] = 0;
+
+ // Sectors per track (default)
+ fs->currentsector.sector[24] = 0x3F;
+ fs->currentsector.sector[25] = 0x00;
+
+ // Heads (default)
+ fs->currentsector.sector[26] = 0xFF;
+ fs->currentsector.sector[27] = 0x00;
+
+ // Hidden sectors
+ fs->currentsector.sector[28] = 0x00;
+ fs->currentsector.sector[29] = 0x00;
+ fs->currentsector.sector[30] = 0x00;
+ fs->currentsector.sector[31] = 0x00;
+
+ // Total sectors for this volume
+ fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+ fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+ fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+ fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+ total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+ fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+ // BPB_FATSz32
+ fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+ fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+ fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+ fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+ // BPB_ExtFlags
+ fs->currentsector.sector[40] = 0;
+ fs->currentsector.sector[41] = 0;
+
+ // BPB_FSVer
+ fs->currentsector.sector[42] = 0;
+ fs->currentsector.sector[43] = 0;
+
+ // BPB_RootClus
+ fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+ fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+ fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+ fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+ // BPB_FSInfo
+ fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+ fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+ // BPB_BkBootSec
+ fs->currentsector.sector[50] = 6;
+ fs->currentsector.sector[51] = 0;
+
+ // Drive number
+ fs->currentsector.sector[64] = 0x00;
+
+ // Boot signature
+ fs->currentsector.sector[66] = 0x29;
+
+ // Volume ID
+ fs->currentsector.sector[67] = 0x12;
+ fs->currentsector.sector[68] = 0x34;
+ fs->currentsector.sector[69] = 0x56;
+ fs->currentsector.sector[70] = 0x78;
+
+ // Volume name
+ for (i=0;i<11;i++)
+ {
+ if (i < (int)strlen(name))
+ fs->currentsector.sector[i+71] = name[i];
+ else
+ fs->currentsector.sector[i+71] = ' ';
+ }
+
+ // File sys type
+ fs->currentsector.sector[82] = 'F';
+ fs->currentsector.sector[83] = 'A';
+ fs->currentsector.sector[84] = 'T';
+ fs->currentsector.sector[85] = '3';
+ fs->currentsector.sector[86] = '2';
+ fs->currentsector.sector[87] = ' ';
+ fs->currentsector.sector[88] = ' ';
+ fs->currentsector.sector[89] = ' ';
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+ }
+
+ if (fs->disk_io.write_media(ctx, boot_sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 sector_lba)
+{
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // FSI_LeadSig
+ fs->currentsector.sector[0] = 0x52;
+ fs->currentsector.sector[1] = 0x52;
+ fs->currentsector.sector[2] = 0x61;
+ fs->currentsector.sector[3] = 0x41;
+
+ // FSI_StrucSig
+ fs->currentsector.sector[484] = 0x72;
+ fs->currentsector.sector[485] = 0x72;
+ fs->currentsector.sector[486] = 0x41;
+ fs->currentsector.sector[487] = 0x61;
+
+ // FSI_Free_Count
+ fs->currentsector.sector[488] = 0xFF;
+ fs->currentsector.sector[489] = 0xFF;
+ fs->currentsector.sector[490] = 0xFF;
+ fs->currentsector.sector[491] = 0xFF;
+
+ // FSI_Nxt_Free
+ fs->currentsector.sector[492] = 0xFF;
+ fs->currentsector.sector[493] = 0xFF;
+ fs->currentsector.sector[494] = 0xFF;
+ fs->currentsector.sector[495] = 0xFF;
+
+ // Signature
+ fs->currentsector.sector[510] = 0x55;
+ fs->currentsector.sector[511] = 0xAA;
+
+ if (fs->disk_io.write_media(ctx, sector_lba, fs->currentsector.sector, 1))
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fat_ctx *ctx, struct fatfs *fs, int is_fat32)
+{
+ uint32 i;
+
+ // Zero sector initially
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+ // Initialise default allocate / reserved clusters
+ if (!is_fat32)
+ {
+ SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+ SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+ }
+ else
+ {
+ SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+ SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+ SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+ }
+
+ if (!fs->disk_io.write_media(ctx, fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+ return 0;
+
+ // Zero remaining FAT sectors
+ memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+ for (i=1;ifat_sectors*fs->num_of_fats;i++)
+ if (!fs->disk_io.write_media(ctx, fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT16
+ fs->fat_type = FAT_TYPE_16;
+
+ // Not valid for FAT16
+ fs->fs_info_sector = 0;
+ fs->rootdir_first_cluster = 0;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(ctx, fs, fs->lba_begin, volume_sectors, name, 0))
+ return 0;
+
+ // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+ fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+ fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(ctx, fs, 0))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(ctx, fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ fs->currentsector.address = FAT32_INVALID_CLUSTER;
+ fs->currentsector.dirty = 0;
+
+ fs->next_free_cluster = 0; // Invalid
+
+ fatfs_fat_init(fs);
+
+ // Make sure we have read + write functions
+ if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+ return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+ // Volume is FAT32
+ fs->fat_type = FAT_TYPE_32;
+
+ // Basic defaults for normal FAT32 partitions
+ fs->fs_info_sector = 1;
+ fs->rootdir_first_cluster = 2;
+
+ // Sector 0: Boot sector
+ // NOTE: We don't need an MBR, it is a waste of a good sector!
+ fs->lba_begin = 0;
+ if (!fatfs_create_boot_sector(ctx, fs, fs->lba_begin, volume_sectors, name, 1))
+ return 0;
+
+ // First FAT LBA address
+ fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+ // The address of the first data cluster on this volume
+ fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+ // Initialise FSInfo sector
+ if (!fatfs_create_fsinfo_sector(ctx, fs, fs->fs_info_sector))
+ return 0;
+
+ // Initialise FAT sectors
+ if (!fatfs_erase_fat(ctx, fs, 1))
+ return 0;
+
+ // Erase Root directory
+ if (!fatfs_erase_sectors(ctx, fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+ // 2GB - 32K limit for safe behaviour for FAT16
+ if (volume_sectors <= 4194304)
+ return fatfs_format_fat16(ctx, fs, volume_sectors, name);
+ else
+ return fatfs_format_fat32(ctx, fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
diff --git a/kernel/fs/fatfs/fat_format.h b/kernel/fs/fatfs/fat_format.h
new file mode 100644
index 0000000..a0192cc
--- /dev/null
+++ b/kernel/fs/fatfs/fat_format.h
@@ -0,0 +1,17 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+struct fat_ctx;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fat_ctx *ctx, struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/kernel/fs/fatfs/fat_list.h b/kernel/fs/fatfs/fat_list.h
new file mode 100644
index 0000000..bd386ef
--- /dev/null
+++ b/kernel/fs/fatfs/fat_list.h
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+ #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+ #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+ struct fat_node *previous;
+ struct fat_node *next;
+};
+
+struct fat_list
+{
+ struct fat_node *head;
+ struct fat_node *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m) p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p) (p)->next
+#define fat_list_prev(l, p) (p)->previous
+#define fat_list_first(l) (l)->head
+#define fat_list_last(l) (l)->tail
+#define fat_list_for_each(l, p) for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if(!node->previous)
+ list->head = node->next;
+ else
+ node->previous->next = node->next;
+
+ if(!node->next)
+ list->tail = node->previous;
+ else
+ node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node;
+ new_node->next = node->next;
+ if (!node->next)
+ list->tail = new_node;
+ else
+ node->next->previous = new_node;
+ node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+ FAT_ASSERT(new_node);
+
+ new_node->previous = node->previous;
+ new_node->next = node;
+ if (!node->previous)
+ list->head = new_node;
+ else
+ node->previous->next = new_node;
+ node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->head)
+ {
+ list->head = node;
+ list->tail = node;
+ node->previous = 0;
+ node->next = 0;
+ }
+ else
+ fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+ FAT_ASSERT(list);
+ FAT_ASSERT(node);
+
+ if (!list->tail)
+ fat_list_insert_first(list, node);
+ else
+ fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+ FAT_ASSERT(list);
+
+ return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+ struct fat_node * node;
+
+ FAT_ASSERT(list);
+
+ node = fat_list_first(list);
+ if (node)
+ fat_list_remove(list, node);
+
+ return node;
+}
+
+#endif
+
diff --git a/kernel/fs/fatfs/fat_misc.c b/kernel/fs/fatfs/fat_misc.c
new file mode 100644
index 0000000..cbf6f08
--- /dev/null
+++ b/kernel/fs/fatfs/fat_misc.c
@@ -0,0 +1,505 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+ int i = 0;
+
+ lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+ // Zero out buffer also
+ if (wipeTable)
+ for (i=0;iString[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+ uint8 LFNIndex, i;
+ LFNIndex = entryBuffer[0] & 0x1F;
+
+ // Limit file name to cache size!
+ if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+ return ;
+
+ // This is an error condition
+ if (LFNIndex == 0)
+ return ;
+
+ if (lfn->no_of_strings == 0)
+ lfn->no_of_strings = LFNIndex;
+
+ lfn->String[LFNIndex-1][0] = entryBuffer[1];
+ lfn->String[LFNIndex-1][1] = entryBuffer[3];
+ lfn->String[LFNIndex-1][2] = entryBuffer[5];
+ lfn->String[LFNIndex-1][3] = entryBuffer[7];
+ lfn->String[LFNIndex-1][4] = entryBuffer[9];
+ lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+ lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+ lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+ lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+ lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+ lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+ lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+ lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+ for (i=0; iString[LFNIndex-1][i]==0xFF)
+ lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+ // Null terminate long filename
+ if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+ lfn->Null = '\0';
+ else if (lfn->no_of_strings)
+ lfn->String[lfn->no_of_strings][0] = '\0';
+ else
+ lfn->String[0][0] = '\0';
+
+ return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+ if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+ if ( (entry->Name[0]==FILE_HEADER_BLANK) ||
+ (entry->Name[0]==FILE_HEADER_DELETED)||
+ (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+ (entry->Attr & FILE_ATTR_SYSHID) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+ (lfn->no_of_strings) )
+ return 1;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+ if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+ (entry->Name[0]!=FILE_HEADER_BLANK) &&
+ (entry->Name[0]!=FILE_HEADER_DELETED) &&
+ (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+ (!(entry->Attr&FILE_ATTR_SYSHID)) )
+ return 1;
+ else
+ return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_DIR)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+ if (entry->Attr & FILE_TYPE_FILE)
+ return 1;
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+ int length = (int)strlen(filename);
+
+ if (length)
+ return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+ else
+ return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+ int i;
+ int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+ // 13 characters entries
+ int length = (int)strlen(filename);
+ int entriesRequired = fatfs_lfn_entries_required(filename);
+
+ // Filename offset
+ int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+ // Initialise to zeros
+ memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+ // LFN entry number
+ buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+ // LFN flag
+ buffer[11] = 0x0F;
+
+ // Checksum of short filename
+ buffer[13] = sfnChk;
+
+ // Copy to buffer
+ for (i=0;iName[i] = shortfilename[i];
+
+ // Unless we have a RTC we might as well set these to 1980
+ entry->CrtTimeTenth = 0x00;
+ entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+ entry->CrtDate[1] = 0x00;
+ entry->CrtDate[0] = 0x20;
+ entry->LstAccDate[1] = 0x00;
+ entry->LstAccDate[0] = 0x20;
+ entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+ entry->WrtDate[1] = 0x00;
+ entry->WrtDate[0] = 0x20;
+
+ if (!dir)
+ entry->Attr = FILE_TYPE_FILE;
+ else
+ entry->Attr = FILE_TYPE_DIR;
+
+ entry->NTRes = 0x00;
+
+ entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+ entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+ entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+ int i;
+ int dotPos = -1;
+ char ext[3];
+ int pos;
+ int len = (int)strlen(filename);
+
+ // Invalid to start with .
+ if (filename[0]=='.')
+ return 0;
+
+ memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+ memset(ext, ' ', 3);
+
+ // Find dot seperator
+ for (i = 0; i< len; i++)
+ {
+ if (filename[i]=='.')
+ dotPos = i;
+ }
+
+ // Extract extensions
+ if (dotPos!=-1)
+ {
+ // Copy first three chars of extension
+ for (i = (dotPos+1); i < (dotPos+1+3); i++)
+ if (i= 'a' && filename[i] <= 'z')
+ sfn_output[pos++] = filename[i] - 'a' + 'A';
+ else
+ sfn_output[pos++] = filename[i];
+ }
+
+ // Fill upto 8 characters
+ if (pos==FAT_SFN_SIZE_PARTIAL)
+ break;
+ }
+
+ // Add extension part
+ for (i=FAT_SFN_SIZE_PARTIAL;i= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+ else
+ sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+ char* cp;
+ char outbuf[12];
+ const char digits[] = "0123456789ABCDEF";
+
+ // Build string backwards
+ cp = outbuf;
+ do
+ {
+ *cp++ = digits[(int)(num % 10)];
+ }
+ while ((num /= 10) > 0);
+
+ *cp-- = 0;
+
+ // Copy in forwards
+ while (cp >= outbuf)
+ *s++ = *cp--;
+
+ *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+ int tail_chars;
+ char tail_str[12];
+
+ if (tailNum > 99999)
+ return 0;
+
+ // Convert to number
+ memset(tail_str, 0x00, sizeof(tail_str));
+ tail_str[0] = '~';
+ fatfs_itoa(tailNum, tail_str+1);
+
+ // Copy in base filename
+ memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+ // Overwrite with tail
+ tail_chars = (int)strlen(tail_str);
+ memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+ return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+ *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+ *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+ *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+ *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+ *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+ *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+ *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+ *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+ uint16 fat_time = 0;
+
+ // Most FAT times are to a resolution of 2 seconds
+ seconds /= FAT_TIME_SECONDS_SCALE;
+
+ fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+ fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+ fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+ return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+ uint16 fat_date = 0;
+
+ // FAT dates are relative to 1980
+ if (year >= FAT_DATE_YEAR_OFFSET)
+ year -= FAT_DATE_YEAR_OFFSET;
+
+ fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+ fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+ fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+ return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+ int i;
+ int j;
+
+ FAT_PRINTF(("Sector %d:\n", sector));
+
+ for (i=0;i 31 && ch < 127)
+ {
+ FAT_PRINTF(("%c", ch));
+ }
+ else
+ {
+ FAT_PRINTF(("."));
+ }
+ }
+
+ FAT_PRINTF(("\n"));
+ }
+ }
+}
+#endif
diff --git a/kernel/fs/fatfs/fat_misc.h b/kernel/fs/fatfs/fat_misc.h
new file mode 100644
index 0000000..0c02634
--- /dev/null
+++ b/kernel/fs/fatfs/fat_misc.h
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES 20
+#define MAX_LFN_ENTRY_LENGTH 13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location) ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location) ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); \
+ buffer[location+2] = (uint8)((value>>16)&0xFF); \
+ buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value) { buffer[location+0] = (uint8)((value)&0xFF); \
+ buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+ // Long File Name Structure (max 260 LFN length)
+ uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+ uint8 Null;
+#endif
+ uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int fatfs_entry_is_file(struct fat_dir_entry *entry);
+int fatfs_lfn_entries_required(char *filename);
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16 fatfs_convert_to_fat_date(int day, int month, int year);
+void fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/kernel/fs/fatfs/fat_opts.h b/kernel/fs/fatfs/fat_opts.h
new file mode 100644
index 0000000..ac4dc86
--- /dev/null
+++ b/kernel/fs/fatfs/fat_opts.h
@@ -0,0 +1,90 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+ #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+ #define FATFS_IS_LITTLE_ENDIAN 1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+ #define FATFS_MAX_LONG_FILENAME 260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+ #define FATFS_MAX_OPEN_FILES 2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES 128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+ #define FATFS_INC_WRITE_SUPPORT 1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+ #define FATFS_INC_LFN_SUPPORT 1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+ #define FATFS_DIR_LIST_SUPPORT 1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+ #define FATFS_INC_TIME_DATE_SUPPORT 0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+ #define FATFS_INC_FORMAT_SUPPORT 1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE 512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+ // Don't include stdio, but there is a printf function available
+ #ifdef FAT_PRINTF_NOINC_STDIO
+ extern int printf(const char* ctrl1, ... );
+ #define FAT_PRINTF(a) printf a
+ // Include stdio to use printf
+ #else
+ #include
+ #define FAT_PRINTF(a) printf a
+ #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+ #include
+#endif
+
+#endif
diff --git a/kernel/fs/fatfs/fat_string.c b/kernel/fs/fatfs/fat_string.c
new file mode 100644
index 0000000..f7206ce
--- /dev/null
+++ b/kernel/fs/fatfs/fat_string.c
@@ -0,0 +1,514 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+// c:\folder\file.zip
+// /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+ int levels = 0;
+ char expectedchar;
+
+ if (!path)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Count levels in path string
+ while (*path)
+ {
+ // Fast forward through actual subdir text to next slash
+ for (; *path; )
+ {
+ // If slash detected escape from for loop
+ if (*path == expectedchar) { path++; break; }
+ path++;
+ }
+
+ // Increase number of subdirs founds
+ levels++;
+ }
+
+ // Subtract the file itself
+ return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+ int i;
+ int pathlen=0;
+ int levels=0;
+ int copypnt=0;
+ char expectedchar;
+
+ if (!path || max_len <= 0)
+ return -1;
+
+ // Acceptable formats:
+ // c:\folder\file.zip
+ // /dev/etc/samba.conf
+ if (*path == '/')
+ {
+ expectedchar = '/';
+ path++;
+ }
+ else if (path[1] == ':' || path[2] == '\\')
+ {
+ expectedchar = '\\';
+ path += 3;
+ }
+ else
+ return -1;
+
+ // Get string length of path
+ pathlen = (int)strlen (path);
+
+ // Loop through the number of times as characters in 'path'
+ for (i = 0; i path = C:\folder filename = file.zip
+// E.g. C:\file.zip -> path = [blank] filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+ int strindex;
+
+ // Count the levels to the filepath
+ int levels = fatfs_total_path_levels(full_path);
+ if (levels == -1)
+ return -1;
+
+ // Get filename part of string
+ if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+ return -1;
+
+ // If root file
+ if (levels == 0)
+ path[0] = '\0';
+ else
+ {
+ strindex = (int)strlen(full_path) - (int)strlen(filename);
+ if (strindex > max_path)
+ strindex = max_path;
+
+ memcpy(path, full_path, strindex);
+ path[strindex-1] = '\0';
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+ int diff;
+ char a,b;
+
+ while (n--)
+ {
+ a = *s1;
+ b = *s2;
+
+ // Make lower case if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+ if ((b>='A') && (b<='Z'))
+ b+= 32;
+
+ diff = a - b;
+
+ // If different
+ if (diff)
+ return diff;
+
+ // If run out of strings
+ if ( (*s1 == 0) || (*s2 == 0) )
+ break;
+
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+ int dotPos = -1;
+ char *strSrc = str;
+
+ // Find last '.' in string (if at all)
+ while (*strSrc)
+ {
+ if (*strSrc=='.')
+ dotPos = (int)(strSrc-str);
+
+ strSrc++;
+ }
+
+ return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+ int length = strLen;
+ char *strSrc = str+strLen-1;
+
+ // Find last non white space
+ while (strLen != 0)
+ {
+ if (*strSrc == ' ')
+ length = (int)(strSrc - str);
+ else
+ break;
+
+ strSrc--;
+ strLen--;
+ }
+
+ return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+ char *ext1 = NULL;
+ char *ext2 = NULL;
+ int ext1Pos, ext2Pos;
+ int file1Len, file2Len;
+
+ // Get both files extension
+ ext1Pos = FileString_GetExtension(strA);
+ ext2Pos = FileString_GetExtension(strB);
+
+ // NOTE: Extension position can be different for matching
+ // filename if trailing space are present before it!
+ // Check that if one has an extension, so does the other
+ if ((ext1Pos==-1) && (ext2Pos!=-1))
+ return 0;
+ if ((ext2Pos==-1) && (ext1Pos!=-1))
+ return 0;
+
+ // If they both have extensions, compare them
+ if (ext1Pos!=-1)
+ {
+ // Set pointer to start of extension
+ ext1 = strA+ext1Pos+1;
+ ext2 = strB+ext2Pos+1;
+
+ // Verify that the file extension lengths match!
+ if (strlen(ext1) != strlen(ext2))
+ return 0;
+
+ // If they dont match
+ if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+ return 0;
+
+ // Filelength is upto extensions
+ file1Len = ext1Pos;
+ file2Len = ext2Pos;
+ }
+ // No extensions
+ else
+ {
+ // Filelength is actual filelength
+ file1Len = (int)strlen(strA);
+ file2Len = (int)strlen(strB);
+ }
+
+ // Find length without trailing spaces (before ext)
+ file1Len = FileString_TrimLength(strA, file1Len);
+ file2Len = FileString_TrimLength(strB, file2Len);
+
+ // Check the file lengths match
+ if (file1Len!=file2Len)
+ return 0;
+
+ // Compare main part of filenames
+ if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+ return 0;
+ else
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+ if (path)
+ {
+ while (*path)
+ {
+ // Last character?
+ if (!(*(path+1)))
+ {
+ if (*path == '\\' || *path == '/')
+ return 1;
+ }
+
+ path++;
+ }
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+ int len = 0;
+ while (*in && len <= 11)
+ {
+ char a = *in++;
+
+ if (a == ' ')
+ continue;
+ // Make lower case if uppercase
+ else if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+ int len = 0;
+
+ // Get files extension offset
+ int ext_pos = FileString_GetExtension(filename);
+
+ if (ext_pos > 0 && out && maxlen)
+ {
+ filename += ext_pos + 1;
+
+ while (*filename && len < (maxlen-1))
+ {
+ char a = *filename++;
+
+ // Make lowercase if uppercase
+ if ((a>='A') && (a<='Z'))
+ a+= 32;
+
+ *out++ = a;
+ len++;
+ }
+
+ *out = '\0';
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+ int len = 0;
+ char last = 0;
+ char seperator = '/';
+
+ if (path && filename && out && maxlen > 0)
+ {
+ while (*path && len < (maxlen-2))
+ {
+ last = *path++;
+ if (last == '\\')
+ seperator = '\\';
+ *out++ = last;
+ len++;
+ }
+
+ // Add a seperator if trailing one not found
+ if (last != '\\' && last != '/')
+ *out++ = seperator;
+
+ while (*filename && len < (maxlen-1))
+ {
+ *out++ = *filename++;
+ len++;
+ }
+
+ *out = '\0';
+
+ return 1;
+ }
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+ char output[255];
+ char output2[255];
+
+ assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+ assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+ assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+ assert(fatfs_total_path_levels("C:\\") == -1);
+ assert(fatfs_total_path_levels("") == -1);
+ assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+ assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "folder") == 0);
+
+ assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+ assert(strcmp(output, "dev") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+ assert(strcmp(output, "etc") == 0);
+
+ assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+ assert(strcmp(output, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "C:\\folder") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(output[0] == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+ assert(strcmp(output, "/dev/etc") == 0);
+ assert(strcmp(output2, "file.zip") == 0);
+
+ assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+ assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+ assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+ assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength("C:\\file.zip ", strlen("C:\\file.zip ")) == strlen("C:\\file.zip"));
+ assert(FileString_TrimLength(" ", strlen(" ")) == 0);
+
+ assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file.ext") == 1);
+ assert(fatfs_compare_names("C:\\file .ext", "C:\\file2.ext") == 0);
+
+ assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+ assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+ assert(fatfs_string_ends_with_slash("/path") == 0);
+ assert(fatfs_string_ends_with_slash("/path/a") == 0);
+ assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+ assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+ assert(strcmp(output, "wav") == 0);
+ assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+ assert(strcmp(output, "ext") != 0);
+
+ assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+ assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+ assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+ assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
diff --git a/kernel/fs/fatfs/fat_string.h b/kernel/fs/fatfs/fat_string.h
new file mode 100644
index 0000000..90ca8e0
--- /dev/null
+++ b/kernel/fs/fatfs/fat_string.h
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#endif
diff --git a/kernel/fs/fatfs/fat_table.c b/kernel/fs/fatfs/fat_table.c
new file mode 100644
index 0000000..c9903cf
--- /dev/null
+++ b/kernel/fs/fatfs/fat_table.c
@@ -0,0 +1,478 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+ #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+ #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+ #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+// FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location) ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location) ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+ int i;
+
+ // FAT buffer chain head
+ fs->fat_buffer_head = NULL;
+
+ for (i=0;ifat_buffers[i].address = FAT32_INVALID_CLUSTER;
+ fs->fat_buffers[i].dirty = 0;
+ memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+ fs->fat_buffers[i].ptr = NULL;
+
+ // Add to head of queue
+ fs->fat_buffers[i].next = fs->fat_buffer_head;
+ fs->fat_buffer_head = &fs->fat_buffers[i];
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fat_ctx *ctx, struct fatfs *fs, struct fat_buffer *pcur)
+{
+ if (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ {
+ if (fs->disk_io.write_media)
+ {
+ uint32 sectors = FAT_BUFFER_SECTORS;
+ uint32 offset = pcur->address - fs->fat_begin_lba;
+
+ // Limit to sectors used for the FAT
+ if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+ sectors = FAT_BUFFER_SECTORS;
+ else
+ sectors = fs->fat_sectors - offset;
+
+ if (!fs->disk_io.write_media(ctx, pcur->address, pcur->sector, sectors))
+ return 0;
+ }
+
+ pcur->dirty = 0;
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fat_ctx *ctx, struct fatfs *fs, uint32 sector)
+{
+ struct fat_buffer *last = NULL;
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Sector within this buffer?
+ if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+ break;
+
+ // End of list?
+ if (pcur->next == NULL)
+ {
+ // Remove buffer from list
+ if (last)
+ last->next = NULL;
+ // We the first and last buffer in the chain?
+ else
+ fs->fat_buffer_head = NULL;
+ }
+
+ last = pcur;
+ pcur = pcur->next;
+ }
+
+ // We found the sector already in FAT buffer chain
+ if (pcur)
+ {
+ pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+ return pcur;
+ }
+
+ // Else, we removed the last item from the list
+ pcur = last;
+
+ // Add to start of sector buffer list (now newest sector)
+ pcur->next = fs->fat_buffer_head;
+ fs->fat_buffer_head = pcur;
+
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(ctx, fs, pcur))
+ return 0;
+
+ // Address is now new sector
+ pcur->address = sector;
+
+ // Read next sector
+ if (!fs->disk_io.read_media(ctx, pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+ {
+ // Read failed, invalidate buffer address
+ pcur->address = FAT32_INVALID_CLUSTER;
+ return NULL;
+ }
+
+ pcur->ptr = pcur->sector;
+ return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fat_ctx *ctx, struct fatfs *fs)
+{
+ struct fat_buffer *pcur = fs->fat_buffer_head;
+
+ // Itterate through sector buffer list
+ while (pcur)
+ {
+ // Writeback sector if changed
+ if (pcur->dirty)
+ if (!fatfs_fat_writeback(ctx, fs, pcur))
+ return 0;
+
+ pcur = pcur->next;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 current_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ struct fat_buffer *pbuf;
+
+ // Why is '..' labelled with cluster 0 when it should be 2 ??
+ if (current_cluster == 0)
+ current_cluster = 2;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(ctx, fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return (FAT32_LAST_CLUSTER);
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+ // If end of chain found
+ if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+
+ // If end of chain found
+ if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+ return (FAT32_LAST_CLUSTER);
+ }
+
+ // Else return next cluster
+ return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 newValue)
+{
+ if (fs->fat_type == FAT_TYPE_16)
+ ;
+ else
+ {
+ // Load sector to change it
+ struct fat_buffer *pbuf = fatfs_fat_read_sector(ctx, fs, fs->lba_begin+fs->fs_info_sector);
+ if (!pbuf)
+ return ;
+
+ // Change
+ FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+ fs->next_free_cluster = newValue;
+
+ // Write back FSINFO sector to disk
+ if (fs->disk_io.write_media)
+ fs->disk_io.write_media(ctx, pbuf->address, pbuf->sector, 1);
+
+ // Invalidate cache entry
+ pbuf->address = FAT32_INVALID_CLUSTER;
+ pbuf->dirty = 0;
+ }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+ uint32 fat_sector_offset, position;
+ uint32 nextcluster;
+ uint32 current_cluster = start_cluster;
+ struct fat_buffer *pbuf;
+
+ do
+ {
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = current_cluster / 256;
+ else
+ fat_sector_offset = current_cluster / 128;
+
+ if ( fat_sector_offset < fs->fat_sectors)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(ctx, fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+ // Read Next Clusters value from Sector Buffer
+ nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+ // Mask out MS 4 bits (its 28bit addressing)
+ nextcluster = nextcluster & 0x0FFFFFFF;
+ }
+
+ if (nextcluster !=0 )
+ current_cluster++;
+ }
+ else
+ // Otherwise, run out of FAT sectors to check...
+ return 0;
+ }
+ while (nextcluster != 0x0);
+
+ // Found blank entry
+ *free_cluster = current_cluster;
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+ struct fat_buffer *pbuf;
+ uint32 fat_sector_offset, position;
+
+ // Find which sector of FAT table to read
+ if (fs->fat_type == FAT_TYPE_16)
+ fat_sector_offset = cluster / 256;
+ else
+ fat_sector_offset = cluster / 128;
+
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(ctx, fs, fs->fat_begin_lba+fat_sector_offset);
+ if (!pbuf)
+ return 0;
+
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ // Find 16 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 256)) * 2;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+ }
+ else
+ {
+ // Find 32 bit entry of current sector relating to cluster number
+ position = (cluster - (fat_sector_offset * 128)) * 4;
+
+ // Write Next Clusters value to Sector Buffer
+ FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster)
+{
+ uint32 last_cluster;
+ uint32 next_cluster = start_cluster;
+
+ // Loop until end of chain
+ while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(ctx, fs, next_cluster);
+
+ // Clear last link
+ fatfs_fat_set_cluster(ctx, fs, last_cluster, 0x00000000);
+ }
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+ uint32 last_cluster = FAT32_LAST_CLUSTER;
+ uint32 next_cluster = start_cluster;
+
+ if (start_cluster == FAT32_LAST_CLUSTER)
+ return 0;
+
+ // Loop until end of chain
+ while ( next_cluster != FAT32_LAST_CLUSTER )
+ {
+ last_cluster = next_cluster;
+
+ // Find next link
+ next_cluster = fatfs_find_next_cluster(ctx, fs, next_cluster);
+ if (!next_cluster)
+ return 0;
+ }
+
+ // Add link in for new cluster
+ fatfs_fat_set_cluster(ctx, fs, last_cluster, newEntry);
+
+ // Mark new cluster as end of chain
+ fatfs_fat_set_cluster(ctx, fs, newEntry, FAT32_LAST_CLUSTER);
+
+ return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fat_ctx *ctx, struct fatfs *fs)
+{
+ uint32 i,j;
+ uint32 count = 0;
+ struct fat_buffer *pbuf;
+
+ for (i = 0; i < fs->fat_sectors; i++)
+ {
+ // Read FAT sector into buffer
+ pbuf = fatfs_fat_read_sector(ctx, fs, fs->fat_begin_lba + i);
+ if (!pbuf)
+ break;
+
+ for (j = 0; j < FAT_SECTOR_SIZE; )
+ {
+ if (fs->fat_type == FAT_TYPE_16)
+ {
+ if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 2;
+ }
+ else
+ {
+ if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+ count++;
+
+ j += 4;
+ }
+ }
+ }
+
+ return count;
+}
diff --git a/kernel/fs/fatfs/fat_table.h b/kernel/fs/fatfs/fat_table.h
new file mode 100644
index 0000000..53338b3
--- /dev/null
+++ b/kernel/fs/fatfs/fat_table.h
@@ -0,0 +1,22 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+struct fat_ctx;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs);
+int fatfs_fat_purge(struct fat_ctx *ctx, struct fatfs *fs);
+uint32 fatfs_find_next_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 current_cluster);
+void fatfs_set_fs_info_next_free_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 newValue);
+int fatfs_find_blank_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int fatfs_fat_set_cluster(struct fat_ctx *ctx, struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int fatfs_fat_add_cluster_to_chain(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int fatfs_free_cluster_chain(struct fat_ctx *ctx, struct fatfs *fs, uint32 start_cluster);
+uint32 fatfs_count_free_clusters(struct fat_ctx *ctx, struct fatfs *fs);
+
+#endif
diff --git a/kernel/fs/fatfs/fat_types.h b/kernel/fs/fatfs/fat_types.h
new file mode 100644
index 0000000..5e2cca8
--- /dev/null
+++ b/kernel/fs/fatfs/fat_types.h
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+ #if __SIZEOF_LONG__ == 8
+ #define FATFS_DEF_UINT32_AS_INT
+ #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+
+ // If compiling on a 64-bit machine, use int as 32-bits
+ #ifdef FATFS_DEF_UINT32_AS_INT
+ typedef unsigned int uint32;
+ // Else for 32-bit machines & embedded systems, use long...
+ #else
+ typedef unsigned long uint32;
+ #endif
+#endif
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+ #define FAT_HTONS(n) (n)
+ #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+ #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+ #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+ ((((uint32)(n) & 0xFF00)) << 8) | \
+ ((((uint32)(n) & 0xFF0000)) >> 8) | \
+ ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED __attribute__ ((packed))
+#else
+ // Other compilers may require other methods of packing structures
+ #define STRUCT_PACK
+ #define STRUCT_PACK_BEGIN
+ #define STRUCT_PACK_END
+ #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/kernel/fs/fatfs/fat_write.c b/kernel/fs/fatfs/fat_write.c
new file mode 100644
index 0000000..98d6f57
--- /dev/null
+++ b/kernel/fs/fatfs/fat_write.c
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// FAT16/32 File IO Library
+// V2.6
+// Ultra-Embedded.com
+// Copyright 2003 - 2012
+//
+// Email: admin@ultra-embedded.com
+//
+// License: GPL
+// If you would like a version with a more permissive license for use in
+// closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO Library 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 FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fat_ctx *ctx, struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+ uint32 i;
+ uint32 nextcluster;
+ uint32 start = *startCluster;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(ctx, fs, FAT32_LAST_CLUSTER);
+
+ for (i=0;irootdir_first_cluster, &nextcluster))
+ {
+ // Point last to this
+ fatfs_fat_set_cluster(ctx, fs, start, nextcluster);
+
+ // Point this to end of file
+ fatfs_fat_set_cluster(ctx, fs, nextcluster, FAT32_LAST_CLUSTER);
+
+ // Adjust argument reference
+ start = nextcluster;
+ if (i == 0)
+ *startCluster = nextcluster;
+ }
+ else
+ return 0;
+ }
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fat_ctx *ctx, struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+ uint32 clusterSize;
+ uint32 clusterCount;
+ uint32 nextcluster;
+
+ if (size==0)
+ return 0;
+
+ // Set the next free cluster hint to unknown
+ if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+ fatfs_set_fs_info_next_free_cluster(ctx, fs, FAT32_LAST_CLUSTER);
+
+ // Work out size and clusters
+ clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+ clusterCount = (size / clusterSize);
+
+ // If any left over
+ if (size-(clusterSize*clusterCount))
+ clusterCount++;
+
+ // Allocated first link in the chain if a new file
+ if (newFile)
+ {
+ if (!fatfs_find_blank_cluster(ctx, fs, fs->rootdir_first_cluster, &nextcluster))
+ return 0;
+
+ // If this is all that is needed then all done
+ if (clusterCount==1)
+ {
+ fatfs_fat_set_cluster(ctx, fs, nextcluster, FAT32_LAST_CLUSTER);
+ *startCluster = nextcluster;
+ return 1;
+ }
+ }
+ // Allocate from end of current chain (startCluster is end of chain)
+ else
+ nextcluster = *startCluster;
+
+ if (!fatfs_add_free_space(ctx, fs, &nextcluster, clusterCount))
+ return 0;
+
+ return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fat_ctx *ctx, struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+ struct fat_dir_entry *directoryEntry;
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ int x=0;
+ int possible_spaces = 0;
+ int start_recorded = 0;
+
+ // No entries required?
+ if (entryCount == 0)
+ return 0;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // Overlay directory entry over buffer
+ directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+ // LFN Entry
+ if (fatfs_entry_lfn_text(directoryEntry))
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the count in-case the file turns
+ // out to be deleted...
+ possible_spaces++;
+ }
+ // SFN Entry
+ else
+ {
+ // Has file been deleted?
+ if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+
+ // Else continue counting until we find a valid entry!
+ }
+ // Is the file entry empty?
+ else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+ {
+ // First entry?
+ if (possible_spaces == 0)
+ {
+ // Store start
+ *pSector = x-1;
+ *pOffset = item;
+ start_recorded = 1;
+ }
+
+ // Increment the blank entries count
+ possible_spaces++;
+
+ // We have found enough space?
+ if (possible_spaces >= entryCount)
+ return 1;
+ }
+ // File entry is valid
+ else
+ {
+ // Reset all flags
+ possible_spaces = 0;
+ start_recorded = 0;
+ }
+ }
+ } // End of for
+ } // End of if
+ // Run out of free space in the directory, allocate some more
+ else
+ {
+ uint32 newCluster;
+
+ // Get a new cluster for directory
+ if (!fatfs_find_blank_cluster(ctx, fs, fs->rootdir_first_cluster, &newCluster))
+ return 0;
+
+ // Add cluster to end of directory tree
+ if (!fatfs_fat_add_cluster_to_chain(ctx, fs, dirCluster, newCluster))
+ return 0;
+
+ // Erase new directory cluster
+ memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+ for (i=0;isectors_per_cluster;i++)
+ {
+ if (!fatfs_write_sector(ctx, fs, newCluster, i, 0))
+ return 0;
+ }
+
+ // If non of the name fitted on previous sectors
+ if (!start_recorded)
+ {
+ // Store start
+ *pSector = (x-1);
+ *pOffset = 0;
+ start_recorded = 1;
+ }
+
+ return 1;
+ }
+ } // End of while loop
+
+ return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fat_ctx *ctx, struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+ uint8 item=0;
+ uint16 recordoffset = 0;
+ uint8 i=0;
+ uint32 x=0;
+ int entryCount;
+ struct fat_dir_entry shortEntry;
+ int dirtySector = 0;
+
+ uint32 dirSector = 0;
+ uint8 dirOffset = 0;
+ int foundEnd = 0;
+
+ uint8 checksum;
+ uint8 *pSname;
+
+ // No write access?
+ if (!fs->disk_io.write_media)
+ return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+ // How many LFN entries are required?
+ // NOTE: We always request one LFN even if it would fit in a SFN!
+ entryCount = fatfs_lfn_entries_required(filename);
+ if (!entryCount)
+ return 0;
+#else
+ entryCount = 0;
+#endif
+
+ // Find space in the directory for this filename (or allocate some more)
+ // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+ if (!fatfs_find_free_dir_offset(ctx, fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+ return 0;
+
+ // Generate checksum of short filename
+ pSname = (uint8*)shortfilename;
+ checksum = 0;
+ for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+ // Start from current sector where space was found!
+ x = dirSector;
+
+ // Main cluster following loop
+ while (1)
+ {
+ // Read sector
+ if (fatfs_sector_reader(ctx, fs, dirCluster, x++, 0))
+ {
+ // Analyse Sector
+ for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+ {
+ // Create the multiplier for sector access
+ recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+ // If the start position for the entry has been found
+ if (foundEnd==0)
+ if ( (dirSector==(x-1)) && (dirOffset==item) )
+ foundEnd = 1;
+
+ // Start adding filename
+ if (foundEnd)
+ {
+ if (entryCount==0)
+ {
+ // Short filename
+ fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+ // Update create, access & modify time & date
+ fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+ memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+ // Writeback
+ return fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1);
+ }
+#if FATFS_INC_LFN_SUPPORT
+ else
+ {
+ entryCount--;
+
+ // Copy entry to directory buffer
+ fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+ dirtySector = 1;
+ }
+#endif
+ }
+ } // End of if
+
+ // Write back to disk before loading another sector
+ if (dirtySector)
+ {
+ if (!fs->disk_io.write_media(ctx, fs->currentsector.address, fs->currentsector.sector, 1))
+ return 0;
+
+ dirtySector = 0;
+ }
+ }
+ else
+ return 0;
+ } // End of while loop
+
+ return 0;
+}
+#endif
diff --git a/kernel/fs/fatfs/fat_write.h b/kernel/fs/fatfs/fat_write.h
new file mode 100644
index 0000000..3d9b47d
--- /dev/null
+++ b/kernel/fs/fatfs/fat_write.h
@@ -0,0 +1,16 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+struct fat_ctx;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fat_ctx *ctx, struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fat_ctx *ctx, struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fat_ctx *ctx, struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/kernel/fs/portfatfs/portfatfs.c b/kernel/fs/portfatfs/portfatfs.c
new file mode 100644
index 0000000..904308b
--- /dev/null
+++ b/kernel/fs/portfatfs/portfatfs.c
@@ -0,0 +1,225 @@
+#include
+#include
+#include "fs/fatfs/fat_context.h"
+#include "fs/fatfs/fat_filelib.h"
+#include "sysdefs/fs.h"
+#include "vfs/vfs.h"
+#include "dlmalloc/malloc.h"
+#include "std/string.h"
+#include "spinlock/spinlock.h"
+#include "util/util.h"
+#include "errors.h"
+#include "kprintf.h"
+
+// REF: https://github.com/ultraembedded/fat_io_lib/blob/master/examples/sd_card_generic/sd.c
+
+int32_t fatfs_cleanup(struct VfsMountPoint *vmp) {
+ fl_shutdown(&vmp->fs.fatfs.instance);
+ return E_OK;
+}
+
+void fatfs_vobj_cleanup(struct VfsObj *vobj) {
+ if (vobj->extra != NULL) {
+ fl_fclose(&vobj->vmp->fs.fatfs.instance, vobj->extra);
+ }
+ dlfree(vobj);
+}
+
+int32_t fatfs_vobj_read(struct VfsObj *vobj, uint8_t *const buffer, size_t n, size_t off) {
+ if (!(vobj->flags & VFS_FLAG_READ)) {
+ return E_INVALIDOPER;
+ }
+
+ spinlock_acquire(&vobj->spinlock);
+
+ int ok = fl_fseek(&vobj->vmp->fs.fatfs.instance, vobj->extra, off, SEEK_SET);
+ if (ok < 0) {
+ spinlock_release(&vobj->spinlock);
+ return E_BADIO;
+ }
+
+ ok = fl_fread(&vobj->vmp->fs.fatfs.instance, buffer, 1, n, vobj->extra);
+ if (ok < 0) {
+ spinlock_release(&vobj->spinlock);
+ return E_BADIO;
+ }
+
+ spinlock_release(&vobj->spinlock);
+ return E_OK;
+}
+
+int32_t fatfs_vobj_write(struct VfsObj *vobj, const uint8_t *const buffer, size_t n, size_t off) {
+ if (!(vobj->flags & VFS_FLAG_WRITE)) {
+ return E_INVALIDOPER;
+ }
+
+ spinlock_acquire(&vobj->spinlock);
+
+ int ok = fl_fseek(&vobj->vmp->fs.fatfs.instance, vobj->extra, off, SEEK_SET);
+ if (ok < 0) {
+ spinlock_release(&vobj->spinlock);
+ return E_BADIO;
+ }
+
+ ok = fl_fwrite(&vobj->vmp->fs.fatfs.instance, buffer, 1, n, vobj->extra);
+ if (ok < 0) {
+ spinlock_release(&vobj->spinlock);
+ return E_BADIO;
+ }
+
+ spinlock_release(&vobj->spinlock);
+ return E_OK;
+}
+
+struct VfsObj *fatfs_open(struct VfsMountPoint *vmp, const char *path, uint32_t flags) {
+ VfsObj *vobj = dlmalloc(sizeof(*vobj));
+ if (vobj == NULL) {
+ return NULL;
+ }
+ memset(vobj, 0, sizeof(*vobj));
+ spinlock_init(&vobj->spinlock);
+
+ char *mods = dlmalloc(4);
+ memset(mods, 0, 4);
+
+ spinlock_acquire(&vmp->spinlock);
+
+ FatFs *fs = &vmp->fs.fatfs;
+
+ if ((flags & VFS_FLAG_READ) && !(flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
+ strcpy(mods, "rb");
+ } else if (!(flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
+ strcpy(mods, "wb");
+ } else if ((flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && !(flags & VFS_FLAG_MAKE)) {
+ strcpy(mods, "rb+");
+ } else if (!(flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && (flags & VFS_FLAG_MAKE)) {
+ strcpy(mods, "wb");
+ } else if ((flags & VFS_FLAG_READ) && (flags & VFS_FLAG_WRITE) && (flags & VFS_FLAG_MAKE)) {
+ strcpy(mods, "wb+");
+ }
+
+ void *f = fl_fopen(&fs->instance, path, mods);
+
+ dlfree(mods);
+
+ if (f == NULL) {
+ dlfree(vobj);
+ spinlock_release(&vmp->spinlock);
+ return NULL;
+ }
+
+ vobj->flags = flags;
+ vobj->extra = f;
+ vobj->vmp = vmp;
+ vobj->cleanup = &fatfs_vobj_cleanup;
+ vobj->read = &fatfs_vobj_read;
+ vobj->write = &fatfs_vobj_write;
+
+ spinlock_release(&vmp->spinlock);
+ return vobj;
+}
+
+int32_t fatfs_stat(struct VfsMountPoint *vmp, const char *path, FsStat *statbuf) {
+ spinlock_acquire(&vmp->spinlock);
+
+ if (fl_is_dir(&vmp->fs.fatfs.instance, path)) {
+ FL_DIR dirstat;
+ if (fl_opendir(&vmp->fs.fatfs.instance, path, &dirstat) == NULL) {
+ spinlock_release(&vmp->spinlock);
+ return E_NOENTRY;
+ }
+
+ statbuf->type = FSSTAT_DIR;
+ statbuf->size = 0;
+ fl_dirent dirent;
+ while (fl_readdir(&vmp->fs.fatfs.instance, &dirstat, &dirent) == 0) {
+ statbuf->size++;
+ }
+ fl_closedir(&vmp->fs.fatfs.instance, &dirstat);
+ } else {
+ FL_FILE *f = fl_fopen(&vmp->fs.fatfs.instance, path, "r");
+ statbuf->type = FSSTAT_FILE;
+ statbuf->size = f->filelength;
+ fl_fclose(&vmp->fs.fatfs.instance, f);
+ }
+
+ spinlock_release(&vmp->spinlock);
+ return E_OK;
+}
+
+int32_t fatfs_fetchdirent(struct VfsMountPoint *vmp, const char *path, FsDirent *direntbuf, size_t idx) {
+ spinlock_acquire(&vmp->spinlock);
+
+ FL_DIR dirstat;
+ FL_DIR *d = fl_opendir(&vmp->fs.fatfs.instance, path, &dirstat);
+ if (d == NULL) {
+ spinlock_release(&vmp->spinlock);
+ return E_BADIO;
+ }
+
+ fl_dirent dirent;
+ size_t i = 0;
+ while (fl_readdir(&vmp->fs.fatfs.instance, d, &dirent) == 0) {
+ if (i == idx) {
+ direntbuf->stat.type = dirent.is_dir ? FSSTAT_DIR : FSSTAT_FILE;
+ direntbuf->stat.size = dirent.is_dir ? 0 : dirent.size;
+ strncpy(direntbuf->name, dirent.filename, sizeof(direntbuf->name));
+ break;
+ }
+ i++;
+ }
+ fl_closedir(&vmp->fs.fatfs.instance, d);
+
+ spinlock_release(&vmp->spinlock);
+ return E_OK;
+}
+
+int32_t fatfs_mkdir(struct VfsMountPoint *vmp, const char *path) {
+ spinlock_acquire(&vmp->spinlock);
+ int err = fl_createdirectory(&vmp->fs.fatfs.instance, path);
+ spinlock_release(&vmp->spinlock);
+ return err == 0 ? E_OK : E_BADIO;
+}
+
+int32_t fatfs_delete(struct VfsMountPoint *vmp, const char *path) {
+ spinlock_acquire(&vmp->spinlock);
+ int err = fl_remove(&vmp->fs.fatfs.instance, path);
+ spinlock_release(&vmp->spinlock);
+ return err == 0 ? E_OK : E_BADIO;
+}
+
+int portfatfs_diskio_read(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count) {
+ VfsMountPoint *vmp = ctx->extra;
+
+ if (sector_count == 0) {
+ return 0;
+ }
+
+ uint64_t byteaddr = (uint64_t)sector * FAT_SECTOR_SIZE;
+ ptrdiff_t sector1 = byteaddr / vmp->backingsd->sectorsize;
+ ptrdiff_t sector_off = byteaddr % vmp->backingsd->sectorsize;
+
+ int32_t ret = vmp->backingsd->read(vmp->backingsd, (uint8_t *const)buffer, sector1, sector_off, sector_count * FAT_SECTOR_SIZE);
+ if (ret != E_OK) {
+ return 0;
+ }
+ return 1;
+}
+
+int portfatfs_diskio_write(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count) {
+ VfsMountPoint *vmp = ctx->extra;
+
+ if (sector_count == 0) {
+ return 0;
+ }
+
+ uint64_t byteaddr = (uint64_t)sector * FAT_SECTOR_SIZE;
+ ptrdiff_t sector1 = byteaddr / vmp->backingsd->sectorsize;
+ ptrdiff_t sector_off = byteaddr % vmp->backingsd->sectorsize;
+
+ int32_t ret = vmp->backingsd->write(vmp->backingsd, (const uint8_t *const)buffer, sector1, sector_off, sector_count * FAT_SECTOR_SIZE);
+ if (ret != E_OK) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/kernel/fs/portfatfs/portfatfs.h b/kernel/fs/portfatfs/portfatfs.h
new file mode 100644
index 0000000..3aa2dc7
--- /dev/null
+++ b/kernel/fs/portfatfs/portfatfs.h
@@ -0,0 +1,26 @@
+#ifndef FS_PORTFATFS_PORTFATFS_H_
+#define FS_PORTFATFS_PORTFATFS_H_
+
+#include
+#include
+#include "fs/fatfs/fat_context.h"
+#include "sysdefs/fs.h"
+
+struct VfsMountPoint;
+struct VfsObj;
+
+typedef struct {
+ struct fat_ctx instance;
+} FatFs;
+
+int32_t fatfs_cleanup(struct VfsMountPoint *vmp);
+struct VfsObj *fatfs_open(struct VfsMountPoint *vmp, const char *path, uint32_t flags);
+int32_t fatfs_stat(struct VfsMountPoint *vmp, const char *path, FsStat *statbuf);
+int32_t fatfs_fetchdirent(struct VfsMountPoint *vmp, const char *path, FsDirent *direntbuf, size_t idx);
+int32_t fatfs_mkdir(struct VfsMountPoint *vmp, const char *path);
+int32_t fatfs_delete(struct VfsMountPoint *vmp, const char *path);
+
+int portfatfs_diskio_read(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count);
+int portfatfs_diskio_write(struct fat_ctx *ctx, uint32_t sector, uint8_t *buffer, uint32_t sector_count);
+
+#endif // FS_PORTFATFS_PORTFATFS_H_
diff --git a/kernel/fs/portlfs/portlfs.c b/kernel/fs/portlfs/portlfs.c
index b19f891..72547f3 100644
--- a/kernel/fs/portlfs/portlfs.c
+++ b/kernel/fs/portlfs/portlfs.c
@@ -165,7 +165,6 @@ struct VfsObj *littlefs_open(struct VfsMountPoint *vmp, const char *path, uint32
vobj->flags = flags;
vobj->extra = file;
- vobj->extrasize = sizeof(*file);
vobj->vmp = vmp;
vobj->cleanup = &littlefs_vobj_cleanup;
vobj->read = &littlefs_vobj_read;
diff --git a/kernel/std/assert.h b/kernel/std/assert.h
new file mode 100644
index 0000000..8900e4e
--- /dev/null
+++ b/kernel/std/assert.h
@@ -0,0 +1,6 @@
+#ifndef STD_ASSERT_H_
+#define STD_ASSERT_H_
+
+#define assert(x)
+
+#endif // STD_ASSERT_H_
diff --git a/kernel/std/string.c b/kernel/std/string.c
index 778d797..63d3da8 100644
--- a/kernel/std/string.c
+++ b/kernel/std/string.c
@@ -14,7 +14,7 @@ void *memcpy(void *dst, const void *src, size_t n) {
return dst;
}
-size_t strlen(char *s) {
+size_t strlen(const char *s) {
char *s2;
for (s2 = s; *s2; ++s2);
return (s2 - s);
@@ -149,3 +149,27 @@ char *strcat(char *dest, const char *src) {
dest[i+j] = '\0';
return dest;
}
+
+int strncmp( const char * s1, const char * s2, size_t n )
+{
+ while ( n && *s1 && ( *s1 == *s2 ) )
+ {
+ ++s1;
+ ++s2;
+ --n;
+ }
+ if ( n == 0 )
+ {
+ return 0;
+ }
+ else
+ {
+ return ( *(unsigned char *)s1 - *(unsigned char *)s2 );
+ }
+}
+
+void strncpy( char* _dst, const char* _src, size_t _n )
+{
+ size_t i = 0;
+ while(i++ != _n && (*_dst++ = *_src++));
+}
diff --git a/kernel/std/string.h b/kernel/std/string.h
index 4314800..6a3afa0 100644
--- a/kernel/std/string.h
+++ b/kernel/std/string.h
@@ -5,7 +5,7 @@
void *memset(void *p, int c, size_t n);
void *memcpy(void *dst, const void *src, size_t n);
-size_t strlen(char *s);
+size_t strlen(const char *s);
int strcmp(const char *a, const char *b);
size_t strcspn(const char *s, const char *reject);
size_t strspn(const char *s, const char *accept);
@@ -14,5 +14,7 @@ char *strchr(const char *s, int c);
int memcmp(const void *s1, const void *s2, int len);
char *strstr(const char *str, const char *substring);
char *strcat(char *dest, const char *src);
+int strncmp( const char * s1, const char * s2, size_t n );
+void strncpy( char* _dst, const char* _src, size_t _n );
#endif // STD_STRING_H_
diff --git a/kernel/syscall/vfs.c b/kernel/syscall/vfs.c
index f1f15a8..134aeb7 100644
--- a/kernel/syscall/vfs.c
+++ b/kernel/syscall/vfs.c
@@ -32,6 +32,8 @@ int32_t SYSCALL4(sys_vfsmount, mountpoint1, fstypestr1, devid1, format1) {
if (strcmp(fstypestr, "LittleFS") == 0) {
fstype = VFS_LITTLEFS;
+ } else if (strcmp(fstypestr, "FAT16") == 0) {
+ fstype = VFS_FAT16;
} else {
ret = E_INVALIDARGUMENT;
goto done;
diff --git a/kernel/vfs/vfs.c b/kernel/vfs/vfs.c
index 69c26ea..c4c7ebc 100644
--- a/kernel/vfs/vfs.c
+++ b/kernel/vfs/vfs.c
@@ -4,6 +4,10 @@
#include "spinlock/spinlock.h"
#include "util/util.h"
#include "fs/portlfs/portlfs.h"
+#include "fs/portfatfs/portfatfs.h"
+#include "fs/fatfs/fat_filelib.h"
+#include "fs/fatfs/fat_format.h"
+#include "fs/fatfs/fat_opts.h"
#include "storedev/storedev.h"
#include "baseimg/baseimg.h"
#include "dlmalloc/malloc.h"
@@ -36,7 +40,7 @@ VfsBusyObjs VFS_BUSY_VOBJS;
\
dlfree(tmpbuf);
-void vfs_init_littlefs(VfsMountPoint *mp, bool format) {
+int32_t vfs_init_littlefs(VfsMountPoint *mp, bool format) {
struct lfs_config *cfg = dlmalloc(sizeof(*cfg));
memset(cfg, 0, sizeof(*cfg));
cfg->context = mp;
@@ -63,6 +67,7 @@ void vfs_init_littlefs(VfsMountPoint *mp, bool format) {
int err = lfs_mount(&mp->fs.littlefs.instance, cfg);
if (err < 0) {
ERR("vfs", "Little FS mount failed %d\n", err);
+ return E_MOUNTERR;
}
mp->cleanup = &littlefs_cleanup;
@@ -71,6 +76,32 @@ void vfs_init_littlefs(VfsMountPoint *mp, bool format) {
mp->fetchdirent = &littlefs_fetchdirent;
mp->mkdir = &littlefs_mkdir;
mp->delete = &littlefs_delete;
+ return E_OK;
+}
+
+int32_t vfs_init_fat16(VfsMountPoint *mp, bool format) {
+ mp->fs.fatfs.instance.extra = mp;
+ fl_init(&mp->fs.fatfs.instance);
+ int err = fl_attach_media(&mp->fs.fatfs.instance, &portfatfs_diskio_read, &portfatfs_diskio_write);
+ if (err != FAT_INIT_OK) {
+ ERR("vfs", "FAT16 mount failed %d\n", err);
+ return E_MOUNTERR;
+ }
+ if (format) {
+ fatfs_format_fat16(&mp->fs.fatfs.instance,
+ &mp->fs.fatfs.instance._fs,
+ mp->backingsd->capacity(mp->backingsd) / FAT_SECTOR_SIZE,
+ "MOP2 FAT16"
+ );
+ }
+
+ mp->cleanup = &fatfs_cleanup;
+ mp->open = &fatfs_open;
+ mp->stat = &fatfs_stat;
+ mp->fetchdirent = &fatfs_fetchdirent;
+ mp->mkdir = &fatfs_mkdir;
+ mp->delete = &fatfs_delete;
+ return E_OK;
}
int32_t vfs_stat(char *mountpoint, const char *path, FsStat *stat) {
@@ -149,15 +180,19 @@ int32_t vfs_mount(char *mountpoint, int32_t fstype, StoreDev *backingsd, bool fo
memcpy(mp->label, mountpoint, strlen(mountpoint));
mp->backingsd = backingsd;
mp->fstype = fstype;
+ int32_t err;
switch (fstype) {
case VFS_LITTLEFS:
- vfs_init_littlefs(mp, format);
+ err = vfs_init_littlefs(mp, format);
+ break;
+ case VFS_FAT16:
+ err = vfs_init_fat16(mp, format);
break;
default:
return E_UNKNOWN_FSTYPE;
}
- return E_OK;
+ return err != E_OK ? err : E_OK;
}
int32_t vfs_unmount(char *mountpoint) {
diff --git a/kernel/vfs/vfs.h b/kernel/vfs/vfs.h
index 6b6d6fa..82a963d 100644
--- a/kernel/vfs/vfs.h
+++ b/kernel/vfs/vfs.h
@@ -6,6 +6,7 @@
#include
#include "spinlock/spinlock.h"
#include "fs/portlfs/portlfs.h"
+#include "fs/portfatfs/portfatfs.h"
#include "storedev/storedev.h"
#include "sysdefs/fs.h"
#include "compiler/attr.h"
@@ -15,10 +16,14 @@
enum {
VFS_LITTLEFS,
+ VFS_FAT16,
+ VFS_FAT32,
};
UNUSED static const char *vfs_strings[] = {
"Little FS",
+ "FAT16",
+ "FAT32",
};
enum {
@@ -34,7 +39,6 @@ typedef struct VfsObj {
SpinLock spinlock;
char path[VFS_MOUNTPOINT_LABEL_MAX+1+VFS_PATH_MAX];
void *extra;
- size_t extrasize;
struct VfsMountPoint *vmp;
int32_t flags;
int32_t (*read)(struct VfsObj *vobj, uint8_t *const buffer, size_t n, size_t off);
@@ -65,6 +69,7 @@ typedef struct VfsMountPoint {
union {
LittleFs littlefs;
+ FatFs fatfs;
} fs;
SpinLock spinlock;
} VfsMountPoint;
diff --git a/share/errors.h b/share/errors.h
index ef6581a..3ce76ae 100644
--- a/share/errors.h
+++ b/share/errors.h
@@ -16,6 +16,7 @@
#define E_RESOURCEAVAIL -12
#define E_SPAWNERROR -13
#define E_NOTYET -14
+#define E_MOUNTERR -15
#if !defined(__ASSEMBLER__)
@@ -36,6 +37,7 @@ static const char *_ERROR_STRINGS[] = {
"Resource already available",
"Process spawn error",
"Data isn't ready yet",
+ "File system mount error",
};
#define ERRSTRING_INDEX(ioh) ((size_t)((ioh) < 0 ? (ioh) * (-1) : (ioh)))