diff --git a/emummc/.gitignore b/emummc/.gitignore new file mode 100644 index 000000000..3c00ac63a --- /dev/null +++ b/emummc/.gitignore @@ -0,0 +1,9 @@ +*.kip +*.data +*.elf +build +.vscode/ipch +.vscode/settings.json +*.exe +*.kip* +emummc.caps diff --git a/emummc/.gitrepo b/emummc/.gitrepo new file mode 100644 index 000000000..6f949b074 --- /dev/null +++ b/emummc/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/m4xw/emuMMC + branch = develop + commit = e72e8f1c8fb7ad8fe7cdedc3784729ea8e11f927 + parent = 87a1aa17a7693ef39ffea91ad0fa1b530f278bb0 + method = rebase + cmdver = 0.4.0 diff --git a/emummc/.vscode/c_cpp_properties.json b/emummc/.vscode/c_cpp_properties.json new file mode 100644 index 000000000..91df1079b --- /dev/null +++ b/emummc/.vscode/c_cpp_properties.json @@ -0,0 +1,35 @@ +{ + "configurations": [ + { + "name": "Switch", + "includePath": [ + "/opt/devkitpro/devkitA64/aarch64-none-elf/include", + "/opt/devkitpro/devkitA64/lib/gcc/aarch64-none-elf/8.3.0/include", + "${workspaceFolder}/libnx-patched/nx/include", + "/opt/devkitpro/portlibs/switch/include", + "/opt/devkitpro/portlibs/switch/include/freetype2", + "${workspaceFolder}/source", + "${workspaceFolder}/source/emmc", + "${workspaceFolder}/source/libs/fatfs", + "${workspaceFolder}/source/power", + "${workspaceFolder}/source/soc", + "${workspaceFolder}/source/utils", + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "__aarch64__", + "__SWITCH__", + "INNER_HEAP_SIZE=0x80000" + ], + "windowsSdkVersion": "10.0.17763.0", + "compilerPath": "/opt/devkitpro/devkitA64/bin/aarch64-none-elf-gcc", + "cStandard": "c11", + "cppStandard": "c++11", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/emummc/LICENSE b/emummc/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/emummc/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/emummc/Makefile b/emummc/Makefile new file mode 100644 index 000000000..664399753 --- /dev/null +++ b/emummc/Makefile @@ -0,0 +1,114 @@ +.SUFFIXES: + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TARGET := emummc +BUILD := build +SOURCES := source/nx source source/utils source/emmc source/soc source/power source/emuMMC source/FS source/libs/fatfs +DATA := data +INCLUDES := include +EXEFS_SRC := exefs_src + +ifneq ($(BUILD),$(notdir $(CURDIR))) +EMUMMCDIR ?= $(CURDIR) +else +EMUMMCDIR ?= $(CURDIR)/../ +endif + +include $(EMUMMCDIR)/nx/switch_rules + +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +DEFINES := -DINNER_HEAP_SIZE=0x80000 + +CFLAGS := -Wall -O2 -ffunction-sections -Wno-unused-function \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(EMUMMCDIR)/nx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +ifneq ($(BUILD),$(notdir $(CURDIR))) + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(EMUMMCDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).elf $(TARGET).kip + +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +all : $(OUTPUT).kip + +$(OUTPUT).kip : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +%.bin.o %_bin.h : %.bin + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +endif diff --git a/emummc/README.md b/emummc/README.md new file mode 100644 index 000000000..4e46cb134 --- /dev/null +++ b/emummc/README.md @@ -0,0 +1,36 @@ +# emuMMC +*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw*** + +### Supported Horizon Versions +**1.0.0 - 8.0.1** + +## Features +* Arbitrary SDMMC backend selection + **This allows loading eMMC from SD or even SD from eMMC** +* On the fly hooking / patching, fully self-infesting + **Only one payload required for all versions!** +* File-based SDMMC backend support (from SD) + **This allows loading eMMC images from hekate-backups (split or not)** +* SDMMC device based sector offset (*currently eMMC only*) + **Raw partition support for eMMC from SD with less performance overhead** +* Full support for `/Nintendo` folder redirection to a arbitrary path + **No 8 char length restriction!** +* exosphere based context configuration + **This includes full support for multiple emuMMC images** + +## Compiling +### hekate +Run `./build.sh` and copy the produced kipm (Kernel Initial Process Modification) file to `/bootloader/sys/` + +### Atmosphere +Run `make`, the resulting kip can be used for code injection via fusee (place at `/atmosphere/emummc.kip`) + +## License +**emuMMC is released as GPLv2** + +## Credits +* **CTCaer** - The CTCaer hekate fork, file-based emuMMC support, SDMMC driver fixes among other things +* **SciresM, hexkyz** - The Atmosphere project, FS offsets, additional research related to newer FS versions +* **naehrwert** - The hekate project, its SDMMC driver and being very helpful in the early research phase +* **jakibaki** - KIP Inject PoC, used in the early dev phase +* **switchbrew/devkitPro** - devkitA64 and libnx sources diff --git a/emummc/build.sh b/emummc/build.sh new file mode 100644 index 000000000..b33895374 --- /dev/null +++ b/emummc/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +make clean +make -j +./hactool.exe -t kip emummc.kip --uncompressed emummc_unpacked.kip +python2.7 tools/kip1converter.py emummc_unpacked.kip emummc.data +cat emummc.caps emummc.data > emummc.kipm diff --git a/emummc/emummc.json b/emummc/emummc.json new file mode 100644 index 000000000..c23e32224 --- /dev/null +++ b/emummc/emummc.json @@ -0,0 +1,132 @@ +{ + "name": "FS", + "title_id": "0x0100000000000000", + "main_thread_stack_size": "0x00008000", + "main_thread_priority": 45, + "default_cpu_id": 3, + "process_category": 1, + "kernel_capabilities": [ + { + "type": "map_page", + "value": "0x60006000" + }, + { + "type": "map", + "value": { + "address": "0x6000D000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x700b0000", + "is_ro": false, + "size": "0x00005000", + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x7000C000", + "is_ro": false, + "size": "0x00002000", + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x70000000", + "is_ro": false, + "size": "0x00004000", + "is_io": true + } + }, + { + "type": "handle_table_size", + "value": 256 + }, + { + "type": "irq_pair", + "value": [ + 46, + 47 + ] + }, + { + "type": "irq_pair", + "value": [ + 51, + 63 + ] + }, + { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCreateInterruptEvent": "0x53", + "svcQueryIoMapping": "0x55", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcDetachDeviceAddressSpace": "0x58", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcGetSystemInfo": "0x6f", + "svcSetProcessMemoryPermission": "0x73", + "svcCallSecureMonitor": "0x7f" + } + } + ] +} \ No newline at end of file diff --git a/emummc/nx/switch.ld b/emummc/nx/switch.ld new file mode 100644 index 000000000..9aaae2fe7 --- /dev/null +++ b/emummc/nx/switch.ld @@ -0,0 +1,229 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) + +PHDRS +{ + code PT_LOAD FLAGS(5) /* Read | Execute */; + rodata PT_LOAD FLAGS(4) /* Read */; + data PT_LOAD FLAGS(6) /* Read | Write */; + dyn PT_DYNAMIC; +} + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = 0x0); + . = __start__; + __code_start = . ; + + /*.trampoline : + { + KEEP (*(.trampoline)) + . = ALIGN(8); + } :code + + .emuMMC_ctx : + { + KEEP (*(.emuMMC_ctx)) + . = ALIGN(8); + } :code */ + + .crt0 : + { + KEEP (*(.crt0)) + . = ALIGN(8); + } :code + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } :code + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } :code + + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + . = ALIGN(8); + } :code + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } :code + + /* =========== RODATA section =========== */ + . = ALIGN(0x1000); + __rodata_start = . ; + + .nx-module-name : { KEEP (*(.nx-module-name)) } :rodata + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } :rodata + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } :rodata + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata + + .dynamic : { *(.dynamic) } :rodata :dyn + .dynsym : { *(.dynsym) } :rodata + .dynstr : { *(.dynstr) } :rodata + .rela.dyn : { *(.rela.*) } :rodata + .interp : { *(.interp) } :rodata + .hash : { *(.hash) } :rodata + .gnu.hash : { *(.gnu.hash) } :rodata + .gnu.version : { *(.gnu.version) } :rodata + .gnu.version_d : { *(.gnu.version_d) } :rodata + .gnu.version_r : { *(.gnu.version_r) } :rodata + .note.gnu.build-id : { *(.note.gnu.build-id) } :rodata + + /* =========== DATA section =========== */ + . = ALIGN(0x1000); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data + + .tdata ALIGN(8) : + { + __tdata_lma = .; + *(.tdata .tdata.* .gnu.linkonce.td.*) + . = ALIGN(8); + __tdata_lma_end = .; + } :data + + .tbss ALIGN(8) : + { + *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) + . = ALIGN(8); + } :data + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } :data + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } :data + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } :data + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } :data + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } :data + + __got_start__ = .; + + .got : { *(.got) *(.igot) } :data + .got.plt : { *(.got.plt) *(.igot.plt) } :data + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } :data + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(8); + + /* Reserve space for the TLS segment of the main thread */ + __tls_start = .; + . += + SIZEOF(.tdata) + SIZEOF(.tbss); + __tls_end = .; + } : data + __bss_end__ = .; + + . = ALIGN(0x1000); + __end__ = ABSOLUTE(.) ; + + PROVIDE(__injected_size__ = (__end__ - __start__)); + + /* ================== + ==== Metadata ==== + ================== */ + + /* Discard sections that difficult post-processing */ + /DISCARD/ : { *(.group .comment .note) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } +} diff --git a/emummc/nx/switch.specs b/emummc/nx/switch.specs new file mode 100644 index 000000000..819dc3e24 --- /dev/null +++ b/emummc/nx/switch.specs @@ -0,0 +1,8 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(PWD /nx/switch.ld) -pie --gc-sections -z text -z nodynamic-undefined-weak --build-id=sha1 --nx-module-name + +*startfile: +crti%O%s crtbegin%O%s + diff --git a/emummc/nx/switch_rules b/emummc/nx/switch_rules new file mode 100644 index 000000000..ddbe82e90 --- /dev/null +++ b/emummc/nx/switch_rules @@ -0,0 +1,81 @@ +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=devkitPro) +endif + +include $(DEVKITPRO)/devkitA64/base_rules + +PORTLIBS := $(PORTLIBS_PATH)/switch +PATH := $(PORTLIBS)/bin:$(PATH) + +LIBNX ?= $(DEVKITPRO)/libnx + +ifeq ($(strip $(APP_TITLE)),) +APP_TITLE := $(notdir $(OUTPUT)) +endif + +ifeq ($(strip $(APP_AUTHOR)),) +APP_AUTHOR := Unspecified Author +endif + +ifeq ($(strip $(APP_VERSION)),) +APP_VERSION := 1.0.0 +endif + +ifeq ($(strip $(APP_ICON)),) +APP_ICON := $(LIBNX)/default_icon.jpg +endif + +#--------------------------------------------------------------------------------- +%.nacp: $(MAKEFILE_LIST) + @nacptool --create "$(APP_TITLE)" "$(APP_AUTHOR)" "$(APP_VERSION)" $@ $(NACPFLAGS) + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +%.npdm: $(APP_JSON) + @npdmtool $< $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +define make_pfs0 + @mkdir -p exefs + @[ $(BUILD_EXEFS_SRC) ] && [ -d $(BUILD_EXEFS_SRC) ] && cp -R $(BUILD_EXEFS_SRC)/* exefs || echo > /dev/null + @cp $*.nso exefs/main + @[ $(APP_JSON) ] && cp $*.npdm exefs/main.npdm || echo > /dev/null + @build_pfs0 exefs $@ + @echo built ... $(notdir $@) +endef + +ifeq ($(strip $(APP_JSON)),) +%.pfs0: %.nso +else +%.pfs0: %.nso %.npdm +endif + $(make_pfs0) + +ifeq ($(strip $(APP_JSON)),) +%.nsp: %.nso +else +%.nsp: %.nso %.npdm +endif + $(make_pfs0) + +#--------------------------------------------------------------------------------- +%.nso: %.elf + @elf2nso $< $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +%.nro: %.elf + @elf2nro $< $@ $(NROFLAGS) + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +%.kip: %.elf + @elf2kip $< $(APP_JSON) $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +%.elf: + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + @$(NM) -CSn $@ > $(notdir $*.lst) diff --git a/emummc/source/FS/FS.h b/emummc/source/FS/FS.h new file mode 100644 index 000000000..8a2b171ab --- /dev/null +++ b/emummc/source/FS/FS.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __FS_H__ +#define __FS_H__ + +// TODO +#include "../emmc/sdmmc_t210.h" + +#include "FS_versions.h" +#include "FS_offsets.h" +#include "FS_structs.h" + +#define FS_SDMMC_EMMC 0 +#define FS_SDMMC_SD 1 +#define FS_SDMMC_GC 2 + +#define FS_EMMC_PARTITION_GPP 0 +#define FS_EMMC_PARTITION_BOOT0 1 +#define FS_EMMC_PARTITION_BOOT1 2 +#define FS_EMMC_PARTITION_INVALID 3 + +#define BOOT_PARTITION_SIZE 0x2000 +#define FS_READ_WRITE_ERROR 1048 + +#endif /* __FS_H__ */ diff --git a/emummc/source/FS/FS_offsets.c b/emummc/source/FS/FS_offsets.c new file mode 100644 index 000000000..7f0cff371 --- /dev/null +++ b/emummc/source/FS/FS_offsets.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "FS_offsets.h" +#include "offsets/100.h" +#include "offsets/200.h" +#include "offsets/200_exfat.h" +#include "offsets/210.h" +#include "offsets/210_exfat.h" +#include "offsets/300.h" +#include "offsets/300_exfat.h" +#include "offsets/301.h" +#include "offsets/301_exfat.h" +#include "offsets/400.h" +#include "offsets/400_exfat.h" +#include "offsets/410.h" +#include "offsets/410_exfat.h" +#include "offsets/500.h" +#include "offsets/500_exfat.h" +#include "offsets/510.h" +#include "offsets/510_exfat.h" +#include "offsets/600.h" +#include "offsets/600_exfat.h" +#include "offsets/700.h" +#include "offsets/700_exfat.h" +#include "offsets/800.h" +#include "offsets/800_exfat.h" +#include "../utils/fatal.h" + +#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers + +#define DEFINE_OFFSET_STRUCT(vers) \ +static const fs_offsets_t GET_OFFSET_STRUCT_NAME(vers) = { \ + .sdmmc_accessor_gc = FS_OFFSET##vers##_SDMMC_ACCESSOR_GC, \ + .sdmmc_accessor_sd = FS_OFFSET##vers##_SDMMC_ACCESSOR_SD, \ + .sdmmc_accessor_nand = FS_OFFSET##vers##_SDMMC_ACCESSOR_NAND, \ + .sdmmc_wrapper_read = FS_OFFSET##vers##_SDMMC_WRAPPER_READ, \ + .sdmmc_wrapper_write = FS_OFFSET##vers##_SDMMC_WRAPPER_WRITE, \ + .clkrst_set_min_v_clock_rate = FS_OFFSET##vers##_CLKRST_SET_MIN_V_CLK_RATE, \ + .rtld = FS_OFFSET##vers##_RTLD, \ + .rtld_destination = FS_OFFSET##vers##_RTLD_DESTINATION, \ + .lock_mutex = FS_OFFSET##vers##_LOCK_MUTEX, \ + .unlock_mutex = FS_OFFSET##vers##_UNLOCK_MUTEX, \ + .sd_mutex = FS_OFFSET##vers##_SD_MUTEX, \ + .nand_mutex = FS_OFFSET##vers##_NAND_MUTEX, \ + .active_partition = FS_OFFSET##vers##_ACTIVE_PARTITION, \ + .sdmmc_das_handle = FS_OFFSET##vers##_SDMMC_DAS_HANDLE, \ + .shutdown_sd = FS_OFFSET##vers##_SHUTDOWN_SD, \ + .sd_das_init = FS_OFFSET##vers##_SD_DAS_INIT, \ + .nintendo_paths = FS_OFFSET##vers##_NINTENDO_PATHS, \ +} + +// Actually define offset structs +DEFINE_OFFSET_STRUCT(_100); +DEFINE_OFFSET_STRUCT(_200); +DEFINE_OFFSET_STRUCT(_200_EXFAT); +DEFINE_OFFSET_STRUCT(_210); +DEFINE_OFFSET_STRUCT(_210_EXFAT); +DEFINE_OFFSET_STRUCT(_300); +DEFINE_OFFSET_STRUCT(_300_EXFAT); +DEFINE_OFFSET_STRUCT(_301); +DEFINE_OFFSET_STRUCT(_301_EXFAT); +DEFINE_OFFSET_STRUCT(_400); +DEFINE_OFFSET_STRUCT(_400_EXFAT); +DEFINE_OFFSET_STRUCT(_410); +DEFINE_OFFSET_STRUCT(_410_EXFAT); +DEFINE_OFFSET_STRUCT(_500); +DEFINE_OFFSET_STRUCT(_500_EXFAT); +DEFINE_OFFSET_STRUCT(_510); +DEFINE_OFFSET_STRUCT(_510_EXFAT); +DEFINE_OFFSET_STRUCT(_600); +DEFINE_OFFSET_STRUCT(_600_EXFAT); +DEFINE_OFFSET_STRUCT(_700); +DEFINE_OFFSET_STRUCT(_700_EXFAT); +DEFINE_OFFSET_STRUCT(_800); +DEFINE_OFFSET_STRUCT(_800_EXFAT); + +const fs_offsets_t *get_fs_offsets(enum FS_VER version) { + switch (version) { + case FS_VER_1_0_0: + return &(GET_OFFSET_STRUCT_NAME(_100)); + case FS_VER_2_0_0: + return &(GET_OFFSET_STRUCT_NAME(_200)); + case FS_VER_2_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_200_EXFAT)); + case FS_VER_2_1_0: + return &(GET_OFFSET_STRUCT_NAME(_210)); + case FS_VER_2_1_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_210_EXFAT)); + case FS_VER_3_0_0: + return &(GET_OFFSET_STRUCT_NAME(_300)); + case FS_VER_3_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_300_EXFAT)); + case FS_VER_3_0_1: + return &(GET_OFFSET_STRUCT_NAME(_301)); + case FS_VER_3_0_1_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_301_EXFAT)); + case FS_VER_4_0_0: + return &(GET_OFFSET_STRUCT_NAME(_400)); + case FS_VER_4_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_400_EXFAT)); + case FS_VER_4_1_0: + return &(GET_OFFSET_STRUCT_NAME(_410)); + case FS_VER_4_1_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_410_EXFAT)); + case FS_VER_5_0_0: + return &(GET_OFFSET_STRUCT_NAME(_500)); + case FS_VER_5_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_500_EXFAT)); + case FS_VER_5_1_0: + return &(GET_OFFSET_STRUCT_NAME(_510)); + case FS_VER_5_1_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_510_EXFAT)); + case FS_VER_6_0_0: + return &(GET_OFFSET_STRUCT_NAME(_600)); + case FS_VER_6_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_600_EXFAT)); + case FS_VER_7_0_0: + return &(GET_OFFSET_STRUCT_NAME(_700)); + case FS_VER_7_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_700_EXFAT)); + case FS_VER_8_0_0: + return &(GET_OFFSET_STRUCT_NAME(_800)); + case FS_VER_8_0_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_800_EXFAT)); + default: + fatal_abort(Fatal_UnknownVersion); + } +} \ No newline at end of file diff --git a/emummc/source/FS/FS_offsets.h b/emummc/source/FS/FS_offsets.h new file mode 100644 index 000000000..8a3256598 --- /dev/null +++ b/emummc/source/FS/FS_offsets.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __FS_OFFSETS_H__ +#define __FS_OFFSETS_H__ + +#include +#include "FS_versions.h" + +typedef struct { + int opcode_reg; + uint32_t adrp_offset; +} fs_offsets_nintendo_path_t; + +typedef struct { + // Accessor vtable getters + uintptr_t sdmmc_accessor_gc; + uintptr_t sdmmc_accessor_sd; + uintptr_t sdmmc_accessor_nand; + // Hooks + uintptr_t sdmmc_wrapper_read; + uintptr_t sdmmc_wrapper_write; + uintptr_t rtld; + uintptr_t rtld_destination; + uintptr_t clkrst_set_min_v_clock_rate; + // Misc funcs + uintptr_t lock_mutex; + uintptr_t unlock_mutex; + // Misc data + uintptr_t sd_mutex; + uintptr_t nand_mutex; + uintptr_t active_partition; + uintptr_t sdmmc_das_handle; + // NOPs + uintptr_t shutdown_sd; + uintptr_t sd_das_init; + // Nintendo Paths + fs_offsets_nintendo_path_t nintendo_paths[]; +} fs_offsets_t; + +const fs_offsets_t *get_fs_offsets(enum FS_VER version); + +#endif // __FS_OFFSETS_H__ diff --git a/emummc/source/FS/FS_structs.h b/emummc/source/FS/FS_structs.h new file mode 100644 index 000000000..8d50352aa --- /dev/null +++ b/emummc/source/FS/FS_structs.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __FS_STRUCTS_H__ +#define __FS_STRUCTS_H__ + +#include "../utils/types.h" + +typedef struct { + char *device_addr_buffer; + uint64_t device_addr_buffer_size; + char *device_addr_buffer_masked; +} sdmmc_dma_buffer_t; + +_Static_assert(__alignof(sdmmc_dma_buffer_t) == 8, "sdmmc_dma_buffer_t definition"); + +typedef struct sdmmc_accessor_vt { + void *ctor; + void *dtor; + void *map_device_addr_space; + void *unmap_device_addr_space; + void *controller_open; + void *controller_close; + uint64_t (*read_write)(void *, uint64_t, uint64_t, void *, uint64_t, uint64_t); + // More not included because we don't use it. +} sdmmc_accessor_vt_t; + +typedef struct { + void *vtab; + t210_sdmmc_t *io_map; + sdmmc_dma_buffer_t dmaBuffers[3]; + // More not included because we don't use it. +} mmc_obj_t; + +typedef struct { + sdmmc_accessor_vt_t *vtab; + mmc_obj_t *parent; + // More not included because we don't use it. +} sdmmc_accessor_t; + +#endif // __FS_STRUCTS_H__ diff --git a/emummc/source/FS/FS_versions.h b/emummc/source/FS/FS_versions.h new file mode 100644 index 000000000..8dd88827c --- /dev/null +++ b/emummc/source/FS/FS_versions.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __FS_VERSIONS_H__ +#define __FS_VERSIONS_H__ + +// FS Version enum +enum FS_VER +{ + FS_VER_1_0_0 = 0, + + FS_VER_2_0_0, + FS_VER_2_0_0_EXFAT, + + FS_VER_2_1_0, + FS_VER_2_1_0_EXFAT, + + FS_VER_3_0_0, + FS_VER_3_0_0_EXFAT, + + FS_VER_3_0_1, + FS_VER_3_0_1_EXFAT, + + FS_VER_4_0_0, + FS_VER_4_0_0_EXFAT, + + FS_VER_4_1_0, + FS_VER_4_1_0_EXFAT, + + FS_VER_5_0_0, + FS_VER_5_0_0_EXFAT, + + FS_VER_5_1_0, + FS_VER_5_1_0_EXFAT, + + FS_VER_6_0_0, + FS_VER_6_0_0_EXFAT, + + FS_VER_7_0_0, + FS_VER_7_0_0_EXFAT, + + FS_VER_8_0_0, + FS_VER_8_0_0_EXFAT, + + FS_VER_MAX, +}; + +#endif // __FS_VERSIONS_H__ diff --git a/emummc/source/FS/offsets/100.h b/emummc/source/FS/offsets/100.h new file mode 100644 index 000000000..e85a60993 --- /dev/null +++ b/emummc/source/FS/offsets/100.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_100_H__ +#define __FS_100_H__ + +// Accessor vtable getters +#define FS_OFFSET_100_SDMMC_ACCESSOR_GC 0x6F850 +#define FS_OFFSET_100_SDMMC_ACCESSOR_SD 0x6F65C +#define FS_OFFSET_100_SDMMC_ACCESSOR_NAND 0x6F230 + +// Hooks +#define FS_OFFSET_100_SDMMC_WRAPPER_READ 0x6A930 +#define FS_OFFSET_100_SDMMC_WRAPPER_WRITE 0x6A9F0 +#define FS_OFFSET_100_RTLD 0x534 +#define FS_OFFSET_100_RTLD_DESTINATION 0xA0 + +#define FS_OFFSET_100_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_100_LOCK_MUTEX 0x2884 +#define FS_OFFSET_100_UNLOCK_MUTEX 0x28F0 + +// Misc Data +#define FS_OFFSET_100_SD_MUTEX 0xE36058 +#define FS_OFFSET_100_NAND_MUTEX 0xE30610 +#define FS_OFFSET_100_ACTIVE_PARTITION 0xE30650 +#define FS_OFFSET_100_SDMMC_DAS_HANDLE 0xE2F730 + +// NOPs +#define FS_OFFSET_100_SHUTDOWN_SD 0x22548 +#define FS_OFFSET_100_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_100_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 9, .adrp_offset = 0x00032C58}, \ + {.opcode_reg = 8, .adrp_offset = 0x00032C60}, \ + {.opcode_reg = 9, .adrp_offset = 0x00032F3C}, \ + {.opcode_reg = 8, .adrp_offset = 0x00032F44}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_100_H__ diff --git a/emummc/source/FS/offsets/200.h b/emummc/source/FS/offsets/200.h new file mode 100644 index 000000000..91673a606 --- /dev/null +++ b/emummc/source/FS/offsets/200.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_200_H__ +#define __FS_200_H__ + +// Accessor vtable getters +#define FS_OFFSET_200_SDMMC_ACCESSOR_GC 0x78BAC +#define FS_OFFSET_200_SDMMC_ACCESSOR_SD 0x7894C +#define FS_OFFSET_200_SDMMC_ACCESSOR_NAND 0x784BC + +// Hooks +#define FS_OFFSET_200_SDMMC_WRAPPER_READ 0x73478 +#define FS_OFFSET_200_SDMMC_WRAPPER_WRITE 0x73538 +#define FS_OFFSET_200_RTLD 0x500 +#define FS_OFFSET_200_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_200_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_200_LOCK_MUTEX 0x3264 +#define FS_OFFSET_200_UNLOCK_MUTEX 0x32D0 + +// Misc Data +#define FS_OFFSET_200_SD_MUTEX 0xE42268 +#define FS_OFFSET_200_NAND_MUTEX 0xE3CED0 +#define FS_OFFSET_200_ACTIVE_PARTITION 0xE3CF10 +#define FS_OFFSET_200_SDMMC_DAS_HANDLE 0xE3BDD0 + +// NOPs +#define FS_OFFSET_200_SHUTDOWN_SD 0x20C48 +#define FS_OFFSET_200_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_200_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00033F08}, \ + {.opcode_reg = 3, .adrp_offset = 0x00035084}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003537C}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_200_H__ diff --git a/emummc/source/FS/offsets/200_exfat.h b/emummc/source/FS/offsets/200_exfat.h new file mode 100644 index 000000000..95ffe4d33 --- /dev/null +++ b/emummc/source/FS/offsets/200_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_200_EXFAT_H__ +#define __FS_200_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_200_EXFAT_SDMMC_ACCESSOR_GC 0x78BAC +#define FS_OFFSET_200_EXFAT_SDMMC_ACCESSOR_SD 0x7894C +#define FS_OFFSET_200_EXFAT_SDMMC_ACCESSOR_NAND 0x784BC + +// Hooks +#define FS_OFFSET_200_EXFAT_SDMMC_WRAPPER_READ 0x73478 +#define FS_OFFSET_200_EXFAT_SDMMC_WRAPPER_WRITE 0x73538 +#define FS_OFFSET_200_EXFAT_RTLD 0x500 +#define FS_OFFSET_200_EXFAT_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_200_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_200_EXFAT_LOCK_MUTEX 0x3264 +#define FS_OFFSET_200_EXFAT_UNLOCK_MUTEX 0x32D0 + +// Misc Data +#define FS_OFFSET_200_EXFAT_SD_MUTEX 0xF22268 +#define FS_OFFSET_200_EXFAT_NAND_MUTEX 0xF1CED0 +#define FS_OFFSET_200_EXFAT_ACTIVE_PARTITION 0xF1CF10 +#define FS_OFFSET_200_EXFAT_SDMMC_DAS_HANDLE 0xF1BDD0 + +// NOPs +#define FS_OFFSET_200_EXFAT_SHUTDOWN_SD 0x20C48 +#define FS_OFFSET_200_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_200_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00033F08}, \ + {.opcode_reg = 3, .adrp_offset = 0x00035084}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003537C}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_200_EXFAT_H__ diff --git a/emummc/source/FS/offsets/210.h b/emummc/source/FS/offsets/210.h new file mode 100644 index 000000000..9d04fdb4a --- /dev/null +++ b/emummc/source/FS/offsets/210.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_210_H__ +#define __FS_210_H__ + +// Accessor vtable getters +#define FS_OFFSET_210_SDMMC_ACCESSOR_GC 0x78F8C +#define FS_OFFSET_210_SDMMC_ACCESSOR_SD 0x78D2C +#define FS_OFFSET_210_SDMMC_ACCESSOR_NAND 0x7889C + +// Hooks +#define FS_OFFSET_210_SDMMC_WRAPPER_READ 0x73858 +#define FS_OFFSET_210_SDMMC_WRAPPER_WRITE 0x73918 +#define FS_OFFSET_210_RTLD 0x500 +#define FS_OFFSET_210_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_210_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_210_LOCK_MUTEX 0x3264 +#define FS_OFFSET_210_UNLOCK_MUTEX 0x32D0 + +// Misc Data +#define FS_OFFSET_210_SD_MUTEX 0xE43268 +#define FS_OFFSET_210_NAND_MUTEX 0xE3DED0 +#define FS_OFFSET_210_ACTIVE_PARTITION 0xE3DF10 +#define FS_OFFSET_210_SDMMC_DAS_HANDLE 0xE3CDD0 + +// NOPs +#define FS_OFFSET_210_SHUTDOWN_SD 0x20E60 +#define FS_OFFSET_210_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_210_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000342E0}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003545C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00035754}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_210_H__ diff --git a/emummc/source/FS/offsets/210_exfat.h b/emummc/source/FS/offsets/210_exfat.h new file mode 100644 index 000000000..9a6cf8a28 --- /dev/null +++ b/emummc/source/FS/offsets/210_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_210_EXFAT_H__ +#define __FS_210_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_210_EXFAT_SDMMC_ACCESSOR_GC 0x78F8C +#define FS_OFFSET_210_EXFAT_SDMMC_ACCESSOR_SD 0x78D2C +#define FS_OFFSET_210_EXFAT_SDMMC_ACCESSOR_NAND 0x7889C + +// Hooks +#define FS_OFFSET_210_EXFAT_SDMMC_WRAPPER_READ 0x73858 +#define FS_OFFSET_210_EXFAT_SDMMC_WRAPPER_WRITE 0x73918 +#define FS_OFFSET_210_EXFAT_RTLD 0x500 +#define FS_OFFSET_210_EXFAT_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_210_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_210_EXFAT_LOCK_MUTEX 0x3264 +#define FS_OFFSET_210_EXFAT_UNLOCK_MUTEX 0x32D0 + +// Misc Data +#define FS_OFFSET_210_EXFAT_SD_MUTEX 0xF22268 +#define FS_OFFSET_210_EXFAT_NAND_MUTEX 0xF1CED0 +#define FS_OFFSET_210_EXFAT_ACTIVE_PARTITION 0xF1CF10 +#define FS_OFFSET_210_EXFAT_SDMMC_DAS_HANDLE 0xF1BDD0 + +// NOPs +#define FS_OFFSET_210_EXFAT_SHUTDOWN_SD 0x20E60 +#define FS_OFFSET_210_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_210_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000342E0}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003545C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00035754}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_210_EXFAT_H__ diff --git a/emummc/source/FS/offsets/300.h b/emummc/source/FS/offsets/300.h new file mode 100644 index 000000000..462ac20e9 --- /dev/null +++ b/emummc/source/FS/offsets/300.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_300_H__ +#define __FS_300_H__ + +// Accessor vtable getters +#define FS_OFFSET_300_SDMMC_ACCESSOR_GC 0x8FAAC +#define FS_OFFSET_300_SDMMC_ACCESSOR_SD 0x8F84C +#define FS_OFFSET_300_SDMMC_ACCESSOR_NAND 0x8F3B8 + +// Hooks +#define FS_OFFSET_300_SDMMC_WRAPPER_READ 0x8A2F4 +#define FS_OFFSET_300_SDMMC_WRAPPER_WRITE 0x8A3B4 +#define FS_OFFSET_300_RTLD 0x4E8 +#define FS_OFFSET_300_RTLD_DESTINATION 0x8C + +#define FS_OFFSET_300_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_300_LOCK_MUTEX 0x35CC +#define FS_OFFSET_300_UNLOCK_MUTEX 0x3638 + +// Misc Data +#define FS_OFFSET_300_SD_MUTEX 0xE69268 +#define FS_OFFSET_300_NAND_MUTEX 0xE646F0 +#define FS_OFFSET_300_ACTIVE_PARTITION 0xE64730 +#define FS_OFFSET_300_SDMMC_DAS_HANDLE 0xE635A0 + +// NOPs +#define FS_OFFSET_300_SHUTDOWN_SD 0x258D8 +#define FS_OFFSET_300_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_300_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000391F4}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A480}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A778}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_300_H__ diff --git a/emummc/source/FS/offsets/300_exfat.h b/emummc/source/FS/offsets/300_exfat.h new file mode 100644 index 000000000..af46699c5 --- /dev/null +++ b/emummc/source/FS/offsets/300_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_300_EXFAT_H__ +#define __FS_300_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_300_EXFAT_SDMMC_ACCESSOR_GC 0x8FAAC +#define FS_OFFSET_300_EXFAT_SDMMC_ACCESSOR_SD 0x8F84C +#define FS_OFFSET_300_EXFAT_SDMMC_ACCESSOR_NAND 0x8F3B8 + +// Hooks +#define FS_OFFSET_300_EXFAT_SDMMC_WRAPPER_READ 0x8A2F4 +#define FS_OFFSET_300_EXFAT_SDMMC_WRAPPER_WRITE 0x8A3B4 +#define FS_OFFSET_300_EXFAT_RTLD 0x4E8 +#define FS_OFFSET_300_EXFAT_RTLD_DESTINATION 0x8C + +#define FS_OFFSET_300_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_300_EXFAT_LOCK_MUTEX 0x35CC +#define FS_OFFSET_300_EXFAT_UNLOCK_MUTEX 0x3638 + +// Misc Data +#define FS_OFFSET_300_EXFAT_SD_MUTEX 0xF4C268 +#define FS_OFFSET_300_EXFAT_NAND_MUTEX 0xF476F0 +#define FS_OFFSET_300_EXFAT_ACTIVE_PARTITION 0xF47730 +#define FS_OFFSET_300_EXFAT_SDMMC_DAS_HANDLE 0xF465A0 + +// NOPs +#define FS_OFFSET_300_EXFAT_SHUTDOWN_SD 0x258D8 +#define FS_OFFSET_300_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_300_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000391F4}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A480}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A778}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_300_EXFAT_H__ diff --git a/emummc/source/FS/offsets/301.h b/emummc/source/FS/offsets/301.h new file mode 100644 index 000000000..40c62e2a4 --- /dev/null +++ b/emummc/source/FS/offsets/301.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_301_H__ +#define __FS_301_H__ + +// Accessor vtable getters +#define FS_OFFSET_301_SDMMC_ACCESSOR_GC 0x8FB68 +#define FS_OFFSET_301_SDMMC_ACCESSOR_SD 0x8F908 +#define FS_OFFSET_301_SDMMC_ACCESSOR_NAND 0x8F474 + +// Hooks +#define FS_OFFSET_301_SDMMC_WRAPPER_READ 0x8A3B0 +#define FS_OFFSET_301_SDMMC_WRAPPER_WRITE 0x8A470 +#define FS_OFFSET_301_RTLD 0x4E8 +#define FS_OFFSET_301_RTLD_DESTINATION 0x8C + +#define FS_OFFSET_301_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_301_LOCK_MUTEX 0x3638 +#define FS_OFFSET_301_UNLOCK_MUTEX 0x36A4 + +// Misc Data +#define FS_OFFSET_301_SD_MUTEX 0xE69268 +#define FS_OFFSET_301_NAND_MUTEX 0xE646F0 +#define FS_OFFSET_301_ACTIVE_PARTITION 0xE64730 +#define FS_OFFSET_301_SDMMC_DAS_HANDLE 0xE635A0 + +// NOPs +#define FS_OFFSET_301_SHUTDOWN_SD 0x25944 +#define FS_OFFSET_301_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_301_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00039260}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A4EC}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A7E4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_301_H__ diff --git a/emummc/source/FS/offsets/301_exfat.h b/emummc/source/FS/offsets/301_exfat.h new file mode 100644 index 000000000..64aeb9dae --- /dev/null +++ b/emummc/source/FS/offsets/301_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_301_EXFAT_H__ +#define __FS_301_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_301_EXFAT_SDMMC_ACCESSOR_GC 0x8FB68 +#define FS_OFFSET_301_EXFAT_SDMMC_ACCESSOR_SD 0x8F908 +#define FS_OFFSET_301_EXFAT_SDMMC_ACCESSOR_NAND 0x8F474 + +// Hooks +#define FS_OFFSET_301_EXFAT_SDMMC_WRAPPER_READ 0x8A3B0 +#define FS_OFFSET_301_EXFAT_SDMMC_WRAPPER_WRITE 0x8A470 +#define FS_OFFSET_301_EXFAT_RTLD 0x4E8 +#define FS_OFFSET_301_EXFAT_RTLD_DESTINATION 0x8C + +#define FS_OFFSET_301_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_301_EXFAT_LOCK_MUTEX 0x3638 +#define FS_OFFSET_301_EXFAT_UNLOCK_MUTEX 0x36A4 + +// Misc Data +#define FS_OFFSET_301_EXFAT_SD_MUTEX 0xF4C268 +#define FS_OFFSET_301_EXFAT_NAND_MUTEX 0xF476F0 +#define FS_OFFSET_301_EXFAT_ACTIVE_PARTITION 0xF47730 +#define FS_OFFSET_301_EXFAT_SDMMC_DAS_HANDLE 0xF465A0 + +// NOPs +#define FS_OFFSET_301_EXFAT_SHUTDOWN_SD 0x25944 +#define FS_OFFSET_301_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_301_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00039260}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A4EC}, \ + {.opcode_reg = 3, .adrp_offset = 0x0003A7E4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_301_EXFAT_H__ diff --git a/emummc/source/FS/offsets/400.h b/emummc/source/FS/offsets/400.h new file mode 100644 index 000000000..22cce3d46 --- /dev/null +++ b/emummc/source/FS/offsets/400.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_400_H__ +#define __FS_400_H__ + +// Accessor vtable getters +#define FS_OFFSET_400_SDMMC_ACCESSOR_GC 0xA3374 +#define FS_OFFSET_400_SDMMC_ACCESSOR_SD 0xA3114 +#define FS_OFFSET_400_SDMMC_ACCESSOR_NAND 0xA2C80 + +// Hooks +#define FS_OFFSET_400_SDMMC_WRAPPER_READ 0x9DBCC +#define FS_OFFSET_400_SDMMC_WRAPPER_WRITE 0x9DC8C +#define FS_OFFSET_400_RTLD 0x4DC +#define FS_OFFSET_400_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_400_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_400_LOCK_MUTEX 0x39A0 +#define FS_OFFSET_400_UNLOCK_MUTEX 0x3A0C + +// Misc Data +#define FS_OFFSET_400_SD_MUTEX 0xE80268 +#define FS_OFFSET_400_NAND_MUTEX 0xE7BC60 +#define FS_OFFSET_400_ACTIVE_PARTITION 0xE7BCA0 +#define FS_OFFSET_400_SDMMC_DAS_HANDLE 0xE7ABF0 + +// NOPs +#define FS_OFFSET_400_SHUTDOWN_SD 0x32D70 +#define FS_OFFSET_400_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_400_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0002023C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021BE8}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021EC4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_400_H__ diff --git a/emummc/source/FS/offsets/400_exfat.h b/emummc/source/FS/offsets/400_exfat.h new file mode 100644 index 000000000..0efc7306d --- /dev/null +++ b/emummc/source/FS/offsets/400_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_400_EXFAT_H__ +#define __FS_400_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_400_EXFAT_SDMMC_ACCESSOR_GC 0xA3374 +#define FS_OFFSET_400_EXFAT_SDMMC_ACCESSOR_SD 0xA3114 +#define FS_OFFSET_400_EXFAT_SDMMC_ACCESSOR_NAND 0xA2C80 + +// Hooks +#define FS_OFFSET_400_EXFAT_SDMMC_WRAPPER_READ 0x9DBCC +#define FS_OFFSET_400_EXFAT_SDMMC_WRAPPER_WRITE 0x9DC8C +#define FS_OFFSET_400_EXFAT_RTLD 0x4DC +#define FS_OFFSET_400_EXFAT_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_400_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_400_EXFAT_LOCK_MUTEX 0x39A0 +#define FS_OFFSET_400_EXFAT_UNLOCK_MUTEX 0x3A0C + +// Misc Data +#define FS_OFFSET_400_EXFAT_SD_MUTEX 0xF63268 +#define FS_OFFSET_400_EXFAT_NAND_MUTEX 0xF5EC60 +#define FS_OFFSET_400_EXFAT_ACTIVE_PARTITION 0xF5ECA0 +#define FS_OFFSET_400_EXFAT_SDMMC_DAS_HANDLE 0xF5DBF0 + +// NOPs +#define FS_OFFSET_400_EXFAT_SHUTDOWN_SD 0x32D70 +#define FS_OFFSET_400_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_400_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0002023C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021BE8}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021EC4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_400_EXFAT_H__ diff --git a/emummc/source/FS/offsets/410.h b/emummc/source/FS/offsets/410.h new file mode 100644 index 000000000..dcb0c7550 --- /dev/null +++ b/emummc/source/FS/offsets/410.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_410_H__ +#define __FS_410_H__ + +// Accessor vtable getters +#define FS_OFFSET_410_SDMMC_ACCESSOR_GC 0xA33D8 +#define FS_OFFSET_410_SDMMC_ACCESSOR_SD 0xA3178 +#define FS_OFFSET_410_SDMMC_ACCESSOR_NAND 0xA2CE4 + +// Hooks +#define FS_OFFSET_410_SDMMC_WRAPPER_READ 0x9DC30 +#define FS_OFFSET_410_SDMMC_WRAPPER_WRITE 0x9DCF0 +#define FS_OFFSET_410_RTLD 0x4DC +#define FS_OFFSET_410_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_410_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_410_LOCK_MUTEX 0x39A0 +#define FS_OFFSET_410_UNLOCK_MUTEX 0x3A0C + +// Misc Data +#define FS_OFFSET_410_SD_MUTEX 0xE80268 +#define FS_OFFSET_410_NAND_MUTEX 0xE7BC60 +#define FS_OFFSET_410_ACTIVE_PARTITION 0xE7BCA0 +#define FS_OFFSET_410_SDMMC_DAS_HANDLE 0xE7ABF0 + +// NOPs +#define FS_OFFSET_410_SHUTDOWN_SD 0x32D70 +#define FS_OFFSET_410_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_410_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0002023C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021BE8}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021EC4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_410_H__ diff --git a/emummc/source/FS/offsets/410_exfat.h b/emummc/source/FS/offsets/410_exfat.h new file mode 100644 index 000000000..78268a518 --- /dev/null +++ b/emummc/source/FS/offsets/410_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_410_EXFAT_H__ +#define __FS_410_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_410_EXFAT_SDMMC_ACCESSOR_GC 0xA33D8 +#define FS_OFFSET_410_EXFAT_SDMMC_ACCESSOR_SD 0xA3178 +#define FS_OFFSET_410_EXFAT_SDMMC_ACCESSOR_NAND 0xA2CE4 + +// Hooks +#define FS_OFFSET_410_EXFAT_SDMMC_WRAPPER_READ 0x9DC30 +#define FS_OFFSET_410_EXFAT_SDMMC_WRAPPER_WRITE 0x9DCF0 +#define FS_OFFSET_410_EXFAT_RTLD 0x4DC +#define FS_OFFSET_410_EXFAT_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_410_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_410_EXFAT_LOCK_MUTEX 0x39A0 +#define FS_OFFSET_410_EXFAT_UNLOCK_MUTEX 0x3A0C + +// Misc Data +#define FS_OFFSET_410_EXFAT_SD_MUTEX 0xF63268 +#define FS_OFFSET_410_EXFAT_NAND_MUTEX 0xF5EC60 +#define FS_OFFSET_410_EXFAT_ACTIVE_PARTITION 0xF5ECA0 +#define FS_OFFSET_410_EXFAT_SDMMC_DAS_HANDLE 0xF5DBF0 + +// NOPs +#define FS_OFFSET_410_EXFAT_SHUTDOWN_SD 0x32D70 +#define FS_OFFSET_410_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_410_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0002023C}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021BE8}, \ + {.opcode_reg = 3, .adrp_offset = 0x00021EC4}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_410_EXFAT_H__ diff --git a/emummc/source/FS/offsets/500.h b/emummc/source/FS/offsets/500.h new file mode 100644 index 000000000..8e10f2a3e --- /dev/null +++ b/emummc/source/FS/offsets/500.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_500_H__ +#define __FS_500_H__ + +// Accessor vtable getters +#define FS_OFFSET_500_SDMMC_ACCESSOR_GC 0xCF250 +#define FS_OFFSET_500_SDMMC_ACCESSOR_SD 0xCEFD0 +#define FS_OFFSET_500_SDMMC_ACCESSOR_NAND 0xCE990 + +// Hooks +#define FS_OFFSET_500_SDMMC_WRAPPER_READ 0xC9420 +#define FS_OFFSET_500_SDMMC_WRAPPER_WRITE 0xC9500 +#define FS_OFFSET_500_RTLD 0x584 +#define FS_OFFSET_500_RTLD_DESTINATION 0x94 + +#define FS_OFFSET_500_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_500_LOCK_MUTEX 0x4080 +#define FS_OFFSET_500_UNLOCK_MUTEX 0x40D0 + +// Misc Data +#define FS_OFFSET_500_SD_MUTEX 0xEC3268 +#define FS_OFFSET_500_NAND_MUTEX 0xEBDE58 +#define FS_OFFSET_500_ACTIVE_PARTITION 0xEBDE98 +#define FS_OFFSET_500_SDMMC_DAS_HANDLE 0xEBCE30 + +// NOPs +#define FS_OFFSET_500_SHUTDOWN_SD 0x443E8 +#define FS_OFFSET_500_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_500_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00028980}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002ACE4}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002B220}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_500_H__ diff --git a/emummc/source/FS/offsets/500_exfat.h b/emummc/source/FS/offsets/500_exfat.h new file mode 100644 index 000000000..9b6bc407e --- /dev/null +++ b/emummc/source/FS/offsets/500_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_500_EXFAT_H__ +#define __FS_500_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_500_EXFAT_SDMMC_ACCESSOR_GC 0xCF250 +#define FS_OFFSET_500_EXFAT_SDMMC_ACCESSOR_SD 0xCEFD0 +#define FS_OFFSET_500_EXFAT_SDMMC_ACCESSOR_NAND 0xCE990 + +// Hooks +#define FS_OFFSET_500_EXFAT_SDMMC_WRAPPER_READ 0xC9420 +#define FS_OFFSET_500_EXFAT_SDMMC_WRAPPER_WRITE 0xC9500 +#define FS_OFFSET_500_EXFAT_RTLD 0x584 +#define FS_OFFSET_500_EXFAT_RTLD_DESTINATION 0x94 + +#define FS_OFFSET_500_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_500_EXFAT_LOCK_MUTEX 0x4080 +#define FS_OFFSET_500_EXFAT_UNLOCK_MUTEX 0x40D0 + +// Misc Data +#define FS_OFFSET_500_EXFAT_SD_MUTEX 0xFA8268 +#define FS_OFFSET_500_EXFAT_NAND_MUTEX 0xFA2E58 +#define FS_OFFSET_500_EXFAT_ACTIVE_PARTITION 0xFA2E98 +#define FS_OFFSET_500_EXFAT_SDMMC_DAS_HANDLE 0xFA1E30 + +// NOPs +#define FS_OFFSET_500_EXFAT_SHUTDOWN_SD 0x443E8 +#define FS_OFFSET_500_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_500_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00028980}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002ACE4}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002B220}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_500_EXFAT_H__ diff --git a/emummc/source/FS/offsets/510.h b/emummc/source/FS/offsets/510.h new file mode 100644 index 000000000..2724e4ceb --- /dev/null +++ b/emummc/source/FS/offsets/510.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_510_H__ +#define __FS_510_H__ + +// Accessor vtable getters +#define FS_OFFSET_510_SDMMC_ACCESSOR_GC 0xCF620 +#define FS_OFFSET_510_SDMMC_ACCESSOR_SD 0xCF3A0 +#define FS_OFFSET_510_SDMMC_ACCESSOR_NAND 0xCED60 + +// Hooks +#define FS_OFFSET_510_SDMMC_WRAPPER_READ 0xC97F0 +#define FS_OFFSET_510_SDMMC_WRAPPER_WRITE 0xC98D0 +#define FS_OFFSET_510_RTLD 0x584 +#define FS_OFFSET_510_RTLD_DESTINATION 0x94 + +#define FS_OFFSET_510_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_510_LOCK_MUTEX 0x4080 +#define FS_OFFSET_510_UNLOCK_MUTEX 0x40D0 + +// Misc Data +#define FS_OFFSET_510_SD_MUTEX 0xEC4268 +#define FS_OFFSET_510_NAND_MUTEX 0xEBEE58 +#define FS_OFFSET_510_ACTIVE_PARTITION 0xEBEE98 +#define FS_OFFSET_510_SDMMC_DAS_HANDLE 0xEBDE30 + +// NOPs +#define FS_OFFSET_510_SHUTDOWN_SD 0x44578 +#define FS_OFFSET_510_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_510_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000289B0}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002AD14}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002B250}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_510_H__ diff --git a/emummc/source/FS/offsets/510_exfat.h b/emummc/source/FS/offsets/510_exfat.h new file mode 100644 index 000000000..645fd8607 --- /dev/null +++ b/emummc/source/FS/offsets/510_exfat.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_510_EXFAT_H__ +#define __FS_510_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_510_EXFAT_SDMMC_ACCESSOR_GC 0xCF620 +#define FS_OFFSET_510_EXFAT_SDMMC_ACCESSOR_SD 0xCF3A0 +#define FS_OFFSET_510_EXFAT_SDMMC_ACCESSOR_NAND 0xCED60 + +// Hooks +#define FS_OFFSET_510_EXFAT_SDMMC_WRAPPER_READ 0xC97F0 +#define FS_OFFSET_510_EXFAT_SDMMC_WRAPPER_WRITE 0xC98D0 +#define FS_OFFSET_510_EXFAT_RTLD 0x584 +#define FS_OFFSET_510_EXFAT_RTLD_DESTINATION 0x94 + +#define FS_OFFSET_510_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_510_EXFAT_LOCK_MUTEX 0x4080 +#define FS_OFFSET_510_EXFAT_UNLOCK_MUTEX 0x40D0 + +// Misc Data +#define FS_OFFSET_510_EXFAT_SD_MUTEX 0xFA9268 +#define FS_OFFSET_510_EXFAT_NAND_MUTEX 0xFA3E58 +#define FS_OFFSET_510_EXFAT_ACTIVE_PARTITION 0xFA3E98 +#define FS_OFFSET_510_EXFAT_SDMMC_DAS_HANDLE 0xFA2E30 + +// NOPs +#define FS_OFFSET_510_EXFAT_SHUTDOWN_SD 0x44578 +#define FS_OFFSET_510_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_510_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000289B0}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002AD14}, \ + {.opcode_reg = 3, .adrp_offset = 0x0002B250}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_510_EXFAT_H__ diff --git a/emummc/source/FS/offsets/600.h b/emummc/source/FS/offsets/600.h new file mode 100644 index 000000000..6bebc56f3 --- /dev/null +++ b/emummc/source/FS/offsets/600.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_600_H__ +#define __FS_600_H__ + +// Accessor vtable getters +#define FS_OFFSET_600_SDMMC_ACCESSOR_GC 0x153780 +#define FS_OFFSET_600_SDMMC_ACCESSOR_SD 0x1534F0 +#define FS_OFFSET_600_SDMMC_ACCESSOR_NAND 0x14F990 + +// Hooks +#define FS_OFFSET_600_SDMMC_WRAPPER_READ 0x1485A0 +#define FS_OFFSET_600_SDMMC_WRAPPER_WRITE 0x148680 +#define FS_OFFSET_600_RTLD 0x5B0 +#define FS_OFFSET_600_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_600_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_600_LOCK_MUTEX 0x1412C0 +#define FS_OFFSET_600_UNLOCK_MUTEX 0x141310 + +// Misc Data +#define FS_OFFSET_600_SD_MUTEX 0xF06268 +#define FS_OFFSET_600_NAND_MUTEX 0xF01BA0 +#define FS_OFFSET_600_ACTIVE_PARTITION 0xF01BE0 +#define FS_OFFSET_600_SDMMC_DAS_HANDLE 0xE01670 + +// NOPs +#define FS_OFFSET_600_SHUTDOWN_SD 0xB2F28 +#define FS_OFFSET_600_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_600_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000790DC}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007A924}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AB18}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007AEF4}, \ + {.opcode_reg = 0, .adrp_offset = 0} \ +} + +#endif // __FS_600_H__ diff --git a/emummc/source/FS/offsets/600_exfat.h b/emummc/source/FS/offsets/600_exfat.h new file mode 100644 index 000000000..fb0773231 --- /dev/null +++ b/emummc/source/FS/offsets/600_exfat.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_600_EXFAT_H__ +#define __FS_600_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_600_EXFAT_SDMMC_ACCESSOR_GC 0x15EE80 +#define FS_OFFSET_600_EXFAT_SDMMC_ACCESSOR_SD 0x15EBF0 +#define FS_OFFSET_600_EXFAT_SDMMC_ACCESSOR_NAND 0x15B090 + +// Hooks +#define FS_OFFSET_600_EXFAT_SDMMC_WRAPPER_READ 0x153CA0 +#define FS_OFFSET_600_EXFAT_SDMMC_WRAPPER_WRITE 0x153D80 +#define FS_OFFSET_600_EXFAT_RTLD 0x5B0 +#define FS_OFFSET_600_EXFAT_RTLD_DESTINATION 0x98 + +#define FS_OFFSET_600_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_600_EXFAT_LOCK_MUTEX 0x14C9C0 +#define FS_OFFSET_600_EXFAT_UNLOCK_MUTEX 0x14CA10 + +// Misc Data +#define FS_OFFSET_600_EXFAT_SD_MUTEX 0xFEB268 +#define FS_OFFSET_600_EXFAT_NAND_MUTEX 0xFE6BA0 +#define FS_OFFSET_600_EXFAT_ACTIVE_PARTITION 0xFE6BE0 +#define FS_OFFSET_600_EXFAT_SDMMC_DAS_HANDLE 0xEE6670 + +// NOPs +#define FS_OFFSET_600_EXFAT_SHUTDOWN_SD 0xBE628 +#define FS_OFFSET_600_EXFAT_SD_DAS_INIT 0x0 + +// Nintendo Paths +#define FS_OFFSET_600_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x000847DC}, \ + {.opcode_reg = 3, .adrp_offset = 0x00086024}, \ + {.opcode_reg = 3, .adrp_offset = 0x00086218}, \ + {.opcode_reg = 3, .adrp_offset = 0x000865F4}, \ + {.opcode_reg = 0, .adrp_offset = 0} \ +} + +#endif // __FS_600_EXFAT_H__ diff --git a/emummc/source/FS/offsets/700.h b/emummc/source/FS/offsets/700.h new file mode 100644 index 000000000..c0a624308 --- /dev/null +++ b/emummc/source/FS/offsets/700.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_700_H__ +#define __FS_700_H__ + +// Accessor vtable getters +#define FS_OFFSET_700_SDMMC_ACCESSOR_GC 0x15BD90 +#define FS_OFFSET_700_SDMMC_ACCESSOR_SD 0x15BB00 +#define FS_OFFSET_700_SDMMC_ACCESSOR_NAND 0x157FF0 + +// Hooks +#define FS_OFFSET_700_SDMMC_WRAPPER_READ 0x14FDF0 +#define FS_OFFSET_700_SDMMC_WRAPPER_WRITE 0x14FED0 +#define FS_OFFSET_700_RTLD 0x5B4 +#define FS_OFFSET_700_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_700_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_700_LOCK_MUTEX 0x148A90 +#define FS_OFFSET_700_UNLOCK_MUTEX 0x148AE0 + +// Misc Data +#define FS_OFFSET_700_SD_MUTEX 0xF123E8 +#define FS_OFFSET_700_NAND_MUTEX 0xF0DBE8 +#define FS_OFFSET_700_ACTIVE_PARTITION 0xF0DC28 +#define FS_OFFSET_700_SDMMC_DAS_HANDLE 0xE0E7A0 + +// NOPs +#define FS_OFFSET_700_SHUTDOWN_SD 0xB8FCC +#define FS_OFFSET_700_SD_DAS_INIT 0x85FE8 + +// Nintendo Paths +#define FS_OFFSET_700_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0007DA90}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007F344}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007F538}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007F914}, \ + {.opcode_reg = 4, .adrp_offset = 0x0007FAD8}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_700_H__ diff --git a/emummc/source/FS/offsets/700_exfat.h b/emummc/source/FS/offsets/700_exfat.h new file mode 100644 index 000000000..1293a200c --- /dev/null +++ b/emummc/source/FS/offsets/700_exfat.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_700_EXFAT_H__ +#define __FS_700_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_700_EXFAT_SDMMC_ACCESSOR_GC 0x167340 +#define FS_OFFSET_700_EXFAT_SDMMC_ACCESSOR_SD 0x1670B0 +#define FS_OFFSET_700_EXFAT_SDMMC_ACCESSOR_NAND 0x1635A0 + +// Hooks +#define FS_OFFSET_700_EXFAT_SDMMC_WRAPPER_READ 0x15B3A0 +#define FS_OFFSET_700_EXFAT_SDMMC_WRAPPER_WRITE 0x15B480 +#define FS_OFFSET_700_EXFAT_RTLD 0x5B4 +#define FS_OFFSET_700_EXFAT_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_700_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x0 + +// Misc funcs +#define FS_OFFSET_700_EXFAT_LOCK_MUTEX 0x154040 +#define FS_OFFSET_700_EXFAT_UNLOCK_MUTEX 0x154090 + +// Misc Data +#define FS_OFFSET_700_EXFAT_SD_MUTEX 0xFF73E8 +#define FS_OFFSET_700_EXFAT_NAND_MUTEX 0xFF2BE8 +#define FS_OFFSET_700_EXFAT_ACTIVE_PARTITION 0xFF2C28 +#define FS_OFFSET_700_EXFAT_SDMMC_DAS_HANDLE 0xEF3A00 + +// NOPs +#define FS_OFFSET_700_EXFAT_SHUTDOWN_SD 0xC457C +#define FS_OFFSET_700_EXFAT_SD_DAS_INIT 0x91598 + +// Nintendo Paths +#define FS_OFFSET_700_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x00089040}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008A8F4}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008AAE8}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008AEC4}, \ + {.opcode_reg = 4, .adrp_offset = 0x0008B088}, \ + {.opcode_reg = 0, .adrp_offset = 0}, \ +} + +#endif // __FS_700_EXFAT_H__ diff --git a/emummc/source/FS/offsets/800.h b/emummc/source/FS/offsets/800.h new file mode 100644 index 000000000..f03aa8042 --- /dev/null +++ b/emummc/source/FS/offsets/800.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_800_H__ +#define __FS_800_H__ + +// Accessor vtable getters +#define FS_OFFSET_800_SDMMC_ACCESSOR_GC 0x15EA20 +#define FS_OFFSET_800_SDMMC_ACCESSOR_SD 0x15E790 +#define FS_OFFSET_800_SDMMC_ACCESSOR_NAND 0x15AC80 + +// Hooks +#define FS_OFFSET_800_SDMMC_WRAPPER_READ 0x152A80 +#define FS_OFFSET_800_SDMMC_WRAPPER_WRITE 0x152B60 +#define FS_OFFSET_800_RTLD 0x5B4 +#define FS_OFFSET_800_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_800_CLKRST_SET_MIN_V_CLK_RATE 0x16F370 + +// Misc funcs +#define FS_OFFSET_800_LOCK_MUTEX 0x14B6D0 +#define FS_OFFSET_800_UNLOCK_MUTEX 0x14B720 + +// Misc Data +#define FS_OFFSET_800_SD_MUTEX 0xF1A3E8 +#define FS_OFFSET_800_NAND_MUTEX 0xF15BE8 +#define FS_OFFSET_800_ACTIVE_PARTITION 0xF15C28 +#define FS_OFFSET_800_SDMMC_DAS_HANDLE 0xE167C0 + +// NOPs +#define FS_OFFSET_800_SHUTDOWN_SD 0xBAF6C +#define FS_OFFSET_800_SD_DAS_INIT 0x87D58 + +// Nintendo Paths +#define FS_OFFSET_800_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0007F5F0}, \ + {.opcode_reg = 3, .adrp_offset = 0x00081084}, \ + {.opcode_reg = 3, .adrp_offset = 0x00081278}, \ + {.opcode_reg = 3, .adrp_offset = 0x00081654}, \ + {.opcode_reg = 4, .adrp_offset = 0x00081818}, \ + {.opcode_reg = 0, .adrp_offset = 0} \ +} + +#endif // __FS_800_H__ diff --git a/emummc/source/FS/offsets/800_exfat.h b/emummc/source/FS/offsets/800_exfat.h new file mode 100644 index 000000000..cfc317c29 --- /dev/null +++ b/emummc/source/FS/offsets/800_exfat.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#ifndef __FS_800_EXFAT_H__ +#define __FS_800_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_800_EXFAT_SDMMC_ACCESSOR_GC 0x169FD0 +#define FS_OFFSET_800_EXFAT_SDMMC_ACCESSOR_SD 0x169D40 +#define FS_OFFSET_800_EXFAT_SDMMC_ACCESSOR_NAND 0x166230 + +// Hooks +#define FS_OFFSET_800_EXFAT_SDMMC_WRAPPER_READ 0x15E030 +#define FS_OFFSET_800_EXFAT_SDMMC_WRAPPER_WRITE 0x15E110 +#define FS_OFFSET_800_EXFAT_RTLD 0x5B4 +#define FS_OFFSET_800_EXFAT_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_800_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x17A920 + +// Misc funcs +#define FS_OFFSET_800_EXFAT_LOCK_MUTEX 0x156C80 +#define FS_OFFSET_800_EXFAT_UNLOCK_MUTEX 0x156CD0 + +// Misc Data +#define FS_OFFSET_800_EXFAT_SD_MUTEX 0xFFE3E8 +#define FS_OFFSET_800_EXFAT_NAND_MUTEX 0xFF9BE8 +#define FS_OFFSET_800_EXFAT_ACTIVE_PARTITION 0xFF9C28 +#define FS_OFFSET_800_EXFAT_SDMMC_DAS_HANDLE 0xEFAA20 + +// NOPs +#define FS_OFFSET_800_EXFAT_SHUTDOWN_SD 0xC651C +#define FS_OFFSET_800_EXFAT_SD_DAS_INIT 0x93308 + +// Nintendo Paths +#define FS_OFFSET_800_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0008ABA0}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008C634}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008C828}, \ + {.opcode_reg = 3, .adrp_offset = 0x0008CC04}, \ + {.opcode_reg = 4, .adrp_offset = 0x0008CDC8}, \ + {.opcode_reg = 0, .adrp_offset = 0} \ +} + +#endif // __FS_800_EXFAT_H__ diff --git a/emummc/source/emmc/mmc.h b/emummc/source/emmc/mmc.h new file mode 100644 index 000000000..dddb956e9 --- /dev/null +++ b/emummc/source/emmc/mmc.h @@ -0,0 +1,432 @@ +/* + * Header for MultiMediaCard (MMC) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Based strongly on code by: + * + * Author: Yong-iL Joh + * + * Author: Andrew Christian + * 15 May 2002 + */ + +#ifndef LINUX_MMC_MMC_H +#define LINUX_MMC_MMC_H + +/* Standard MMC commands (4.1) type argument response */ +/* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_BUS_TEST_R 14 /* adtc R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ +#define MMC_BUS_TEST_W 19 /* adtc R1 */ +#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */ +#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */ + +/* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ + +/* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + +/* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + +/* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + +/* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 38 /* ac R1b */ + +/* class 9 */ +#define MMC_FAST_IO 39 /* ac R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + +/* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + +/* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ + +/* class 11 */ +#define MMC_QUE_TASK_PARAMS 44 /* ac [20:16] task id R1 */ +#define MMC_QUE_TASK_ADDR 45 /* ac [31:0] data addr R1 */ +#define MMC_EXECUTE_READ_TASK 46 /* adtc [20:16] task id R1 */ +#define MMC_EXECUTE_WRITE_TASK 47 /* adtc [20:16] task id R1 */ +#define MMC_CMDQ_TASK_MGMT 48 /* ac [20:16] task id R1b */ + +/* +* MMC_SWITCH argument format: +* +* [31:26] Always 0 +* [25:24] Access Mode +* [23:16] Location of target Byte in EXT_CSD +* [15:08] Value Byte +* [07:03] Always 0 +* [02:00] Command Set +*/ + +/* +MMC status in R1, for native mode (SPI bits are different) +Type +e : error bit +s : status bit +r : detected and set for the actual command response +x : detected and set during command execution. the host must poll +the card by sending status command in order to read these bits. +Clear condition +a : according to the card state +b : always related to the previous command. Reception of +a valid command will clear it (with a delay of one command) +c : clear by read +*/ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ +#define R1_EXCEPTION_EVENT (1 << 6) /* sr, a */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +/* +* MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS +* R1 is the low order byte; R2 is the next highest byte, when present. +*/ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero */ +#define R2_SPI_CARD_LOCKED (1 << 8) +#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 10) +#define R2_SPI_CC_ERROR (1 << 11) +#define R2_SPI_CARD_ECC_ERROR (1 << 12) +#define R2_SPI_WP_VIOLATION (1 << 13) +#define R2_SPI_ERASE_PARAM (1 << 14) +#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +/* +* OCR bits are mostly in host.h +*/ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +/* +* Card Command Classes (CCC) +*/ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ +/* (CMD0,1,2,3,4,7,9,10,12,13,15) */ +/* (and for SPI, CMD58,59) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ +/* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ +/* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ +/* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ +/* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ +/* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ +/* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ +/* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ +/* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ +/* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ +/* (CMD6,34,35,36,37,50) */ +/* (11) Reserved */ +/* (CMD?) */ + +/* +* CSD field definitions +*/ + +#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ +#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ + +#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ +#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ + +/* +* EXT_CSD fields +*/ + +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ +#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ +#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ +#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ +#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ +#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_HPI_MGMT 161 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_BKOPS_START 164 /* W */ +#define EXT_CSD_SANITIZE_START 165 /* W */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_BOOT_WP 173 /* R/W */ +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */ +#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */ +#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 /* RO */ +#define EXT_CSD_REL_WR_SEC_C 222 /* RO */ +#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */ +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ +#define EXT_CSD_BOOT_MULT 226 /* RO */ +#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ +#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ +#define EXT_CSD_TRIM_MULT 232 /* RO */ +#define EXT_CSD_PWR_CL_200_195 236 /* RO */ +#define EXT_CSD_PWR_CL_200_360 237 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */ +#define EXT_CSD_BKOPS_STATUS 246 /* RO */ +#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ +#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ +#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */ +#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */ +#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */ +#define EXT_CSD_DEVICE_VERSION 262 /* RO, 2 bytes */ +#define EXT_CSD_PRE_EOL_INFO 267 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A 268 /* RO */ +#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B 269 /* RO */ +#define EXT_CSD_CMDQ_DEPTH 307 /* RO */ +#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */ +#define EXT_CSD_SUPPORTED_MODE 493 /* RO */ +#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ +#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ +#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ +#define EXT_CSD_MAX_PACKED_READS 501 /* RO */ +#define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ +#define EXT_CSD_HPI_FEATURES 503 /* RO */ + +/* +* EXT_CSD field definitions +*/ + +#define EXT_CSD_WR_REL_PARAM_EN (1<<2) + +#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) +#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) +#define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) +#define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) + +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3) +#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) + +#define EXT_CSD_PART_SETTING_COMPLETED (0x1) +#define EXT_CSD_PART_SUPPORT_PART_EN (0x1) + +#define EXT_CSD_CMD_SET_NORMAL (1<<0) +#define EXT_CSD_CMD_SET_SECURE (1<<1) +#define EXT_CSD_CMD_SET_CPSECURE (1<<2) + +#define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */ +#define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */ +#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \ + EXT_CSD_CARD_TYPE_HS_52) +#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */ +/* DDR mode @1.8V or 3V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */ +/* DDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ + | EXT_CSD_CARD_TYPE_DDR_1_2V) +#define EXT_CSD_CARD_TYPE_HS200_1_8V (1<<4) /* Card can run at 200MHz */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V (1<<5) /* Card can run at 200MHz */ +/* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) +#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */ +#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */ +#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \ + EXT_CSD_CARD_TYPE_HS400_1_2V) +#define EXT_CSD_CARD_TYPE_HS400ES (1<<8) /* Card can run at HS400ES */ + +#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ +#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ +#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ +#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_BUS_WIDTH_STROBE (1<<7) /* Enhanced strobe mode */ + +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ +#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */ + +#define EXT_CSD_SEC_ER_EN (1<<0) +#define EXT_CSD_SEC_BD_BLK_EN (1<<2) +#define EXT_CSD_SEC_GB_CL_EN (1<<4) +#define EXT_CSD_SEC_SANITIZE (1<<6) /* v4.5 only */ + +#define EXT_CSD_RST_N_EN_MASK 0x3 +#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */ + +#define EXT_CSD_NO_POWER_NOTIFICATION 0 +#define EXT_CSD_POWER_ON 1 +#define EXT_CSD_POWER_OFF_SHORT 2 +#define EXT_CSD_POWER_OFF_LONG 3 + +#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 +#define EXT_CSD_PWR_CL_4BIT_SHIFT 0 + +#define EXT_CSD_PACKED_EVENT_EN (1<<3) + +/* +* EXCEPTION_EVENT_STATUS field +*/ +#define EXT_CSD_URGENT_BKOPS (1<<0) +#define EXT_CSD_DYNCAP_NEEDED (1<<1) +#define EXT_CSD_SYSPOOL_EXHAUSTED (1<<2) +#define EXT_CSD_PACKED_FAILURE (1<<3) + +#define EXT_CSD_PACKED_GENERIC_ERROR (1<<0) +#define EXT_CSD_PACKED_INDEXED_ERROR (1<<1) + +/* +* BKOPS status level +*/ +#define EXT_CSD_BKOPS_LEVEL_2 0x2 + +/* +* BKOPS modes +*/ +#define EXT_CSD_MANUAL_BKOPS_MASK 0x01 +#define EXT_CSD_AUTO_BKOPS_MASK 0x02 + +/* +* Command Queue +*/ +#define EXT_CSD_CMDQ_MODE_ENABLED (1<<0) +#define EXT_CSD_CMDQ_DEPTH_MASK 0x1F +#define EXT_CSD_CMDQ_SUPPORTED (1<<0) + +/* +* MMC_SWITCH access modes +*/ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* +* Erase/trim/discard +*/ +#define MMC_ERASE_ARG 0x00000000 +#define MMC_SECURE_ERASE_ARG 0x80000000 +#define MMC_TRIM_ARG 0x00000001 +#define MMC_DISCARD_ARG 0x00000003 +#define MMC_SECURE_TRIM1_ARG 0x80000001 +#define MMC_SECURE_TRIM2_ARG 0x80008000 +#define MMC_SECURE_ARGS 0x80000000 +#define MMC_TRIM_ARGS 0x00008001 + +#endif /* LINUX_MMC_MMC_H */ diff --git a/emummc/source/emmc/nx_emmc.c b/emummc/source/emmc/nx_emmc.c new file mode 100644 index 000000000..2dca775ae --- /dev/null +++ b/emummc/source/emmc/nx_emmc.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include "../utils/types.h" +#include "nx_emmc.h" + +int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf) +{ + // The last LBA is inclusive. + if (part->lba_start + sector_off > part->lba_end) + return 0; + return sdmmc_storage_read(storage, part->lba_start + sector_off, num_sectors, buf); +} + +int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf) +{ + // The last LBA is inclusive. + if (part->lba_start + sector_off > part->lba_end) + return 0; + return sdmmc_storage_write(storage, part->lba_start + sector_off, num_sectors, buf); +} diff --git a/emummc/source/emmc/nx_emmc.h b/emummc/source/emmc/nx_emmc.h new file mode 100644 index 000000000..e90d49f60 --- /dev/null +++ b/emummc/source/emmc/nx_emmc.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _NX_EMMC_H_ +#define _NX_EMMC_H_ + +#include "../utils/types.h" +#include "sdmmc.h" + +typedef struct _gpt_entry_t +{ + u8 type_guid[0x10]; + u8 part_guid[0x10]; + u64 lba_start; + u64 lba_end; + u64 attrs; + u16 name[36]; +} gpt_entry_t; + +typedef struct _gpt_header_t +{ + u64 signature; + u32 revision; + u32 size; + u32 crc32; + u32 res1; + u64 my_lba; + u64 alt_lba; + u64 first_use_lba; + u64 last_use_lba; + u8 disk_guid[0x10]; + u64 part_ent_lba; + u32 num_part_ents; + u32 part_ent_size; + u32 part_ents_crc32; + u8 res2[420]; +} gpt_header_t; + +#define NX_GPT_FIRST_LBA 1 +#define NX_GPT_NUM_BLOCKS 33 +#define NX_EMMC_BLOCKSIZE 512 + +typedef struct _emmc_part_t +{ + u32 lba_start; + u32 lba_end; + u64 attrs; + s8 name[37]; +} emmc_part_t; + +int nx_emmc_part_read(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf); +int nx_emmc_part_write(sdmmc_storage_t *storage, emmc_part_t *part, u32 sector_off, u32 num_sectors, void *buf); + +#endif diff --git a/emummc/source/emmc/sd.h b/emummc/source/emmc/sd.h new file mode 100644 index 000000000..c3bf82ba0 --- /dev/null +++ b/emummc/source/emmc/sd.h @@ -0,0 +1,124 @@ +/* + * include/linux/mmc/sd.h + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef LINUX_MMC_SD_H +#define LINUX_MMC_SD_H + +/* SD commands type argument response */ +/* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ +#define SD_SWITCH_VOLTAGE 11 /* ac R1 */ + +/* class 10 */ +#define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + +/* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + +/* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SD_STATUS 13 /* adtc R1 */ +#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SET_CLR_CARD_DETECT 42 +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* OCR bit definitions */ +#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */ +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ +#define SD_OCR_XPC (1 << 28) /* SDXC power control */ +#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ +#define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */ + +/* +* SD_SWITCH argument format: +* +* [31] Check (0) or switch (1) +* [30:24] Reserved (0) +* [23:20] Function group 6 +* [19:16] Function group 5 +* [15:12] Function group 4 +* [11:8] Function group 3 +* [7:4] Function group 2 +* [3:0] Function group 1 +*/ + +/* +* SD_SEND_IF_COND argument format: +* +* [31:12] Reserved (0) +* [11:8] Host Voltage Supply Flags +* [7:0] Check Pattern (0xAA) +*/ + +/* +* SCR field definitions +*/ +#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ +#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ +#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */ +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) + +/* +* SD bus widths +*/ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* +* SD bus speeds +*/ +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 +#define HS400_BUS_SPEED 5 + +#define SD_MODE_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED) +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) + +#define SD_DRIVER_TYPE_B 0x01 +#define SD_DRIVER_TYPE_A 0x02 + +#define SD_SET_CURRENT_LIMIT_200 0 +#define SD_SET_CURRENT_LIMIT_400 1 +#define SD_SET_CURRENT_LIMIT_600 2 +#define SD_SET_CURRENT_LIMIT_800 3 + +/* +* SD_SWITCH mode +*/ +#define SD_SWITCH_CHECK 0 +#define SD_SWITCH_SET 1 + +/* +* SD_SWITCH function groups +*/ +#define SD_SWITCH_GRP_ACCESS 0 + +/* +* SD_SWITCH access modes +*/ +#define SD_SWITCH_ACCESS_DEF 0 +#define SD_SWITCH_ACCESS_HS 1 + +#endif /* LINUX_MMC_SD_H */ diff --git a/emummc/source/emmc/sdmmc.c b/emummc/source/emmc/sdmmc.c new file mode 100644 index 000000000..166462e61 --- /dev/null +++ b/emummc/source/emmc/sdmmc.c @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include +#include +#include "sdmmc.h" +#include "mmc.h" +#include "sd.h" +#include "../utils/types.h" +#include "../utils/util.h" + +#define DPRINTF(...) //fprintf(stdout, __VA_ARGS__) + +static inline u32 unstuff_bits(u32 *resp, u32 start, u32 size) +{ + const u32 mask = (size < 32 ? 1 << size : 0) - 1; + const u32 off = 3 - ((start) / 32); + const u32 shft = (start) & 31; + u32 res = resp[off] >> shft; + if (size + shft > 32) + res |= resp[off - 1] << ((32 - shft) % 32); + return res & mask; +} + +/* +* Common functions for SD and MMC. +*/ + +static int _sdmmc_storage_check_result(u32 res) +{ + //Error mask: + //R1_OUT_OF_RANGE, R1_ADDRESS_ERROR, R1_BLOCK_LEN_ERROR, + //R1_ERASE_SEQ_ERROR, R1_ERASE_PARAM, R1_WP_VIOLATION, + //R1_LOCK_UNLOCK_FAILED, R1_COM_CRC_ERROR, R1_ILLEGAL_COMMAND, + //R1_CARD_ECC_FAILED, R1_CC_ERROR, R1_ERROR, R1_CID_CSD_OVERWRITE, + //R1_WP_ERASE_SKIP, R1_ERASE_RESET, R1_SWITCH_ERROR + if (!(res & 0xFDF9A080)) + return 1; + //TODO: R1_SWITCH_ERROR we can skip for certain card types. + return 0; +} + +static int _sdmmc_storage_execute_cmd_type1_ex(sdmmc_storage_t *storage, u32 *resp, u32 cmd, u32 arg, u32 check_busy, u32 expected_state, u32 mask) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, cmd, arg, SDMMC_RSP_TYPE_1, check_busy); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 0; + + sdmmc_get_rsp(storage->sdmmc, resp, 4, SDMMC_RSP_TYPE_1); + if (mask) + *resp &= ~mask; + + if (_sdmmc_storage_check_result(*resp)) + if (expected_state == 0x10 || R1_CURRENT_STATE(*resp) == expected_state) + return 1; + return 0; +} + +static int _sdmmc_storage_execute_cmd_type1(sdmmc_storage_t *storage, u32 cmd, u32 arg, u32 check_busy, u32 expected_state) +{ + u32 tmp; + return _sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, cmd, arg, check_busy, expected_state, 0); +} + +static int _sdmmc_storage_go_idle_state(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmd; + sdmmc_init_cmd(&cmd, MMC_GO_IDLE_STATE, 0, SDMMC_RSP_TYPE_0, 0); + return sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0); +} + +static int _sdmmc_storage_get_cid(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmd; + sdmmc_init_cmd(&cmd, MMC_ALL_SEND_CID, 0, SDMMC_RSP_TYPE_2, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0)) + return 0; + sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); + return 1; +} + +static int _sdmmc_storage_select_card(sdmmc_storage_t *storage) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SELECT_CARD, storage->rca << 16, 1, 0x10); +} + +static int _sdmmc_storage_get_csd(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, MMC_SEND_CSD, storage->rca << 16, SDMMC_RSP_TYPE_2, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 0; + sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); + return 1; +} + +static int _sdmmc_storage_set_blocklen(sdmmc_storage_t *storage, u32 blocklen) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SET_BLOCKLEN, blocklen, 0, R1_STATE_TRAN); +} + +static int _sdmmc_storage_get_status(sdmmc_storage_t *storage, u32 *resp, u32 mask) +{ + return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, MMC_SEND_STATUS, storage->rca << 16, 0, R1_STATE_TRAN, mask); +} + +static int _sdmmc_storage_check_status(sdmmc_storage_t *storage) +{ + u32 tmp; + return _sdmmc_storage_get_status(storage, &tmp, 0); +} + +static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out, u32 sector, u32 num_sectors, void *buf, u32 is_write) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, is_write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK, sector, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.num_sectors = num_sectors; + reqbuf.blksize = 512; + reqbuf.is_write = is_write; + reqbuf.is_multi_block = 1; + reqbuf.is_auto_cmd12 = 1; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, blkcnt_out)) + { + u32 tmp = 0; + sdmmc_stop_transmission(storage->sdmmc, &tmp); + _sdmmc_storage_get_status(storage, &tmp, 0); + return 0; + } + return 1; +} + +int sdmmc_storage_end(sdmmc_storage_t *storage) +{ + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + sdmmc_end(storage->sdmmc); + return 1; +} + +static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write) +{ + u8 *bbuf = (u8 *)buf; + + while (num_sectors) + { + u32 blkcnt = 0; + //Retry 9 times on error. + u32 retries = 10; + do + { + if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write)) + goto out; + else + retries--; + + msleep(100); + } while (retries); + return 0; + +out:; + DPRINTF("readwrite: %08X\n", blkcnt); + sector += blkcnt; + num_sectors -= blkcnt; + bbuf += 512 * blkcnt; + } + return 1; +} + +int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + return _sdmmc_storage_readwrite(storage, sector, num_sectors, buf, 0); +} + +int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf) +{ + return _sdmmc_storage_readwrite(storage, sector, num_sectors, buf, 1); +} + +/* +* MMC specific functions. +*/ + +static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u32 power) +{ + sdmmc_cmd_t cmd; + + u32 arg = 0; + switch (power) + { + case SDMMC_POWER_1_8: + arg = 0x40000080; //Sector access, voltage. + break; + case SDMMC_POWER_3_3: + arg = 0x403F8000; //Sector access, voltage. + break; + default: + return 0; + } + + sdmmc_init_cmd(&cmd, MMC_SEND_OP_COND, arg, SDMMC_RSP_TYPE_3, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0)) + return 0; + + return sdmmc_get_rsp(storage->sdmmc, pout, 4, SDMMC_RSP_TYPE_3); +} + +static int _mmc_storage_get_op_cond(sdmmc_storage_t *storage, u32 power) +{ + u32 timeout = get_tmr_ms() + 1500; + + while (1) + { + u32 cond = 0; + if (!_mmc_storage_get_op_cond_inner(storage, &cond, power)) + break; + if (cond & MMC_CARD_BUSY) + { + if (cond & 0x40000000) + storage->has_sector_access = 1; + return 1; + } + if (get_tmr_ms() > timeout) + break; + usleep(1000); + } + + return 0; +} + +static int _mmc_storage_set_relative_addr(sdmmc_storage_t *storage) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SET_RELATIVE_ADDR, storage->rca << 16, 0, 0x10); +} + +static void _mmc_storage_parse_cid(sdmmc_storage_t *storage) +{ + u32 *raw_cid = (u32 *)&(storage->raw_cid); + + switch (storage->csd.mmca_vsn) + { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + storage->cid.prod_name[6] = unstuff_bits(raw_cid, 48, 8); + storage->cid.manfid = unstuff_bits(raw_cid, 104, 24); + storage->cid.hwrev = unstuff_bits(raw_cid, 44, 4); + storage->cid.fwrev = unstuff_bits(raw_cid, 40, 4); + storage->cid.serial = unstuff_bits(raw_cid, 16, 24); + break; + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + storage->cid.manfid = unstuff_bits(raw_cid, 120, 8); + storage->cid.card_bga = unstuff_bits(raw_cid, 112, 2); + storage->cid.oemid = unstuff_bits(raw_cid, 104, 8); + storage->cid.prv = unstuff_bits(raw_cid, 48, 8); + storage->cid.serial = unstuff_bits(raw_cid, 16, 32); + break; + default: + break; + } + + storage->cid.prod_name[0] = unstuff_bits(raw_cid, 96, 8); + storage->cid.prod_name[1] = unstuff_bits(raw_cid, 88, 8); + storage->cid.prod_name[2] = unstuff_bits(raw_cid, 80, 8); + storage->cid.prod_name[3] = unstuff_bits(raw_cid, 72, 8); + storage->cid.prod_name[4] = unstuff_bits(raw_cid, 64, 8); + storage->cid.prod_name[5] = unstuff_bits(raw_cid, 56, 8); + + storage->cid.month = unstuff_bits(raw_cid, 12, 4); + storage->cid.year = unstuff_bits(raw_cid, 8, 4) + 1997; + if (storage->ext_csd.rev >= 5) + { + if (storage->cid.year < 2010) + storage->cid.year += 16; + } +} + +static void _mmc_storage_parse_csd(sdmmc_storage_t *storage) +{ + u32 *raw_csd = (u32 *)&(storage->raw_csd); + + storage->csd.mmca_vsn = unstuff_bits(raw_csd, 122, 4); + storage->csd.structure = unstuff_bits(raw_csd, 126, 2); + storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12); + storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4); + storage->csd.capacity = (1 + unstuff_bits(raw_csd, 62, 12)) << (unstuff_bits(raw_csd, 47, 3) + 2); +} + +static void _mmc_storage_parse_ext_csd(sdmmc_storage_t *storage, u8 *buf) +{ + storage->ext_csd.rev = buf[EXT_CSD_REV]; + storage->ext_csd.ext_struct = buf[EXT_CSD_STRUCTURE]; + storage->ext_csd.card_type = buf[EXT_CSD_CARD_TYPE]; + storage->ext_csd.dev_version = *(u16 *)&buf[EXT_CSD_DEVICE_VERSION]; + storage->ext_csd.boot_mult = buf[EXT_CSD_BOOT_MULT]; + storage->ext_csd.rpmb_mult = buf[EXT_CSD_RPMB_MULT]; + storage->ext_csd.sectors = *(u32 *)&buf[EXT_CSD_SEC_CNT]; + storage->ext_csd.bkops = buf[EXT_CSD_BKOPS_SUPPORT]; + storage->ext_csd.bkops_en = buf[EXT_CSD_BKOPS_EN]; + storage->ext_csd.bkops_status = buf[EXT_CSD_BKOPS_STATUS]; + + storage->sec_cnt = *(u32 *)&buf[EXT_CSD_SEC_CNT]; +} + +static int _mmc_storage_get_ext_csd(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, MMC_SEND_EXT_CSD, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 512; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + _mmc_storage_parse_ext_csd(storage, buf); + + return _sdmmc_storage_check_result(tmp); +} + +static int _mmc_storage_switch(sdmmc_storage_t *storage, u32 arg) +{ + return _sdmmc_storage_execute_cmd_type1(storage, MMC_SWITCH, arg, 1, 0x10); +} + +static int _mmc_storage_switch_buswidth(sdmmc_storage_t *storage, u32 bus_width) +{ + if (bus_width == SDMMC_BUS_WIDTH_1) + return 1; + + u32 arg = 0; + switch (bus_width) + { + case SDMMC_BUS_WIDTH_4: + arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + break; + case SDMMC_BUS_WIDTH_8: + arg = SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8); + break; + } + + if (_mmc_storage_switch(storage, arg)) + if (_sdmmc_storage_check_status(storage)) + { + sdmmc_set_bus_width(storage->sdmmc, bus_width); + return 1; + } + + return 0; +} + +static int _mmc_storage_enable_HS(sdmmc_storage_t *storage, int check) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS))) + return 0; + if (check && !_sdmmc_storage_check_status(storage)) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 2)) + return 0; + DPRINTF("[MMC] switched to HS\n"); + storage->csd.busspeed = 52; + if (check || _sdmmc_storage_check_status(storage)) + return 1; + return 0; +} + +static int _mmc_storage_enable_HS200(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200))) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 3)) + return 0; + if (!sdmmc_config_tuning(storage->sdmmc, 3, MMC_SEND_TUNING_BLOCK_HS200)) + return 0; + DPRINTF("[MMC] switched to HS200\n"); + storage->csd.busspeed = 200; + return _sdmmc_storage_check_status(storage); +} + +static int _mmc_storage_enable_HS400(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_enable_HS200(storage)) + return 0; + sdmmc_get_venclkctl(storage->sdmmc); + if (!_mmc_storage_enable_HS(storage, 0)) + return 0; + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8))) + return 0; + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400))) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, 4)) + return 0; + DPRINTF("[MMC] switched to HS400\n"); + storage->csd.busspeed = 400; + return _sdmmc_storage_check_status(storage); +} + +static int _mmc_storage_enable_highspeed(sdmmc_storage_t *storage, u32 card_type, u32 type) +{ + //TODO: this should be a config item. + // --v + if (!1 || sdmmc_get_voltage(storage->sdmmc) != SDMMC_POWER_1_8) + goto out; + + if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 && + card_type & EXT_CSD_CARD_TYPE_HS400_1_8V && + type == 4) + return _mmc_storage_enable_HS400(storage); + + if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 || + (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_4 + && card_type & EXT_CSD_CARD_TYPE_HS200_1_8V + && (type == 4 || type == 3))) + return _mmc_storage_enable_HS200(storage); + +out:; + if (card_type & EXT_CSD_CARD_TYPE_HS_52) + return _mmc_storage_enable_HS(storage, 1); + return 1; +} + +static int _mmc_storage_enable_bkops(sdmmc_storage_t *storage) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_SET_BITS, EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_LEVEL_2))) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) +{ + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + storage->rca = 2; //TODO: this could be a config item. + + if (!sdmmc_init(sdmmc, id, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, 0, 0)) + return 0; + DPRINTF("[MMC] after init\n"); + + usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + DPRINTF("[MMC] went to idle state\n"); + + if (!_mmc_storage_get_op_cond(storage, SDMMC_POWER_1_8)) + return 0; + DPRINTF("[MMC] got op cond\n"); + + if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) + return 0; + DPRINTF("[MMC] got cid\n"); + + if (!_mmc_storage_set_relative_addr(storage)) + return 0; + DPRINTF("[MMC] set relative addr\n"); + + if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) + return 0; + DPRINTF("[MMC] got csd\n"); + _mmc_storage_parse_csd(storage); + + if (!sdmmc_setup_clock(storage->sdmmc, 1)) + return 0; + DPRINTF("[MMC] after setup clock\n"); + + if (!_sdmmc_storage_select_card(storage)) + return 0; + DPRINTF("[MMC] card selected\n"); + + if (!_sdmmc_storage_set_blocklen(storage, 512)) + return 0; + DPRINTF("[MMC] set blocklen to 512\n"); + + u32 *csd = (u32 *)storage->raw_csd; + //Check system specification version, only version 4.0 and later support below features. + if (unstuff_bits(csd, 122, 4) < CSD_SPEC_VER_4) + { + storage->sec_cnt = (1 + unstuff_bits(csd, 62, 12)) << (unstuff_bits(csd, 47, 3) + 2); + return 1; + } + + if (!_mmc_storage_switch_buswidth(storage, bus_width)) + return 0; + DPRINTF("[MMC] switched buswidth\n"); + + u8 *ext_csd = (u8 *)malloc(512); + if (!_mmc_storage_get_ext_csd(storage, ext_csd)) + { + free(ext_csd); + return 0; + } + free(ext_csd); + DPRINTF("[MMC] got ext_csd\n"); + _mmc_storage_parse_cid(storage); //This needs to be after csd and ext_csd + + /* When auto BKOPS is enabled the mmc device should be powered all the time until we disable this and check status. + Disable it for now until BKOPS disable added to power down sequence at sdmmc_storage_end(). + Additionally this works only when we put the device in idle mode which we don't after enabling it. */ + if (storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2) && 0) + { + _mmc_storage_enable_bkops(storage); + DPRINTF("[MMC] BKOPS enabled\n"); + } + else + { + DPRINTF("[MMC] BKOPS disabled\n"); + } + + if (!_mmc_storage_enable_highspeed(storage, storage->ext_csd.card_type, type)) + return 0; + DPRINTF("[MMC] succesfully switched to highspeed mode\n"); + + sdmmc_sd_clock_ctrl(storage->sdmmc, 1); + + return 1; +} + +int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition) +{ + if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_PART_CONFIG, partition))) + return 0; + if (!_sdmmc_storage_check_status(storage)) + return 0; + storage->partition = partition; + return 1; +} + +/* +* SD specific functions. +*/ + +static int _sd_storage_execute_app_cmd(sdmmc_storage_t *storage, u32 expected_state, u32 mask, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + u32 tmp; + if (!_sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, MMC_APP_CMD, storage->rca << 16, 0, expected_state, mask)) + return 0; + return sdmmc_execute_cmd(storage->sdmmc, cmd, req, blkcnt_out); +} + +static int _sd_storage_execute_app_cmd_type1(sdmmc_storage_t *storage, u32 *resp, u32 cmd, u32 arg, u32 check_busy, u32 expected_state) +{ + if (!_sdmmc_storage_execute_cmd_type1(storage, MMC_APP_CMD, storage->rca << 16, 0, R1_STATE_TRAN)) + return 0; + return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, cmd, arg, check_busy, expected_state, 0); +} + +static int _sd_storage_send_if_cond(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SEND_IF_COND, 0x1AA, SDMMC_RSP_TYPE_5, 0); + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + return 1; // The SD Card is version 1.X + + u32 resp = 0; + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_5)) + return 2; + + return (resp & 0xFF) == 0xAA ? 0 : 2; +} + +static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int is_version_1, int supports_low_voltage) +{ + sdmmc_cmd_t cmdbuf; + // Support for Current > 150mA + u32 arg = (~is_version_1 & 1) ? SD_OCR_XPC : 0; + // Support for handling block-addressed SDHC cards + arg |= (~is_version_1 & 1) ? SD_OCR_CCS : 0; + // Support for 1.8V + arg |= (supports_low_voltage & ~is_version_1 & 1) ? SD_OCR_S18R : 0; + // This is needed for most cards. Do not set bit7 even if 1.8V is supported. + arg |= SD_OCR_VDD_32_33; + sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0); + + DPRINTF("[SD] before _sd_storage_execute_app_cmd\n"); + if (!_sd_storage_execute_app_cmd(storage, 0x10, is_version_1 ? 0x400000 : 0, &cmdbuf, 0, 0)) + return 0; + DPRINTF("[SD] before sdmmc_get_rsp\n"); + return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3); +} + +static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage) +{ + u32 timeout = get_tmr_ms() + 1500; + + while (1) + { + u32 cond = 0; + if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage)) + { + DPRINTF("[SD] _sd_storage_get_op_cond_once failed\r\n"); + break; + } + + if (cond & MMC_CARD_BUSY) + { + if (cond & SD_OCR_CCS) + storage->has_sector_access = 1; + + if (false && cond & SD_ROCR_S18A && supports_low_voltage) + { + //The low voltage regulator configuration is valid for SDMMC1 only. + if (storage->sdmmc->id == SDMMC_1 && + _sdmmc_storage_execute_cmd_type1(storage, SD_SWITCH_VOLTAGE, 0, 0, R1_STATE_READY)) + { + if (!sdmmc_enable_low_voltage(storage->sdmmc)) + return 0; + storage->is_low_voltage = 1; + + DPRINTF("-> switched to low voltage\n"); + } + } + + return 1; + } + if (get_tmr_ms() > timeout) + break; + msleep(10); // Needs to be at least 10ms for some SD Cards + } + + DPRINTF("[SD] _sd_storage_get_op_cond Timeout\r\n"); + + return 0; +} + +static int _sd_storage_get_rca(sdmmc_storage_t *storage) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SEND_RELATIVE_ADDR, 0, SDMMC_RSP_TYPE_4, 0); + + u32 timeout = get_tmr_ms() + 1500; + + while (1) + { + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) + break; + + u32 resp = 0; + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_4)) + break; + + if (resp >> 16) + { + storage->rca = resp >> 16; + return 1; + } + + if (get_tmr_ms() > timeout) + break; + usleep(1000); + } + + return 0; +} + +static void _sd_storage_parse_scr(sdmmc_storage_t *storage) +{ + // unstuff_bits can parse only 4 u32 + u32 resp[4]; + + resp[3] = *(u32 *)&storage->raw_scr[4]; + resp[2] = *(u32 *)&storage->raw_scr[0]; + + storage->scr.sda_vsn = unstuff_bits(resp, 56, 4); + storage->scr.bus_widths = unstuff_bits(resp, 48, 4); + if (storage->scr.sda_vsn == SCR_SPEC_VER_2) + /* Check if Physical Layer Spec v3.0 is supported */ + storage->scr.sda_spec3 = unstuff_bits(resp, 47, 1); + if (storage->scr.sda_spec3) + storage->scr.cmds = unstuff_bits(resp, 32, 2); +} + +int _sd_storage_get_scr(sdmmc_storage_t *storage, u8 *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_APP_SEND_SCR, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 8; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + //Prepare buffer for unstuff_bits + for (int i = 0; i < 8; i+=4) + { + storage->raw_scr[i + 3] = buf[i]; + storage->raw_scr[i + 2] = buf[i + 1]; + storage->raw_scr[i + 1] = buf[i + 2]; + storage->raw_scr[i] = buf[i + 3]; + } + _sd_storage_parse_scr(storage); + + return _sdmmc_storage_check_result(tmp); +} + +int _sd_storage_switch_get(sdmmc_storage_t *storage, void *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_SWITCH, 0xFFFFFF, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_storage_check_result(tmp); +} + +int _sd_storage_switch(sdmmc_storage_t *storage, void *buf, int mode, int group, u32 arg) +{ + sdmmc_cmd_t cmdbuf; + u32 switchcmd = mode << 31 | 0x00FFFFFF; + switchcmd &= ~(0xF << (group * 4)); + switchcmd |= arg << (group * 4); + sdmmc_init_cmd(&cmdbuf, SD_SWITCH, switchcmd, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_storage_check_result(tmp); +} + +void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u8 *buf) +{ + u32 pwr = SD_SET_CURRENT_LIMIT_800; + _sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr); + + while (pwr > 0) + { + pwr--; + _sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr); + if (((buf[15] >> 4) & 0x0F) == pwr) + break; + } + + switch (pwr) + { + case SD_SET_CURRENT_LIMIT_800: + DPRINTF("[SD] Power limit raised to 800mA\n"); + break; + case SD_SET_CURRENT_LIMIT_600: + DPRINTF("[SD] Power limit raised to 600mA\n"); + break; + case SD_SET_CURRENT_LIMIT_400: + DPRINTF("[SD] Power limit raised to 800mA\n"); + break; + default: + case SD_SET_CURRENT_LIMIT_200: + DPRINTF("[SD] Power limit defaulted to 200mA\n"); + break; + } +} + +int _sd_storage_enable_highspeed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf) +{ + if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, 0, hs_type)) + return 0; + + u32 type_out = buf[16] & 0xF; + if (type_out != hs_type) + return 0; + + if ((((u16)buf[0] << 8) | buf[1]) < 0x320) + { + if (!_sd_storage_switch(storage, buf, SD_SWITCH_SET, 0, hs_type)) + return 0; + + if (type_out != (buf[16] & 0xF)) + return 0; + } + + return 1; +} + +int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf) +{ + // Try to raise the current limit to let the card perform better. + _sd_storage_set_current_limit(storage, buf); + + if (sdmmc_get_bus_width(storage->sdmmc) != SDMMC_BUS_WIDTH_4) + return 0; + + if (!_sd_storage_switch_get(storage, buf)) + return 0; + + u32 hs_type = 0; + switch (type) + { + case 11: + // Fall through if not supported. + if (buf[13] & SD_MODE_UHS_SDR104) + { + type = 11; + hs_type = UHS_SDR104_BUS_SPEED; + DPRINTF("[SD] Bus speed set to SDR104\n"); + storage->csd.busspeed = 104; + break; + } + case 10: + if (buf[13] & SD_MODE_UHS_SDR50) + { + type = 10; + hs_type = UHS_SDR50_BUS_SPEED; + DPRINTF("[SD] Bus speed set to SDR50\n"); + storage->csd.busspeed = 50; + break; + } + case 8: + if (!(buf[13] & SD_MODE_UHS_SDR12)) + return 0; + type = 8; + hs_type = UHS_SDR12_BUS_SPEED; + DPRINTF("[SD] Bus speed set to SDR12\n"); + storage->csd.busspeed = 12; + break; + default: + return 0; + break; + } + + if (!_sd_storage_enable_highspeed(storage, hs_type, buf)) + return 0; + if (!sdmmc_setup_clock(storage->sdmmc, type)) + return 0; + if (!sdmmc_config_tuning(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK)) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int _sd_storage_enable_highspeed_high_volt(sdmmc_storage_t *storage, u8 *buf) +{ + if (!_sd_storage_switch_get(storage, buf)) + return 0; + if (!(buf[13] & SD_MODE_HIGH_SPEED)) + return 1; + + if (!_sd_storage_enable_highspeed(storage, 1, buf)) + return 0; + if (!_sdmmc_storage_check_status(storage)) + return 0; + return sdmmc_setup_clock(storage->sdmmc, 7); +} + +static void _sd_storage_parse_ssr(sdmmc_storage_t *storage) +{ + // unstuff_bits supports only 4 u32 so break into 2 x 16byte groups + u32 raw_ssr1[4]; + u32 raw_ssr2[4]; + + raw_ssr1[3] = *(u32 *)&storage->raw_ssr[12]; + raw_ssr1[2] = *(u32 *)&storage->raw_ssr[8]; + raw_ssr1[1] = *(u32 *)&storage->raw_ssr[4]; + raw_ssr1[0] = *(u32 *)&storage->raw_ssr[0]; + + raw_ssr2[3] = *(u32 *)&storage->raw_ssr[28]; + raw_ssr2[2] = *(u32 *)&storage->raw_ssr[24]; + raw_ssr2[1] = *(u32 *)&storage->raw_ssr[20]; + raw_ssr2[0] = *(u32 *)&storage->raw_ssr[16]; + + storage->ssr.bus_width = (unstuff_bits(raw_ssr1, 510 - 384, 2) & SD_BUS_WIDTH_4) ? 4 : 1; + switch(unstuff_bits(raw_ssr1, 440 - 384, 8)) + { + case 0: + storage->ssr.speed_class = 0; + break; + case 1: + storage->ssr.speed_class = 2; + break; + case 2: + storage->ssr.speed_class = 4; + break; + case 3: + storage->ssr.speed_class = 6; + break; + case 4: + storage->ssr.speed_class = 10; + break; + default: + storage->ssr.speed_class = unstuff_bits(raw_ssr1, 440 - 384, 8); + break; + } + storage->ssr.uhs_grade = unstuff_bits(raw_ssr1, 396 - 384, 4); + storage->ssr.video_class = unstuff_bits(raw_ssr1, 384 - 384, 8); + + storage->ssr.app_class = unstuff_bits(raw_ssr2, 336 - 256, 4); +} + +static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf) +{ + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, SD_APP_SD_STATUS, 0, SDMMC_RSP_TYPE_1, 0); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 0; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!(storage->csd.cmdclass & CCC_APP_SPEC)) + { + DPRINTF("[SD] ssr: Card lacks mandatory SD Status function\n"); + return 0; + } + + if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0)) + return 0; + + u32 tmp = 0; + sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1); + //Prepare buffer for unstuff_bits + for (int i = 0; i < 64; i+=4) + { + storage->raw_ssr[i + 3] = buf[i]; + storage->raw_ssr[i + 2] = buf[i + 1]; + storage->raw_ssr[i + 1] = buf[i + 2]; + storage->raw_ssr[i] = buf[i + 3]; + } + _sd_storage_parse_ssr(storage); + + return _sdmmc_storage_check_result(tmp); +} + +static void _sd_storage_parse_cid(sdmmc_storage_t *storage) +{ + u32 *raw_cid = (u32 *)&(storage->raw_cid); + + storage->cid.manfid = unstuff_bits(raw_cid, 120, 8); + storage->cid.oemid = unstuff_bits(raw_cid, 104, 16); + storage->cid.prod_name[0] = unstuff_bits(raw_cid, 96, 8); + storage->cid.prod_name[1] = unstuff_bits(raw_cid, 88, 8); + storage->cid.prod_name[2] = unstuff_bits(raw_cid, 80, 8); + storage->cid.prod_name[3] = unstuff_bits(raw_cid, 72, 8); + storage->cid.prod_name[4] = unstuff_bits(raw_cid, 64, 8); + storage->cid.hwrev = unstuff_bits(raw_cid, 60, 4); + storage->cid.fwrev = unstuff_bits(raw_cid, 56, 4); + storage->cid.serial = unstuff_bits(raw_cid, 24, 32); + storage->cid.month = unstuff_bits(raw_cid, 8, 4); + storage->cid.year = unstuff_bits(raw_cid, 12, 8) + 2000; +} + +static void _sd_storage_parse_csd(sdmmc_storage_t *storage) +{ + u32 *raw_csd = (u32 *)&(storage->raw_csd); + + storage->csd.structure = unstuff_bits(raw_csd, 126, 2); + storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12); + storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4); + storage->csd.write_protect = unstuff_bits(raw_csd, 12, 2); + switch(storage->csd.structure) + { + case 0: + storage->csd.capacity = (1 + unstuff_bits(raw_csd, 62, 12)) << (unstuff_bits(raw_csd, 47, 3) + 2); + break; + case 1: + storage->csd.c_size = (1 + unstuff_bits(raw_csd, 48, 22)); + storage->csd.capacity = storage->csd.c_size << 10; + storage->csd.read_blkbits = 9; + break; + } +} + +int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) +{ + int is_version_1 = 0; + + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + + DPRINTF("[SD] before init\n"); + if (!sdmmc_init(sdmmc, id, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0)) + return 0; + DPRINTF("[SD] after init\n"); + + usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!_sdmmc_storage_go_idle_state(storage)) + return 0; + DPRINTF("[SD] went to idle state\n"); + + is_version_1 = _sd_storage_send_if_cond(storage); + if (is_version_1 == 2) + return 0; + DPRINTF("[SD] after send if cond\n"); + + if (!_sd_storage_get_op_cond(storage, is_version_1, bus_width == SDMMC_BUS_WIDTH_4 && type == 11)) + return 0; + DPRINTF("[SD] got op cond\n"); + + if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) + return 0; + DPRINTF("[SD] got cid\n"); + _sd_storage_parse_cid(storage); + + if (!_sd_storage_get_rca(storage)) + return 0; + DPRINTF("[SD] got rca (= %04X)\n", storage->rca); + + if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) + return 0; + DPRINTF("[SD] got csd\n"); + + //Parse CSD. + _sd_storage_parse_csd(storage); + switch (storage->csd.structure) + { + case 0: + storage->sec_cnt = storage->csd.capacity; + break; + case 1: + storage->sec_cnt = storage->csd.c_size << 10; + break; + default: + DPRINTF("[SD] Unknown CSD structure %d\n", storage->csd.structure); + break; + } + + if (!storage->is_low_voltage) + { + if (!sdmmc_setup_clock(storage->sdmmc, 6)) + return 0; + DPRINTF("[SD] after setup clock\n"); + } + + if (!_sdmmc_storage_select_card(storage)) + return 0; + DPRINTF("[SD] card selected\n"); + + if (!_sdmmc_storage_set_blocklen(storage, 512)) + return 0; + DPRINTF("[SD] set blocklen to 512\n"); + + u32 tmp = 0; + if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_CLR_CARD_DETECT, 0, 0, R1_STATE_TRAN)) + return 0; + DPRINTF("[SD] cleared card detect\n"); + + u8 *buf = (u8 *)malloc(512); + if (!_sd_storage_get_scr(storage, buf)) + { + free(buf); + return 0; + } + + DPRINTF("[SD] got scr\n"); + + // Check if card supports a wider bus and if it's not SD Version 1.X + if (bus_width == SDMMC_BUS_WIDTH_4 && (storage->scr.bus_widths & 4) && (storage->scr.sda_vsn & 0xF)) + { + if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN)) + { + free(buf); + return 0; + } + sdmmc_set_bus_width(storage->sdmmc, SDMMC_BUS_WIDTH_4); + DPRINTF("[SD] switched to wide bus width\n"); + } + else + { + DPRINTF("[SD] SD does not support wide bus width\n"); + } + + if (storage->is_low_voltage) + { + if (!_sd_storage_enable_highspeed_low_volt(storage, type, buf)) + { + free(buf); + return 0; + } + DPRINTF("[SD] enabled highspeed (low voltage)\n"); + } + else if (type != 6 && (storage->scr.sda_vsn & 0xF) != 0) + { + if (!_sd_storage_enable_highspeed_high_volt(storage, buf)) + { + free(buf); + return 0; + } + DPRINTF("[SD] enabled highspeed (high voltage)\n"); + storage->csd.busspeed = 25; + } + + sdmmc_sd_clock_ctrl(sdmmc, 1); + + // Parse additional card info from sd status. + if (_sd_storage_get_ssr(storage, buf)) + { + DPRINTF("[SD] got sd status\n"); + } + + free(buf); + return 1; +} + +/* +* Gamecard specific functions. +*/ + +int _gc_storage_custom_cmd(sdmmc_storage_t *storage, void *buf) +{ + u32 resp; + sdmmc_cmd_t cmdbuf; + sdmmc_init_cmd(&cmdbuf, 60, 0, SDMMC_RSP_TYPE_1, 1); + + sdmmc_req_t reqbuf; + reqbuf.buf = buf; + reqbuf.blksize = 64; + reqbuf.num_sectors = 1; + reqbuf.is_write = 1; + reqbuf.is_multi_block = 0; + reqbuf.is_auto_cmd12 = 0; + + if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, 0)) + { + sdmmc_stop_transmission(storage->sdmmc, &resp); + return 0; + } + + if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_1)) + return 0; + if (!_sdmmc_storage_check_result(resp)) + return 0; + return _sdmmc_storage_check_status(storage); +} + +int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc) +{ + memset(storage, 0, sizeof(sdmmc_storage_t)); + storage->sdmmc = sdmmc; + + if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, 14, 0)) + return 0; + DPRINTF("[gc] after init\n"); + + usleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor); + + if (!sdmmc_config_tuning(storage->sdmmc, 14, MMC_SEND_TUNING_BLOCK_HS200)) + return 0; + DPRINTF("[gc] after tuning\n"); + + sdmmc_sd_clock_ctrl(sdmmc, 1); + + return 1; +} diff --git a/emummc/source/emmc/sdmmc.h b/emummc/source/emmc/sdmmc.h new file mode 100644 index 000000000..7ae20e346 --- /dev/null +++ b/emummc/source/emmc/sdmmc.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _SDMMC_H_ +#define _SDMMC_H_ + +#include "../utils/types.h" +#include "sdmmc_driver.h" + +typedef struct _mmc_cid +{ + u32 manfid; + u8 prod_name[8]; + u8 card_bga; + u8 prv; + u32 serial; + u16 oemid; + u16 year; + u8 hwrev; + u8 fwrev; + u8 month; +} mmc_cid_t; + +typedef struct _mmc_csd +{ + u8 structure; + u8 mmca_vsn; + u16 cmdclass; + u32 c_size; + u32 r2w_factor; + u32 max_dtr; + u32 erase_size; /* In sectors */ + u32 read_blkbits; + u32 write_blkbits; + u32 capacity; + u8 write_protect; + u16 busspeed; +} mmc_csd_t; + +typedef struct _mmc_ext_csd +{ + u8 rev; + u32 sectors; + int bkops; /* background support bit */ + int bkops_en; /* manual bkops enable bit */ + u8 ext_struct; /* 194 */ + u8 card_type; /* 196 */ + u8 bkops_status; /* 246 */ + u16 dev_version; + u8 boot_mult; + u8 rpmb_mult; +} mmc_ext_csd_t; + +typedef struct _sd_scr +{ + u8 sda_vsn; + u8 sda_spec3; + u8 bus_widths; + u8 cmds; +} sd_scr_t; + +typedef struct _sd_ssr +{ + u8 bus_width; + u8 speed_class; + u8 uhs_grade; + u8 video_class; + u8 app_class; +} sd_ssr_t; + +/*! SDMMC storage context. */ +typedef struct _sdmmc_storage_t +{ + sdmmc_t *sdmmc; + u32 rca; + int has_sector_access; + u32 sec_cnt; + int is_low_voltage; + u32 partition; + u8 raw_cid[0x10]; + u8 raw_csd[0x10]; + u8 raw_scr[8]; + u8 raw_ssr[0x40]; + mmc_cid_t cid; + mmc_csd_t csd; + mmc_ext_csd_t ext_csd; + sd_scr_t scr; + sd_ssr_t ssr; +} sdmmc_storage_t; + +int sdmmc_storage_end(sdmmc_storage_t *storage); +int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); +int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); +int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition); +int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); +int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc); + +#endif diff --git a/emummc/source/emmc/sdmmc_driver.c b/emummc/source/emmc/sdmmc_driver.c new file mode 100644 index 000000000..0530c9361 --- /dev/null +++ b/emummc/source/emmc/sdmmc_driver.c @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include "../utils/types.h" +#include "../nx/cache.h" +#include "sdmmc.h" +#include "../utils/util.h" +#include "../soc/clock.h" +#include "mmc.h" +#include "../power/max7762x.h" +#include "../soc/t210.h" +#include "../soc/pmc.h" +#include "../soc/pinmux.h" +#include "../soc/gpio.h" + +#define DPRINTF(...) + +/*! SCMMC controller base addresses. */ +static const u64 _sdmmc_bases[4] = { + 0x700B0000, + 0x700B0200, + 0x700B0400, + 0x700B0600, +}; + +int sdmmc_get_voltage(sdmmc_t *sdmmc) +{ + u32 p = sdmmc->regs->pwrcon; + if (!(p & TEGRA_MMC_PWRCTL_SD_BUS_POWER)) + return SDMMC_POWER_OFF; + if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8) + return SDMMC_POWER_1_8; + if (p & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3) + return SDMMC_POWER_3_3; + return -1; +} + +static int _sdmmc_set_voltage(sdmmc_t *sdmmc, u32 power) +{ + u8 pwr = 0; + + switch (power) + { + case SDMMC_POWER_OFF: + sdmmc->regs->pwrcon &= ~TEGRA_MMC_PWRCTL_SD_BUS_POWER; + break; + case SDMMC_POWER_1_8: + sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; + break; + case SDMMC_POWER_3_3: + sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; + break; + default: + return 0; + } + + if (power != SDMMC_POWER_OFF) + { + pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; + sdmmc->regs->pwrcon = pwr; + } + + return 1; +} + +u32 sdmmc_get_bus_width(sdmmc_t *sdmmc) +{ + u32 h = sdmmc->regs->hostctl; + if (h & TEGRA_MMC_HOSTCTL_8BIT) + return SDMMC_BUS_WIDTH_8; + if (h & TEGRA_MMC_HOSTCTL_4BIT) + return SDMMC_BUS_WIDTH_4; + return SDMMC_BUS_WIDTH_1; +} + +void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width) +{ + if (bus_width == SDMMC_BUS_WIDTH_1) + sdmmc->regs->hostctl &= ~(TEGRA_MMC_HOSTCTL_4BIT | TEGRA_MMC_HOSTCTL_8BIT); + else if (bus_width == SDMMC_BUS_WIDTH_4) + { + sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_4BIT; + sdmmc->regs->hostctl &= ~TEGRA_MMC_HOSTCTL_8BIT; + } + else if (bus_width == SDMMC_BUS_WIDTH_8) + sdmmc->regs->hostctl |= TEGRA_MMC_HOSTCTL_8BIT; +} + +void sdmmc_get_venclkctl(sdmmc_t *sdmmc) +{ + sdmmc->venclkctl_tap = sdmmc->regs->venclkctl >> 16; + sdmmc->venclkctl_set = 1; +} + +static int _sdmmc_config_ven_ceata_clk(sdmmc_t *sdmmc, u32 id) +{ + u32 tap_val = 0; + + if (id == 4) + sdmmc->regs->venceatactl = (sdmmc->regs->venceatactl & 0xFFFFC0FF) | 0x2800; + sdmmc->regs->ventunctl0 &= 0xFFFDFFFF; + if (id == 4) + { + if (!sdmmc->venclkctl_set) + return 0; + tap_val = sdmmc->venclkctl_tap; + } + else + { + static const u32 tap_values[] = { 4, 0, 3, 0 }; + tap_val = tap_values[sdmmc->id]; + } + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xFF00FFFF) | (tap_val << 16); + + return 1; +} + +static int _sdmmc_get_clkcon(sdmmc_t *sdmmc) +{ + return sdmmc->regs->clkcon; +} + +static void _sdmmc_pad_config_fallback(sdmmc_t *sdmmc, u32 power) +{ + _sdmmc_get_clkcon(sdmmc); + switch (sdmmc->id) + { + case SDMMC_1: + if (power == SDMMC_POWER_OFF) + break; + if (power == SDMMC_POWER_1_8) + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x304; // Up: 3, Dn: 4. + else if (power == SDMMC_POWER_3_3) + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x808; // Up: 8, Dn: 8. + break; + case SDMMC_4: + APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) = (APB_MISC(APB_MISC_GP_EMMC4_PAD_CFGPADCTRL) & 0x3FFC) | 0x1040; + break; + } + //TODO: load standard values for other controllers, can depend on power. +} + +static int _sdmmc_wait_type4(sdmmc_t *sdmmc) +{ + int res = 1, should_disable_sd_clock = 0; + + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + sdmmc->regs->vendllcal |= 0x80000000; + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr_ms() + 5; + while (sdmmc->regs->vendllcal & 0x80000000) + { + if (get_tmr_ms() > timeout) + { + res = 0; + goto out; + } + } + + timeout = get_tmr_ms() + 10; + while (sdmmc->regs->dllcfgstatus & 0x80000000) + { + if (get_tmr_ms() > timeout) + { + res = 0; + goto out; + } + } + +out:; + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + return res; +} + +int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type) +{ + //Disable the SD clock if it was enabled, and reenable it later. + bool should_enable_sd_clock = false; + if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) + { + should_enable_sd_clock = true; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + _sdmmc_config_ven_ceata_clk(sdmmc, type); + + switch (type) + { + case 0: + case 1: + case 5: + case 6: + sdmmc->regs->hostctl &= 0xFB; //Should this be 0xFFFB (~4) ? + sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + break; + case 2: + case 7: + sdmmc->regs->hostctl |= 4; + sdmmc->regs->hostctl2 &= SDHCI_CTRL_VDD_330; + break; + case 3: + case 11: + case 13: + case 14: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 4: + //Non standard + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | HS400_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 8: + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR12_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + case 10: + //T210 Errata for SDR50, the host must be set to SDR104. + sdmmc->regs->hostctl2 = (sdmmc->regs->hostctl2 & SDHCI_CTRL_UHS_MASK) | UHS_SDR104_BUS_SPEED; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_VDD_180; + break; + } + + _sdmmc_get_clkcon(sdmmc); + + u32 tmp; + u16 divisor; + clock_sdmmc_get_params(&tmp, &divisor, type); + clock_sdmmc_config_clock_source(&tmp, sdmmc->id, tmp); + sdmmc->divisor = (tmp + divisor - 1) / divisor; + + //if divisor != 1 && divisor << 31 -> error + + u16 div = divisor >> 1; + divisor = 0; + if (div > 0xFF) + divisor = div >> 8; + sdmmc->regs->clkcon = (sdmmc->regs->clkcon & 0x3F) | (div << 8) | (divisor << 6); + + //Enable the SD clock again. + if (should_enable_sd_clock) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + if (type == 4) + return _sdmmc_wait_type4(sdmmc); + return 1; +} + +static void _sdmmc_sd_clock_enable(sdmmc_t *sdmmc) +{ + if (!sdmmc->no_sd) + { + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + sdmmc->sd_clock_enabled = 1; +} + +static void _sdmmc_sd_clock_disable(sdmmc_t *sdmmc) +{ + sdmmc->sd_clock_enabled = 0; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd) +{ + sdmmc->no_sd = no_sd; + if (no_sd) + { + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + return; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + return; + } + if (sdmmc->sd_clock_enabled) + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +static int _sdmmc_cache_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) +{ + switch (type) + { + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_3: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (size < 4) + return 0; + rsp[0] = sdmmc->regs->rspreg0; + break; + case SDMMC_RSP_TYPE_2: + if (size < 0x10) + return 0; + // CRC is stripped, so shifting is needed. + u32 tempreg; + for (int i = 0; i < 4; i++) + { + switch(i) + { + case 0: + tempreg = sdmmc->regs->rspreg3; + break; + case 1: + tempreg = sdmmc->regs->rspreg2; + break; + case 2: + tempreg = sdmmc->regs->rspreg1; + break; + case 3: + tempreg = sdmmc->regs->rspreg0; + break; + } + rsp[i] = tempreg << 8; + + if (i != 0) + rsp[i - 1] |= (tempreg >> 24) & 0xFF; + } + break; + default: + return 0; + break; + } + + return 1; +} + +int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type) +{ + if (!rsp || sdmmc->expected_rsp_type != type) + return 0; + + switch (type) + { + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_3: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (size < 4) + return 0; + rsp[0] = sdmmc->rsp[0]; + break; + case SDMMC_RSP_TYPE_2: + if (size < 0x10) + return 0; + rsp[0] = sdmmc->rsp[0]; + rsp[1] = sdmmc->rsp[1]; + rsp[2] = sdmmc->rsp[2]; + rsp[3] = sdmmc->rsp[3]; + break; + default: + return 0; + break; + } + + return 1; +} + +static void _sdmmc_reset(sdmmc_t *sdmmc) +{ + sdmmc->regs->swrst |= + TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE | TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE; + _sdmmc_get_clkcon(sdmmc); + u32 timeout = get_tmr_ms() + 2000; + while (sdmmc->regs->swrst << 29 >> 30 && get_tmr_ms() < timeout) + ; +} + +static int _sdmmc_wait_prnsts_type0(sdmmc_t *sdmmc, u32 wait_dat) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr_ms() + 2000; + while(sdmmc->regs->prnsts & 1) //CMD inhibit. + if (get_tmr_ms() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + + if (wait_dat) + { + timeout = get_tmr_ms() + 2000; + while (sdmmc->regs->prnsts & 2) //DAT inhibit. + if (get_tmr_ms() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + } + + return 1; +} + +static int _sdmmc_wait_prnsts_type1(sdmmc_t *sdmmc) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr_ms() + 2000; + while (!(sdmmc->regs->prnsts & 0x100000)) //DAT0 line level. + if (get_tmr_ms() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + + return 1; +} + +static int _sdmmc_setup_read_small_block(sdmmc_t *sdmmc) +{ + switch (sdmmc_get_bus_width(sdmmc)) + { + case SDMMC_BUS_WIDTH_1: + return 0; + break; + case SDMMC_BUS_WIDTH_4: + sdmmc->regs->blksize = 0x40; + break; + case SDMMC_BUS_WIDTH_8: + sdmmc->regs->blksize = 0x80; + break; + } + sdmmc->regs->blkcnt = 1; + sdmmc->regs->trnmod = TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + return 1; +} + +static int _sdmmc_parse_cmdbuf(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, bool is_data_present) +{ + u16 cmdflags = 0; + + switch (cmd->rsp_type) + { + case SDMMC_RSP_TYPE_0: + break; + case SDMMC_RSP_TYPE_1: + case SDMMC_RSP_TYPE_4: + case SDMMC_RSP_TYPE_5: + if (cmd->check_busy) + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY | + TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + else + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 | + TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + break; + case SDMMC_RSP_TYPE_2: + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 | + TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + break; + case SDMMC_RSP_TYPE_3: + cmdflags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + break; + default: + return 0; + break; + } + + if (is_data_present) + cmdflags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + sdmmc->regs->argument = cmd->arg; + sdmmc->regs->cmdreg = (cmd->cmd << 8) | cmdflags; + + return 1; +} + +static void _sdmmc_parse_cmd_48(sdmmc_t *sdmmc, u32 cmd) +{ + sdmmc_cmd_t cmdbuf; + cmdbuf.cmd = cmd; + cmdbuf.arg = 0; + cmdbuf.rsp_type = SDMMC_RSP_TYPE_1; + cmdbuf.check_busy = 0; + _sdmmc_parse_cmdbuf(sdmmc, &cmdbuf, true); +} + +static int _sdmmc_config_tuning_once(sdmmc_t *sdmmc, u32 cmd) +{ + if (sdmmc->no_sd) + return 0; + if (!_sdmmc_wait_prnsts_type0(sdmmc, 1)) + return 0; + + _sdmmc_setup_read_small_block(sdmmc); + sdmmc->regs->norintstsen |= TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY; + sdmmc->regs->norintsts = sdmmc->regs->norintsts; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_parse_cmd_48(sdmmc, cmd); + _sdmmc_get_clkcon(sdmmc); + usleep(1); + _sdmmc_reset(sdmmc); + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr_us() + 5000; + while (get_tmr_us() < timeout) + { + if (sdmmc->regs->norintsts & 0x20) + { + sdmmc->regs->norintsts = 0x20; + sdmmc->regs->norintstsen &= 0xFFDF; + _sdmmc_get_clkcon(sdmmc); + usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); + return 1; + } + } + _sdmmc_reset(sdmmc); + sdmmc->regs->norintstsen &= 0xFFDF; + _sdmmc_get_clkcon(sdmmc); + usleep((1000 * 8 + sdmmc->divisor - 1) / sdmmc->divisor); + return 0; +} + +int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd) +{ + u32 max = 0, flag = 0; + + sdmmc->regs->field_1C4 = 0; + switch (type) + { + case 3: + case 4: + case 11: + max = 0x80; + flag = 0x4000; + break; + case 10: + case 13: + case 14: + max = 0x100; + flag = 0x8000; + break; + default: + return 0; + } + + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFF1FFF) | flag; + sdmmc->regs->ventunctl0 = (sdmmc->regs->ventunctl0 & 0xFFFFE03F) | 0x40; + sdmmc->regs->ventunctl0 |= 0x20000; + sdmmc->regs->hostctl2 |= SDHCI_CTRL_EXEC_TUNING; + + for (u32 i = 0; i < max; i++) + { + _sdmmc_config_tuning_once(sdmmc, cmd); + if (!(sdmmc->regs->hostctl2 & SDHCI_CTRL_EXEC_TUNING)) + break; + } + + if (sdmmc->regs->hostctl2 & SDHCI_CTRL_TUNED_CLK) + return 1; + return 0; +} + +static int _sdmmc_enable_internal_clock(sdmmc_t *sdmmc) +{ + //Enable internal clock and wait till it is stable. + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + u32 timeout = get_tmr_ms() + 2000; + while (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) + { + if (get_tmr_ms() > timeout) + return 0; + } + + sdmmc->regs->hostctl2 &= ~SDHCI_CTRL_PRESET_VAL_EN; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_CLKGEN_SELECT; + sdmmc->regs->hostctl2 |= SDHCI_HOST_VERSION_4_EN; + + if (!(sdmmc->regs->capareg & 0x10000000)) + return 0; + + sdmmc->regs->hostctl2 |= SDHCI_ADDRESSING_64BIT_EN; + sdmmc->regs->hostctl &= 0xE7; + sdmmc->regs->timeoutcon = (sdmmc->regs->timeoutcon & 0xF0) | 0xE; + + return 1; +} + +static int _sdmmc_autocal_config_offset(sdmmc_t *sdmmc, u32 power) +{ + u32 off_pd = 0; + u32 off_pu = 0; + + switch (sdmmc->id) + { + case SDMMC_2: + case SDMMC_4: + if (power != SDMMC_POWER_1_8) + return 0; + off_pd = 5; + off_pu = 5; + break; + case SDMMC_1: + case SDMMC_3: + if (power == SDMMC_POWER_1_8) + { + off_pd = 123; + off_pu = 123; + } + else if (power == SDMMC_POWER_3_3) + { + off_pd = 125; + off_pu = 0; + } + else + return 0; + break; + } + + sdmmc->regs->autocalcfg = (((sdmmc->regs->autocalcfg & 0xFFFF80FF) | (off_pd << 8)) >> 7 << 7) | off_pu; + return 1; +} + +static void _sdmmc_autocal_execute(sdmmc_t *sdmmc, u32 power) +{ + bool should_enable_sd_clock = false; + if (sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE) + { + should_enable_sd_clock = true; + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + } + + if (!(sdmmc->regs->sdmemcmppadctl & 0x80000000)) + { + sdmmc->regs->sdmemcmppadctl |= 0x80000000; + _sdmmc_get_clkcon(sdmmc); + usleep(1); + } + + sdmmc->regs->autocalcfg |= 0xA0000000; + _sdmmc_get_clkcon(sdmmc); + usleep(1); + + u32 timeout = get_tmr_ms() + 10; + while (sdmmc->regs->autocalcfg & 0x80000000) + { + if (get_tmr_ms() > timeout) + { + //In case autocalibration fails, we load suggested standard values. + _sdmmc_pad_config_fallback(sdmmc, power); + sdmmc->regs->autocalcfg &= 0xDFFFFFFF; + break; + } + } + + sdmmc->regs->sdmemcmppadctl &= 0x7FFFFFFF; + + if(should_enable_sd_clock) + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; +} + +static void _sdmmc_enable_interrupts(sdmmc_t *sdmmc) +{ + sdmmc->regs->norintstsen |= 0xB; + sdmmc->regs->errintstsen |= 0x17F; + sdmmc->regs->norintsts = sdmmc->regs->norintsts; + sdmmc->regs->errintsts = sdmmc->regs->errintsts; +} + +static void _sdmmc_mask_interrupts(sdmmc_t *sdmmc) +{ + sdmmc->regs->errintstsen &= 0xFE80; + sdmmc->regs->norintstsen &= 0xFFF4; +} + +static int _sdmmc_check_mask_interrupt(sdmmc_t *sdmmc, u16 *pout, u16 mask) +{ + u16 norintsts = sdmmc->regs->norintsts; + u16 errintsts = sdmmc->regs->errintsts; + + DPRINTF("norintsts %08X; errintsts %08X\n", norintsts, errintsts); + + if (pout) + *pout = norintsts; + + //Check for error interrupt. + if (norintsts & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) + { + sdmmc->regs->errintsts = errintsts; + return SDMMC_MASKINT_ERROR; + } + else if (norintsts & mask) + { + sdmmc->regs->norintsts = norintsts & mask; + return SDMMC_MASKINT_MASKED; + } + + return SDMMC_MASKINT_NOERROR; +} + +static int _sdmmc_wait_request(sdmmc_t *sdmmc) +{ + _sdmmc_get_clkcon(sdmmc); + + u32 timeout = get_tmr_ms() + 2000; + while (1) + { + int res = _sdmmc_check_mask_interrupt(sdmmc, 0, TEGRA_MMC_NORINTSTS_CMD_COMPLETE); + if (res == SDMMC_MASKINT_MASKED) + break; + if (res != SDMMC_MASKINT_NOERROR || get_tmr_ms() > timeout) + { + _sdmmc_reset(sdmmc); + return 0; + } + } + + return 1; +} + +static int _sdmmc_stop_transmission_inner(sdmmc_t *sdmmc, u32 *rsp) +{ + sdmmc_cmd_t cmd; + + if (!_sdmmc_wait_prnsts_type0(sdmmc, 0)) + return 0; + + _sdmmc_enable_interrupts(sdmmc); + cmd.cmd = MMC_STOP_TRANSMISSION; + cmd.arg = 0; + cmd.rsp_type = SDMMC_RSP_TYPE_1; + cmd.check_busy = 1; + _sdmmc_parse_cmdbuf(sdmmc, &cmd, false); + int res = _sdmmc_wait_request(sdmmc); + _sdmmc_mask_interrupts(sdmmc); + + if (!res) + return 0; + + _sdmmc_cache_rsp(sdmmc, rsp, 4, SDMMC_RSP_TYPE_1); + return _sdmmc_wait_prnsts_type1(sdmmc); +} + +int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp) +{ + if (!sdmmc->sd_clock_enabled) + return 0; + + bool should_disable_sd_clock = false; + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = true; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + } + + int res = _sdmmc_stop_transmission_inner(sdmmc, rsp); + usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + return res; +} + +static int _sdmmc_config_dma(sdmmc_t *sdmmc, u32 *blkcnt_out, sdmmc_req_t *req) +{ + if (!req->blksize || !req->num_sectors) + return 0; + + u32 blkcnt = req->num_sectors; + if (blkcnt >= 0xFFFF) + blkcnt = 0xFFFF; + u64 admaaddr = sdmmc->dma_addr_fs; + + //Check alignment. + if (admaaddr & 7) + return 0; + + sdmmc->regs->admaaddr = admaaddr & 0xFFFFFFFFF; + sdmmc->regs->admaaddr_hi = (admaaddr >> 32) & 0xFFFFFFFFF; + + sdmmc->dma_addr_next = (admaaddr + 0x80000) & 0xFFFFFFFFFFF80000; + + sdmmc->regs->blksize = req->blksize | 0x7000; + sdmmc->regs->blkcnt = blkcnt; + + if (blkcnt_out) + *blkcnt_out = blkcnt; + + u32 trnmode = TEGRA_MMC_TRNMOD_DMA_ENABLE; + if (req->is_multi_block) + trnmode = TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE | + TEGRA_MMC_TRNMOD_DMA_ENABLE; + if (!req->is_write) + trnmode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + if (req->is_auto_cmd12) + trnmode = (trnmode & 0xFFF3) | TEGRA_MMC_TRNMOD_AUTO_CMD12; + + sdmmc->regs->trnmod = trnmode; + + return 1; +} + +static int _sdmmc_update_dma(sdmmc_t *sdmmc) +{ + u16 blkcnt = 0; + do + { + blkcnt = sdmmc->regs->blkcnt; + u32 timeout = get_tmr_ms() + 1500; + do + { + int res = 0; + while (1) + { + u16 intr = 0; + res = _sdmmc_check_mask_interrupt(sdmmc, &intr, + TEGRA_MMC_NORINTSTS_XFER_COMPLETE | TEGRA_MMC_NORINTSTS_DMA_INTERRUPT); + if (res < 0) + break; + if (intr & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) + return 1; //Transfer complete. + if (intr & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) + { + //Update DMA. + sdmmc->regs->admaaddr = sdmmc->dma_addr_next & 0xFFFFFFFFF; + sdmmc->regs->admaaddr_hi = (sdmmc->dma_addr_next >> 32) & 0xFFFFFFFFF; + sdmmc->dma_addr_next += 0x80000; + } + } + if (res != SDMMC_MASKINT_NOERROR) + { + _sdmmc_reset(sdmmc); + return 0; + } + } while (get_tmr_ms() < timeout); + } while (sdmmc->regs->blkcnt != blkcnt); + + _sdmmc_reset(sdmmc); + return 0; +} + +static int _sdmmc_execute_cmd_inner(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + int has_req_or_check_busy = req || cmd->check_busy; + if (!_sdmmc_wait_prnsts_type0(sdmmc, has_req_or_check_busy)) + return 0; + + u32 blkcnt = 0; + bool is_data_present = false; + if (req) + { + _sdmmc_config_dma(sdmmc, &blkcnt, req); + armDCacheFlush(req->buf, req->blksize * blkcnt); + + _sdmmc_enable_interrupts(sdmmc); + is_data_present = true; + } + else + { + _sdmmc_enable_interrupts(sdmmc); + is_data_present = false; + } + + _sdmmc_parse_cmdbuf(sdmmc, cmd, is_data_present); + + int res = _sdmmc_wait_request(sdmmc); + DPRINTF("rsp(%d): %08X, %08X, %08X, %08X\n", res, + sdmmc->regs->rspreg0, sdmmc->regs->rspreg1, sdmmc->regs->rspreg2, sdmmc->regs->rspreg3); + if (res) + { + if (cmd->rsp_type) + { + sdmmc->expected_rsp_type = cmd->rsp_type; + _sdmmc_cache_rsp(sdmmc, sdmmc->rsp, 0x10, cmd->rsp_type); + } + if (req) + _sdmmc_update_dma(sdmmc); + } + + _sdmmc_mask_interrupts(sdmmc); + + if (res) + { + if (req) + { + armDCacheFlush(req->buf, req->blksize * blkcnt); + + if (blkcnt_out) + *blkcnt_out = blkcnt; + + if (req->is_auto_cmd12) + sdmmc->rsp3 = sdmmc->regs->rspreg3; + } + + if (cmd->check_busy || req) + return _sdmmc_wait_prnsts_type1(sdmmc); + } + + return res; +} + +static int _sdmmc_config_sdmmc1() +{ + //Configure SD card detect. + PINMUX_AUX(PINMUX_AUX_GPIO_PZ1) = PINMUX_INPUT_ENABLE | PINMUX_PULL_UP | 1; //GPIO control, pull up. + APB_MISC(APB_MISC_GP_VGPIO_GPIO_MUX_SEL) = 0; + gpio_config(GPIO_PORT_Z, GPIO_PIN_1, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_Z, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); + usleep(100); + if(!!gpio_read(GPIO_PORT_Z, GPIO_PIN_1)) + return 0; + + /* + * Pinmux config: + * DRV_TYPE = DRIVE_2X + * E_SCHMT = ENABLE (for 1.8V), DISABLE (for 3.3V) + * E_INPUT = ENABLE + * TRISTATE = PASSTHROUGH + * APB_MISC_GP_SDMMCx_CLK_LPBK_CONTROL = SDMMCx_CLK_PAD_E_LPBK for CLK + */ + + //Configure SDMMC1 pinmux. + APB_MISC(APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL) = 1; + PINMUX_AUX(PINMUX_AUX_SDMMC1_CLK) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED; + PINMUX_AUX(PINMUX_AUX_SDMMC1_CMD) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT3) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT2) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT1) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; + PINMUX_AUX(PINMUX_AUX_SDMMC1_DAT0) = PINMUX_DRIVE_2X | PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_PULL_UP; + + //Make sure the SDMMC1 controller is powered. + //PMC(APBDEV_PMC_NO_IOPOWER) &= ~(1 << 12); + //Assume 3.3V SD card voltage. + //PMC(APBDEV_PMC_PWR_DET_VAL) |= (1 << 12); + + //Set enable SD card power. + PINMUX_AUX(PINMUX_AUX_DMIC3_CLK) = PINMUX_INPUT_ENABLE | PINMUX_PULL_DOWN | 1; //GPIO control, pull down. + gpio_config(GPIO_PORT_E, GPIO_PIN_4, GPIO_MODE_GPIO); + gpio_write(GPIO_PORT_E, GPIO_PIN_4, GPIO_HIGH); + gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_ENABLE); + + usleep(1000); + + //Enable SD card power. + //max77620_regulator_set_voltage(REGULATOR_LDO2, 3300000); + //max77620_regulator_enable(REGULATOR_LDO2, 1); + + //usleep(1000); + + //For good measure. + APB_MISC(APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL) = 0x10000000; + + usleep(1000); + + return 1; +} + +int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd) +{ + if (id > SDMMC_4) + return 0; + + if (id == SDMMC_1) + if (!_sdmmc_config_sdmmc1()) + return 0; + + memset(sdmmc, 0, sizeof(sdmmc_t)); + + sdmmc->regs = (t210_sdmmc_t *)QueryIoMapping(_sdmmc_bases[id], 0x200); + sdmmc->id = id; + sdmmc->clock_stopped = 1; + + if (clock_sdmmc_is_not_reset_and_enabled(id)) + { + _sdmmc_sd_clock_disable(sdmmc); + _sdmmc_get_clkcon(sdmmc); + } + + u32 clock; + u16 divisor; + clock_sdmmc_get_params(&clock, &divisor, type); + clock_sdmmc_enable(id, clock); + + sdmmc->clock_stopped = 0; + + //TODO: make this skip-able. + sdmmc->regs->iospare |= 0x80000; + sdmmc->regs->veniotrimctl &= 0xFFFFFFFB; + static const u32 trim_values[] = { 2, 8, 3, 8 }; + sdmmc->regs->venclkctl = (sdmmc->regs->venclkctl & 0xE0FFFFFF) | (trim_values[sdmmc->id] << 24); + sdmmc->regs->sdmemcmppadctl = (sdmmc->regs->sdmemcmppadctl & 0xF) | 7; + if (!_sdmmc_autocal_config_offset(sdmmc, power)) + return 0; + _sdmmc_autocal_execute(sdmmc, power); + if (_sdmmc_enable_internal_clock(sdmmc)) + { + sdmmc_set_bus_width(sdmmc, bus_width); + _sdmmc_set_voltage(sdmmc, power); + if (sdmmc_setup_clock(sdmmc, type)) + { + sdmmc_sd_clock_ctrl(sdmmc, no_sd); + _sdmmc_sd_clock_enable(sdmmc); + _sdmmc_get_clkcon(sdmmc); + return 1; + } + return 0; + } + return 0; +} + +void sdmmc_end(sdmmc_t *sdmmc) +{ + if (!sdmmc->clock_stopped) + { + _sdmmc_sd_clock_disable(sdmmc); + // Disable SDMMC power. + _sdmmc_set_voltage(sdmmc, SDMMC_POWER_OFF); + + // Disable SD card power. + if (sdmmc->id == SDMMC_1) + { + gpio_output_enable(GPIO_PORT_E, GPIO_PIN_4, GPIO_OUTPUT_DISABLE); + //max77620_regulator_enable(REGULATOR_LDO2, 0); + msleep(100); // To power cycle min 1ms without power is needed. + } + + _sdmmc_get_clkcon(sdmmc); + clock_sdmmc_disable(sdmmc->id); + sdmmc->clock_stopped = 1; + } +} + +void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy) +{ + cmdbuf->cmd = cmd; + cmdbuf->arg = arg; + cmdbuf->rsp_type = rsp_type; + cmdbuf->check_busy = check_busy; +} + +int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out) +{ + if (!sdmmc->sd_clock_enabled) + return 0; + + //Recalibrate periodically for SDMMC1. + if (sdmmc->id == SDMMC_1 && sdmmc->no_sd) + _sdmmc_autocal_execute(sdmmc, sdmmc_get_voltage(sdmmc)); + + int should_disable_sd_clock = 0; + if (!(sdmmc->regs->clkcon & TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE)) + { + should_disable_sd_clock = 1; + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + } + + int res = _sdmmc_execute_cmd_inner(sdmmc, cmd, req, blkcnt_out); + usleep((8000 + sdmmc->divisor - 1) / sdmmc->divisor); + if (should_disable_sd_clock) + sdmmc->regs->clkcon &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + + return res; +} + +int sdmmc_enable_low_voltage(sdmmc_t *sdmmc) +{ + if(sdmmc->id != SDMMC_1) + return 0; + + if (!sdmmc_setup_clock(sdmmc, 8)) + return 0; + + _sdmmc_get_clkcon(sdmmc); + + max77620_regulator_set_voltage(REGULATOR_LDO2, 1800000); + PMC(APBDEV_PMC_PWR_DET_VAL) &= ~(1 << 12); + + _sdmmc_autocal_config_offset(sdmmc, SDMMC_POWER_1_8); + _sdmmc_autocal_execute(sdmmc, SDMMC_POWER_1_8); + _sdmmc_set_voltage(sdmmc, SDMMC_POWER_1_8); + _sdmmc_get_clkcon(sdmmc); + msleep(5); + + if (sdmmc->regs->hostctl2 & SDHCI_CTRL_VDD_180) + { + sdmmc->regs->clkcon |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + _sdmmc_get_clkcon(sdmmc); + msleep(1); + if ((sdmmc->regs->prnsts & 0xF00000) == 0xF00000) + return 1; + } + + return 0; +} diff --git a/emummc/source/emmc/sdmmc_driver.h b/emummc/source/emmc/sdmmc_driver.h new file mode 100644 index 000000000..b2820e7e2 --- /dev/null +++ b/emummc/source/emmc/sdmmc_driver.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _SDMMC_DRIVER_H_ +#define _SDMMC_DRIVER_H_ + +#include "../utils/types.h" +#include "sdmmc_t210.h" + +/*! SDMMC controller IDs. */ +#define SDMMC_1 0 +#define SDMMC_2 1 +#define SDMMC_3 2 +#define SDMMC_4 3 + +/*! SDMMC power types. */ +#define SDMMC_POWER_OFF 0 +#define SDMMC_POWER_1_8 1 +#define SDMMC_POWER_3_3 2 + +/*! SDMMC bus widths. */ +#define SDMMC_BUS_WIDTH_1 0 +#define SDMMC_BUS_WIDTH_4 1 +#define SDMMC_BUS_WIDTH_8 2 + +/*! SDMMC response types. */ +#define SDMMC_RSP_TYPE_0 0 +#define SDMMC_RSP_TYPE_1 1 +#define SDMMC_RSP_TYPE_2 2 +#define SDMMC_RSP_TYPE_3 3 +#define SDMMC_RSP_TYPE_4 4 +#define SDMMC_RSP_TYPE_5 5 + +/*! SDMMC mask interrupt status. */ +#define SDMMC_MASKINT_MASKED 0 +#define SDMMC_MASKINT_NOERROR -1 +#define SDMMC_MASKINT_ERROR -2 + +/*! SDMMC host control 2 */ +#define SDHCI_CTRL_UHS_MASK 0xFFF8 +#define SDHCI_CTRL_VDD_330 0xFFF7 +#define SDHCI_CTRL_VDD_180 8 +#define SDHCI_CTRL_EXEC_TUNING 0x40 +#define SDHCI_CTRL_TUNED_CLK 0x80 +#define SDHCI_HOST_VERSION_4_EN 0x1000 +#define SDHCI_ADDRESSING_64BIT_EN 0x2000 +#define SDHCI_CTRL_PRESET_VAL_EN 0x8000 + +/*! SD bus speeds. */ +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 +#define HS400_BUS_SPEED 5 + +/*! Helper for SWITCH command argument. */ +#define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8)) + +/*! SDMMC controller context. */ +typedef struct _sdmmc_t +{ + t210_sdmmc_t *regs; + u32 id; + u32 divisor; + u32 clock_stopped; + int no_sd; + int sd_clock_enabled; + int venclkctl_set; + u32 venclkctl_tap; + u32 expected_rsp_type; + u64 dma_addr_fs; + u64 dma_addr_next; + u32 rsp[4]; + u32 rsp3; +} sdmmc_t; + +/*! SDMMC command. */ +typedef struct _sdmmc_cmd_t +{ + u16 cmd; + u32 arg; + u32 rsp_type; + u32 check_busy; +} sdmmc_cmd_t; + +/*! SDMMC request. */ +typedef struct _sdmmc_req_t +{ + void *buf; + u32 blksize; + u32 num_sectors; + int is_write; + int is_multi_block; + int is_auto_cmd12; +} sdmmc_req_t; + +int sdmmc_get_voltage(sdmmc_t *sdmmc); +u32 sdmmc_get_bus_width(sdmmc_t *sdmmc); +void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width); +void sdmmc_get_venclkctl(sdmmc_t *sdmmc); +int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type); +void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd); +int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type); +int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd); +int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp); +int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd); +void sdmmc_end(sdmmc_t *sdmmc); +void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy); +int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out); +int sdmmc_enable_low_voltage(sdmmc_t *sdmmc); + +#endif diff --git a/emummc/source/emmc/sdmmc_t210.h b/emummc/source/emmc/sdmmc_t210.h new file mode 100644 index 000000000..e11c3ff9c --- /dev/null +++ b/emummc/source/emmc/sdmmc_t210.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _SDMMC_T210_H_ +#define _SDMMC_T210_H_ + +#include "../utils/types.h" + +#define TEGRA_MMC_PWRCTL_SD_BUS_POWER 0x1 +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 0xA +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 0xC +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 0xE +#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_MASK 0xF1 + +#define TEGRA_MMC_HOSTCTL_1BIT 0x00 +#define TEGRA_MMC_HOSTCTL_4BIT 0x02 +#define TEGRA_MMC_HOSTCTL_8BIT 0x20 + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE 0x1 +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE 0x2 +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE 0x4 +#define TEGRA_MMC_CLKCON_CLKGEN_SELECT 0x20 + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL 0x1 +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE 0x2 +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE 0x4 + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE 0x1 +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE 0x2 +#define TEGRA_MMC_TRNMOD_AUTO_CMD12 0x4 +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE 0x0 +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ 0x10 +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT 0x20 + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK 0x8 +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK 0x10 +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER 0x20 + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK 0x3 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE 0x0 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 0x1 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 0x2 +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY 0x3 + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE 0x1 +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE 0x2 +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT 0x8 +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT 0x8000 +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT 0x10000 + +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY 0x20 + +typedef struct _t210_sdmmc_t +{ + vu32 sysad; + vu16 blksize; + vu16 blkcnt; + vu32 argument; + vu16 trnmod; + vu16 cmdreg; + vu32 rspreg0; + vu32 rspreg1; + vu32 rspreg2; + vu32 rspreg3; + vu32 bdata; + vu32 prnsts; + vu8 hostctl; + vu8 pwrcon; + vu8 blkgap; + vu8 wakcon; + vu16 clkcon; + vu8 timeoutcon; + vu8 swrst; + vu16 norintsts; + vu16 errintsts; + vu16 norintstsen; + vu16 errintstsen; + vu16 norintsigen; + vu16 errintsigen; + vu16 acmd12errsts; + vu16 hostctl2; + vu32 capareg; + vu32 capareg_1; + vu32 maxcurr; + vu8 res3[4]; + vu16 setacmd12err; + vu16 setinterr; + vu8 admaerr; + vu8 res4[3]; + vu32 admaaddr; + vu32 admaaddr_hi; + vu8 res5[156]; + vu16 slotintstatus; + vu16 hcver; + vu32 venclkctl; + vu32 venspictl; + vu32 venspiintsts; + vu32 venceatactl; + vu32 venbootctl; + vu32 venbootacktout; + vu32 venbootdattout; + vu32 vendebouncecnt; + vu32 venmiscctl; + vu32 res6[34]; + vu32 veniotrimctl; + vu32 vendllcal; + vu8 res7[8]; + vu32 dllcfgstatus; + vu32 ventunctl0; + vu32 field_1C4; + vu8 field_1C8[24]; + vu32 sdmemcmppadctl; + vu32 autocalcfg; + vu32 autocalintval; + vu32 autocalsts; + vu32 iospare; +} t210_sdmmc_t; + +#endif diff --git a/emummc/source/emuMMC/emummc.c b/emummc/source/emuMMC/emummc.c new file mode 100644 index 000000000..9f0d5a1d7 --- /dev/null +++ b/emummc/source/emuMMC/emummc.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include + +#include "../soc/gpio.h" +#include "../utils/fatal.h" +#include "../libs/fatfs/diskio.h" +#include "emummc.h" +#include "emummc_ctx.h" + +static bool storageMMCinitialized = false; +static bool storageSDinitialized = false; + +// hekate sdmmmc vars +sdmmc_t sdmmc; +sdmmc_storage_t storage; +sdmmc_t sd_sdmmc; +sdmmc_storage_t sd_storage; + +// init vars +bool custom_driver = true; +extern const volatile emuMMC_ctx_t emuMMC_ctx; + +// FS funcs +_sdmmc_accessor_gc sdmmc_accessor_gc; +_sdmmc_accessor_sd sdmmc_accessor_sd; +_sdmmc_accessor_nand sdmmc_accessor_nand; +_lock_mutex lock_mutex; +_unlock_mutex unlock_mutex; + +// FS misc +void *sd_mutex; +void *nand_mutex; +volatile int *active_partition; +volatile Handle *sdmmc_das_handle; + +// FatFS +static bool fat_mounted = false; +static file_based_ctxt f_emu; + +static void _sdmmc_ensure_device_attached(void) +{ + // This ensures that the sd device address space handle is always attached, + // even if FS hasn't attached it + static bool did_attach = false; + if (!did_attach) + { + svcAttachDeviceAddressSpace(DeviceName_SDMMC1A, *sdmmc_das_handle); + did_attach = true; + } +} + +static void _sdmmc_ensure_initialized(void) +{ + // The boot sysmodule will eventually kill power to SD. Detect this, and reinitialize when it happens. + static bool init_done = false; + if (!init_done) + { + if (gpio_read(GPIO_PORT_E, GPIO_PIN_4) == 0) + { + sdmmc_finalize(); + sdmmc_initialize(); + init_done = true; + } + } +} + +void sdmmc_finalize(void) +{ + if (!sdmmc_storage_end(&sd_storage)) + { + fatal_abort(Fatal_InitSD); + } + storageSDinitialized = false; +} + +static void _file_based_update_filename(char *outFilename, u32 sd_path_len, u32 part_idx) +{ + if (part_idx < 10) + { + outFilename[sd_path_len] = '0'; + itoa(part_idx, &outFilename[sd_path_len + 1], 10); + } + else + { + itoa(part_idx, &outFilename[sd_path_len], 10); + } +} + +static void _file_based_emmc_finalize(void) +{ + if ((emuMMC_ctx.EMMC_Type == emuMMC_SD_File) && fat_mounted) + { + // Close all open handles. + f_close(f_emu.fp_boot0); + f_close(f_emu.fp_boot1); + + for (int i = 0; i < f_emu.parts; i++) + f_close(f_emu.fp_gpp[i]); + + // Force unmount FAT volume. + f_mount(NULL, "", 1); + + fat_mounted = false; + } +} + +static void _file_based_emmc_initialize(void) +{ + char path[sizeof(emuMMC_ctx.storagePath) + 0x20]; + memset(&path, 0, sizeof(path)); + memset(&f_emu, 0, sizeof(file_based_ctxt)); + + memcpy(path, (void *)emuMMC_ctx.storagePath, sizeof(emuMMC_ctx.storagePath)); + strcat(path, "/eMMC"); + int path_len = strlen(path); + + // Open BOOT0 physical partition. + f_emu.fp_boot0 = (FIL *)malloc(sizeof(FIL)); + memcpy(path + path_len, "BOOT0", 6); + if (f_open(f_emu.fp_boot0, path, FA_READ | FA_WRITE) != FR_OK) + fatal_abort(Fatal_InitSD); + + // Open BOOT1 physical partition. + f_emu.fp_boot1 = (FIL *)malloc(sizeof(FIL)); + memcpy(path + path_len, "BOOT1", 6); + if (f_open(f_emu.fp_boot1, path, FA_READ | FA_WRITE) != FR_OK) + fatal_abort(Fatal_InitSD); + + // Open handles for GPP physical partition files. + _file_based_update_filename(path, path_len, 00); + if (f_open(f_emu.fp_gpp[0], path, FA_READ | FA_WRITE) != FR_OK) + fatal_abort(Fatal_InitSD); + + f_emu.part_size = f_size(f_emu.fp_gpp[0]); + + // Iterate folder for split parts and stop if next doesn't exist. + // Supports up to 32 parts of any size. + // TODO: decide on max parts and define them. (hekate produces up to 30 parts on 1GB mode.) + for (f_emu.parts = 1; f_emu.parts < 32; f_emu.parts++) + { + f_emu.fp_gpp[f_emu.parts] = (FIL *)malloc(sizeof(FIL)); + _file_based_update_filename(path, path_len, f_emu.parts); + + if (f_open(f_emu.fp_gpp[f_emu.parts], path, FA_READ | FA_WRITE) != FR_OK) + { + free(f_emu.fp_gpp[f_emu.parts]); + + // Check if single file. + if (f_emu.parts == 1) + f_emu.parts = 0; + + return; + } + } +} + +bool sdmmc_initialize(void) +{ + if (!storageMMCinitialized) + { + if (sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + if (sdmmc_storage_set_mmc_partition(&storage, FS_EMMC_PARTITION_GPP)) + storageMMCinitialized = true; + } + else + { + fatal_abort(Fatal_InitMMC); + } + } + + if (!storageSDinitialized) + { + if (sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11)) + { + storageSDinitialized = true; + + // File based emummc. + if ((emuMMC_ctx.EMMC_Type == emuMMC_SD_File) && !fat_mounted) + { + f_emu.sd_fs = (FATFS *)malloc(sizeof(FATFS)); + if (f_mount(f_emu.sd_fs, "", 1) != FR_OK) + fatal_abort(Fatal_InitSD); + else + fat_mounted = true; + + _file_based_emmc_initialize(); + } + } + else + { + fatal_abort(Fatal_InitSD); + } + } + + return storageMMCinitialized && storageSDinitialized; +} + +// FS DMA calculations. +intptr_t sdmmc_calculate_dma_addr(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors) +{ + int dma_buf_idx = 0; + char *_buf = (char *)buf; + char *actual_buf_start = _buf; + char *actual_buf_end = &_buf[512 * num_sectors]; + char *dma_buffer_start = _this->parent->dmaBuffers[FS_SDMMC_EMMC].device_addr_buffer; + + if (dma_buffer_start <= _buf && actual_buf_end <= &dma_buffer_start[_this->parent->dmaBuffers[FS_SDMMC_EMMC].device_addr_buffer_size]) + { + dma_buf_idx = FS_SDMMC_EMMC; + } + else + { + dma_buffer_start = _this->parent->dmaBuffers[FS_SDMMC_SD].device_addr_buffer; + if (dma_buffer_start <= actual_buf_start && actual_buf_end <= &dma_buffer_start[_this->parent->dmaBuffers[FS_SDMMC_SD].device_addr_buffer_size]) + { + dma_buf_idx = FS_SDMMC_SD; + } + else + { + dma_buffer_start = _this->parent->dmaBuffers[FS_SDMMC_GC].device_addr_buffer; + dma_buf_idx = FS_SDMMC_GC; + } + } + + intptr_t admaaddr = (intptr_t)&_this->parent->dmaBuffers[dma_buf_idx].device_addr_buffer_masked[actual_buf_start - dma_buffer_start]; + + return admaaddr; +} + +sdmmc_accessor_t *sdmmc_accessor_get(int mmc_id) +{ + sdmmc_accessor_t *_this; + switch (mmc_id) + { + case FS_SDMMC_EMMC: + _this = sdmmc_accessor_nand(); + break; + case FS_SDMMC_SD: + _this = sdmmc_accessor_sd(); + break; + case FS_SDMMC_GC: + _this = sdmmc_accessor_gc(); + break; + default: + fatal_abort(Fatal_InvalidAccessor); + } + + return _this; +} + +void mutex_lock_handler(int mmc_id) +{ + lock_mutex(sd_mutex); + lock_mutex(nand_mutex); +} + +void mutex_unlock_handler(int mmc_id) +{ + unlock_mutex(nand_mutex); + unlock_mutex(sd_mutex); +} + +int sdmmc_nand_get_active_partition_index() +{ + switch (*active_partition) + { + case FS_EMMC_PARTITION_GPP: + return 2; + case FS_EMMC_PARTITION_BOOT1: + return 1; + case FS_EMMC_PARTITION_BOOT0: + return 0; + } + + fatal_abort(Fatal_InvalidAccessor); +} + +static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned int num_sectors, bool is_write) +{ + if ((emuMMC_ctx.EMMC_Type == emuMMC_SD)) + { + // raw partition sector offset: emuMMC_ctx.EMMC_StoragePartitionOffset. + sector += emuMMC_ctx.EMMC_StoragePartitionOffset; + // Set physical partition offset. + sector += (sdmmc_nand_get_active_partition_index() * BOOT_PARTITION_SIZE); + if (!is_write) + return sdmmc_storage_read(&sd_storage, sector, num_sectors, buf); + else + return sdmmc_storage_write(&sd_storage, sector, num_sectors, buf); + } + + // File based emummc. + FIL *fp_tmp = NULL; + switch (*active_partition) + { + case FS_EMMC_PARTITION_GPP: + if (f_emu.parts) + { + fp_tmp = f_emu.fp_gpp[sector / f_emu.part_size]; + sector = sector % f_emu.part_size; + } + else + { + fp_tmp = f_emu.fp_gpp[0]; + } + break; + case FS_EMMC_PARTITION_BOOT1: + fp_tmp = f_emu.fp_boot1; + break; + case FS_EMMC_PARTITION_BOOT0: + fp_tmp = f_emu.fp_boot0; + break; + } + + if (f_lseek(fp_tmp, sector << 9) != FR_OK) + { + ; //TODO. Out of range. close stuff and fatal? + } + + if (!is_write) + return !(f_read(fp_tmp, buf, num_sectors << 9, NULL)); + else + return !(f_write(fp_tmp, buf, num_sectors << 9, NULL)); +} + +// FS read wrapper. +uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned int sector, unsigned int num_sectors) +{ + sdmmc_accessor_t *_this; + uint64_t read_res; + + _this = sdmmc_accessor_get(mmc_id); + + if (_this != NULL) + { + if (mmc_id == FS_SDMMC_EMMC || mmc_id == FS_SDMMC_SD) + { + mutex_lock_handler(mmc_id); + // Make sure we're attached to the device address space. + _sdmmc_ensure_device_attached(); + // Make sure we're still initialized if boot killed sd card power. + _sdmmc_ensure_initialized(); + } + + if (mmc_id == FS_SDMMC_EMMC) + { + sd_storage.sdmmc->dma_addr_fs = (u64)sdmmc_calculate_dma_addr(_this, buf, num_sectors); + + // Call hekates driver. + if (emummc_read_write_inner(buf, sector, num_sectors, false)) + { + mutex_unlock_handler(mmc_id); + return 0; + } + + mutex_unlock_handler(mmc_id); + return FS_READ_WRITE_ERROR; + } + + if (mmc_id == FS_SDMMC_SD) + { + sd_storage.sdmmc->dma_addr_fs = (u64)sdmmc_calculate_dma_addr(_this, buf, num_sectors); + + // Call hekates driver. + if (sdmmc_storage_read(&sd_storage, sector, num_sectors, buf)) + { + mutex_unlock_handler(mmc_id); + return 0; + } + + mutex_unlock_handler(mmc_id); + return FS_READ_WRITE_ERROR; + } + + read_res = _this->vtab->read_write(_this, sector, num_sectors, buf, bufSize, 1); + return read_res; + } + + fatal_abort(Fatal_ReadNoAccessor); +} + +// FS write wrapper. +uint64_t sdmmc_wrapper_write(int mmc_id, unsigned int sector, unsigned int num_sectors, void *buf, uint64_t bufSize) +{ + sdmmc_accessor_t *_this; + uint64_t write_res; + + _this = sdmmc_accessor_get(mmc_id); + + if (_this != NULL) + { + if (mmc_id == FS_SDMMC_EMMC) + { + mutex_lock_handler(mmc_id); + + sd_storage.sdmmc->dma_addr_fs = (u64)sdmmc_calculate_dma_addr(_this, buf, num_sectors); + + // Call hekates driver. + if (emummc_read_write_inner(buf, sector, num_sectors, true)) + { + mutex_unlock_handler(mmc_id); + return 0; + } + + mutex_unlock_handler(mmc_id); + return FS_READ_WRITE_ERROR; + } + + if (mmc_id == FS_SDMMC_SD) + { + mutex_lock_handler(mmc_id); + + sector += 0; + sd_storage.sdmmc->dma_addr_fs = (u64)sdmmc_calculate_dma_addr(_this, buf, num_sectors); + + // Call hekates driver. + if (sdmmc_storage_write(&sd_storage, sector, num_sectors, buf)) + { + mutex_unlock_handler(mmc_id); + return 0; + } + + mutex_unlock_handler(mmc_id); + return FS_READ_WRITE_ERROR; + } + + write_res = _this->vtab->read_write(_this, sector, num_sectors, buf, bufSize, 0); + return write_res; + } + + fatal_abort(Fatal_WriteNoAccessor); +} diff --git a/emummc/source/emuMMC/emummc.h b/emummc/source/emuMMC/emummc.h new file mode 100644 index 000000000..d6f8c8c5e --- /dev/null +++ b/emummc/source/emuMMC/emummc.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __EMUMMC_H__ +#define __EMUMMC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include "../emmc/sdmmc.h" +#include "../soc/i2c.h" +#include "../soc/gpio.h" +#include "../utils/util.h" +#include "../FS/FS.h" +#include "../libs/fatfs/ff.h" + +// FS typedefs +typedef sdmmc_accessor_t *(*_sdmmc_accessor_gc)(); +typedef sdmmc_accessor_t *(*_sdmmc_accessor_sd)(); +typedef sdmmc_accessor_t *(*_sdmmc_accessor_nand)(); +typedef void (*_lock_mutex)(void *mtx); +typedef void (*_unlock_mutex)(void *mtx); + +bool sdmmc_initialize(void); +void sdmmc_finalize(void); +int sdmmc_nand_get_active_partition_index(); +sdmmc_accessor_t *sdmmc_accessor_get(int mmc_id); + +void mutex_lock_handler(int mmc_id); +void mutex_unlock_handler(int mmc_id); + +intptr_t sdmmc_calculate_dma_addr(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors); + +uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned int sector, unsigned int num_sectors); +uint64_t sdmmc_wrapper_write(int mmc_id, unsigned int sector, unsigned int num_sectors, void *buf, uint64_t bufSize); + +// TODO: check if FatFS internal buffers are good (perf wise) to have a x16 alignment. +typedef struct _file_based_ctxt +{ + uint64_t parts; + uint64_t part_size; + FATFS *sd_fs; + FIL *fp_boot0; + FIL *fp_boot1; + FIL *fp_gpp[32]; +} file_based_ctxt; + +#ifdef __cplusplus +} +#endif + +#endif /* __EMUMMC_H__ */ diff --git a/emummc/source/emuMMC/emummc_ctx.h b/emummc/source/emuMMC/emummc_ctx.h new file mode 100644 index 000000000..0bf404a3c --- /dev/null +++ b/emummc/source/emuMMC/emummc_ctx.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef __EMUMMC_CTX_H__ +#define __EMUMMC_CTX_H__ + +#include "../utils/types.h" +#include "../FS/FS_versions.h" + +#define EMUMMC_STORAGE_MAGIC 0x30534645 /* EFS0, EmuFS0 */ +#define EMUMMC_MAX_DIR_LENGTH 0x7F + +enum emuMMC_Type +{ + // EMMC Device raw + emuMMC_EMMC = 0, + + // SD Device raw + emuMMC_SD, + // SD Device File + emuMMC_SD_File, + + emuMMC_MAX +}; + +typedef struct _emuMMC_ctx_t +{ + u32 magic; + u32 id; + enum FS_VER fs_ver; + enum emuMMC_Type EMMC_Type; + enum emuMMC_Type SD_Type; + + /* Partition based */ + u64 EMMC_StoragePartitionOffset; + u64 SD_StoragePartitionOffset; + + /* File-Based */ + char storagePath[EMUMMC_MAX_DIR_LENGTH+1]; +} emuMMC_ctx_t, *PemuMMC_ctx_t; + +#endif /* __EMUMMC_CTX_H__ */ diff --git a/emummc/source/libs/fatfs/diskio.c b/emummc/source/libs/fatfs/diskio.c new file mode 100644 index 000000000..d800d2182 --- /dev/null +++ b/emummc/source/libs/fatfs/diskio.c @@ -0,0 +1,72 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */ +/*-----------------------------------------------------------------------*/ +/* If a working storage control module is available, it should be */ +/* attached to the FatFs via a glue function rather than modifying it. */ +/* This is an example of glue functions to attach various exsisting */ +/* storage control modules to the FatFs module with a defined API. */ +/*-----------------------------------------------------------------------*/ + +#include +#include "diskio.h" /* FatFs lower layer API */ +#include "../../emmc/sdmmc.h" + +extern sdmmc_storage_t sd_storage; + +/*-----------------------------------------------------------------------*/ +/* Get Drive Status */ +/*-----------------------------------------------------------------------*/ +DSTATUS disk_status ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ +/*-----------------------------------------------------------------------*/ +DSTATUS disk_initialize ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ +DRESULT disk_read ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to read */ +) +{ + return sdmmc_storage_read(&sd_storage, sector, count, buff) ? RES_OK : RES_ERROR; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ +DRESULT disk_write ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Start sector in LBA */ + UINT count /* Number of sectors to write */ +) +{ + return sdmmc_storage_write(&sd_storage, sector, count, (void *)buff) ? RES_OK : RES_ERROR; +} + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ +DRESULT disk_ioctl ( + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + return RES_OK; +} diff --git a/emummc/source/libs/fatfs/diskio.h b/emummc/source/libs/fatfs/diskio.h new file mode 100644 index 000000000..d6ae8f83a --- /dev/null +++ b/emummc/source/libs/fatfs/diskio.h @@ -0,0 +1,79 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../utils/types.h" + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ +#define ISDIO_READ 55 /* Read data form SD iSDIO register */ +#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ +#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/emummc/source/libs/fatfs/ff.c b/emummc/source/libs/fatfs/ff.c new file mode 100644 index 000000000..32304742f --- /dev/null +++ b/emummc/source/libs/fatfs/ff.c @@ -0,0 +1,6623 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem Module R0.13c (p3) / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2018, ChaN, all right reserved. +/ Copyright (c) 2018 naehrwert +/ Copyright (C) 2018-2019 CTCaer +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + +#define EFSPRINTF(text, ...) + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if FF_DEFINED != 86604 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Limits and boundaries */ +#define MAX_DIR 0x200000 /* Max size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */ +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ + + +/* Character code support macros */ +#define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') +#define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) +#define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) +#define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) + + +/* Additional file access control and file status flags for internal use */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Additional file attribute bits for internal use */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Name status flags in fn[11] */ +#define NSFLAG 11 /* Index of the name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* exFAT directory entry types */ +#define ET_BITMAP 0x81 /* Allocation bitmap */ +#define ET_UPCASE 0x82 /* Up-case table */ +#define ET_VLABEL 0x83 /* Volume label */ +#define ET_FILEDIR 0x85 /* File and directory */ +#define ET_STREAM 0xC0 /* Stream extension */ +#define ET_FILENAME 0xC1 /* Name extension */ + + +/* FatFs refers the FAT structure as simple byte array instead of structure member +/ because the C structure is not binary compatible between different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* WindowsNT error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* Filesystem type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ +#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */ +#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + + +/* Post process on fatal error in the file operations */ +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Re-entrancy related */ +#if FF_FS_REENTRANT +#if FF_USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define LEAVE_FF(fs, res) return res +#endif + + +/* Definitions of volume - physical location conversion */ +#if FF_MULTI_PARTITION +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#else +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#endif + + +/* Definitions of sector size */ +#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if FF_MAX_SS == FF_MIN_SS +#define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if FF_FS_NORTC == 1 +#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31 +#error Invalid FF_FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if FF_FS_LOCK != 0 +#if FF_FS_READONLY +#error FF_FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, containing directory (0:root) */ + DWORD ofs; /* Object ID 3, offset in the directory */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* SBCS up-case tables (\x80-\xFF) */ +#define TBL_CT437 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT720 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT737 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT771 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT775 {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT852 {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} +#define TBL_CT855 {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT862 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT864 {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT865 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT866 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} +#define TBL_CT869 {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + + +/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */ +#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00} +#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00} +#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE} +#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00} + + +/* Macros for table definitions */ +#define MERGE_2STR(a, b) a ## b +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ +/* Remark: Variables defined here without initial value shall be guaranteed +/ zero/null at start-up. If not, the linker option or start-up routine is +/ not compliance with C standard. */ + +/*--------------------------------*/ +/* File/Volume controls */ +/*--------------------------------*/ + +#if FF_VOLUMES < 1 || FF_VOLUMES > 10 +#error Wrong FF_VOLUMES setting +#endif +static FATFS* FatFs[FF_VOLUMES]; /* Pointer to the filesystem objects (logical drives) */ +static WORD Fsid; /* Filesystem mount ID */ + +#if FF_FS_RPATH != 0 +static BYTE CurrVol; /* Current drive */ +#endif + +#if FF_FS_LOCK != 0 +static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if FF_STR_VOLUME_ID +#ifdef FF_VOLUME_STRS +static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS}; /* Pre-defined volume ID */ +#endif +#endif + + +/*--------------------------------*/ +/* LFN/Directory working buffer */ +/*--------------------------------*/ + +#if FF_USE_LFN == 0 /* Non-LFN configuration */ +#if FF_FS_EXFAT +#error LFN must be enabled when enable exFAT +#endif +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#else /* LFN configurations */ +#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255 +#error Wrong setting of FF_MAX_LFN +#endif +#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12 +#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF +#endif +#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3 +#error Wrong setting of FF_LFN_UNICODE +#endif +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* FAT: Offset of LFN characters in the directory entry */ +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */ + +#if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ +#if FF_FS_EXFAT +static BYTE DirBuf[MAXDIRB(FF_MAX_LFN)]; /* Directory entry block scratchpad buffer */ +#endif +static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)]; /* LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN+1]; /* LFN working buffer */ +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif +#define LEAVE_MKFS(res) return res + +#elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if FF_FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer and directory entry block scratchpad buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; /* Pointer to LFN working buffer */ +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif +#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } +#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ + +#else +#error Wrong setting of FF_USE_LFN + +#endif /* FF_USE_LFN == 1 */ +#endif /* FF_USE_LFN == 0 */ + + + +/*--------------------------------*/ +/* Code conversion tables */ +/*--------------------------------*/ + +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ +#define CODEPAGE CodePage +static WORD CodePage; /* Current code page */ +static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ + +static const BYTE Ct437[] = TBL_CT437; +static const BYTE Ct720[] = TBL_CT720; +static const BYTE Ct737[] = TBL_CT737; +static const BYTE Ct771[] = TBL_CT771; +static const BYTE Ct775[] = TBL_CT775; +static const BYTE Ct850[] = TBL_CT850; +static const BYTE Ct852[] = TBL_CT852; +static const BYTE Ct855[] = TBL_CT855; +static const BYTE Ct857[] = TBL_CT857; +static const BYTE Ct860[] = TBL_CT860; +static const BYTE Ct861[] = TBL_CT861; +static const BYTE Ct862[] = TBL_CT862; +static const BYTE Ct863[] = TBL_CT863; +static const BYTE Ct864[] = TBL_CT864; +static const BYTE Ct865[] = TBL_CT865; +static const BYTE Ct866[] = TBL_CT866; +static const BYTE Ct869[] = TBL_CT869; +static const BYTE Dc932[] = TBL_DC932; +static const BYTE Dc936[] = TBL_DC936; +static const BYTE Dc949[] = TBL_DC949; +static const BYTE Dc950[] = TBL_DC950; + +#elif FF_CODE_PAGE < 900 /* Static code page configuration (SBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE); + +#else /* Static code page configuration (DBCS) */ +#define CODEPAGE FF_CODE_PAGE +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if FF_FS_EXFAT +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !FF_FS_READONLY +static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if FF_FS_EXFAT +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !FF_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static void mem_cpy (void* dst, const void* src, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt != 0) { + do { + *d++ = *s++; + } while (--cnt); + } +} + + +/* Fill memory block */ +static void mem_set (void* dst, int val, UINT cnt) +{ + BYTE *d = (BYTE*)dst; + + do { + *d++ = (BYTE)val; + } while (--cnt); +} + + +/* Compare memory block */ +static int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:different */ +{ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + + +/* Check if chr is contained in the string */ +static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */ +{ + while (*str && *str != chr) str++; + return *str; +} + + +/* Test if the character is DBC 1st byte */ +static int dbc_1st (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; /* 1st byte range 1 */ + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[0]) { + if (c <= DbcTbl[1]) return 1; + if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +/* Test if the character is DBC 2nd byte */ +static int dbc_2nd (BYTE c) +{ +#if FF_CODE_PAGE == 0 /* Variable code page */ + if (DbcTbl && c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; /* 2nd byte range 1 */ + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */ + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */ + } +#elif FF_CODE_PAGE >= 900 /* DBCS fixed code page */ + if (c >= DbcTbl[4]) { + if (c <= DbcTbl[5]) return 1; + if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; + if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; + } +#else /* SBCS fixed code page */ + if (c != 0) return 0; /* Always false */ +#endif + return 0; +} + + +#if FF_USE_LFN + +/* Get a character from TCHAR string in defined API encodeing */ +static DWORD tchar2uni ( /* Returns character in UTF-16 encoding (>=0x10000 on double encoding unit, 0xFFFFFFFF on decode error) */ + const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ +) +{ + DWORD uc; + const TCHAR *p = *str; + +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + WCHAR wc; + + uc = *p++; /* Get a unit */ + if (IsSurrogate(uc)) { /* Surrogate? */ + wc = *p++; /* Get low surrogate */ + if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF; /* Wrong surrogate? */ + uc = uc << 16 | wc; + } + +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + BYTE b; + int nf; + + uc = (BYTE)*p++; /* Get a unit */ + if (uc & 0x80) { /* Multiple byte code? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + uc &= 0x1F; nf = 1; + } else { + if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else { + if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; + } + } + } + do { /* Get trailing bytes */ + b = (BYTE)*p++; + if ((b & 0xC0) != 0x80) return 0xFFFFFFFF; /* Wrong sequence? */ + uc = uc << 6 | (b & 0x3F); + } while (--nf != 0); + if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + } + +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ + uc = (TCHAR)*p++; /* Get a unit */ + if (uc >= 0x110000) return 0xFFFFFFFF; /* Wrong code? */ + if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ + +#else /* ANSI/OEM input */ + BYTE b; + WCHAR wc; + + wc = (BYTE)*p++; /* Get a byte */ + if (dbc_1st((BYTE)wc)) { /* Is it a DBC 1st byte? */ + b = (BYTE)*p++; /* Get 2nd byte */ + if (!dbc_2nd(b)) return 0xFFFFFFFF; /* Invalid code? */ + wc = (wc << 8) + b; /* Make a DBC */ + } + if (wc != 0) { + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM ==> Unicode */ + if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */ + } + uc = wc; + +#endif + *str = p; /* Next read pointer */ + return uc; +} + + +/* Output a TCHAR string in defined API encoding */ +static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ + DWORD chr, /* UTF-16 encoded character (Double encoding unit char if >=0x10000) */ + TCHAR* buf, /* Output buffer */ + UINT szb /* Size of the buffer */ +) +{ +#if FF_LFN_UNICODE == 1 /* UTF-16 output */ + WCHAR hs, wc; + + hs = (WCHAR)(chr >> 16); + wc = (WCHAR)chr; + if (hs == 0) { /* Single encoding unit? */ + if (szb < 1 || IsSurrogate(wc)) return 0; /* Buffer overflow or wrong code? */ + *buf = wc; + return 1; + } + if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0; /* Buffer overflow or wrong surrogate? */ + *buf++ = hs; + *buf++ = wc; + return 2; + +#elif FF_LFN_UNICODE == 2 /* UTF-8 output */ + DWORD hc; + + if (chr < 0x80) { /* Single byte code? */ + if (szb < 1) return 0; /* Buffer overflow? */ + *buf = (TCHAR)chr; + return 1; + } + if (chr < 0x800) { /* 2-byte sequence? */ + if (szb < 2) return 0; /* Buffer overflow? */ + *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 2; + } + if (chr < 0x10000) { /* 3-byte sequence? */ + if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ + *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 3; + } + /* 4-byte sequence */ + if (szb < 4) return 0; /* Buffer overflow? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + return 4; + +#elif FF_LFN_UNICODE == 3 /* UTF-32 output */ + DWORD hc; + + if (szb < 1) return 0; /* Buffer overflow? */ + if (chr >= 0x10000) { /* Out of BMP? */ + hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6; /* Get high 10 bits */ + chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ + if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ + chr = (hc | chr) + 0x10000; + } + *buf++ = (TCHAR)chr; + return 1; + +#else /* ANSI/OEM output */ + WCHAR wc; + + wc = ff_uni2oem(chr, CODEPAGE); + if (wc >= 0x100) { /* Is this a DBC? */ + if (szb < 2) return 0; + *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ + *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + return 2; + } + if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ + *buf++ = (TCHAR)wc; /* Store the character */ + return 1; +#endif +} +#endif /* FF_USE_LFN */ + + +#if FF_FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static int lock_fs ( /* 1:Ok, 0:timeout */ + FATFS* fs /* Filesystem object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static void unlock_fs ( + FATFS* fs, /* Filesystem object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if FF_FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */ +) +{ + UINT i, be; + + /* Search open object table for the object */ + be = 0; + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matches with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == FF_FS_LOCK) { /* The object has not been opened */ + return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK; /* Is there a blank entry for new object? */ + } + + /* The object was opened. Reject any open against writing file and all write mode open */ + return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + return (i == FF_FS_LOCK) ? 0 : 1; +} + + +static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < FF_FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } + + if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; + if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; /* Index number origin from 1 */ +} + + +static FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < FF_FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* FF_FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the filesystem object */ +/*-----------------------------------------------------------------------*/ +#if !FF_FS_READONLY +static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Is the disk access window dirty */ + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) { /* Write back the window */ + fs->wflag = 0; /* Clear window dirty flag */ + if (fs->winsect - fs->fatbase < fs->fsize) { /* Is it in the 1st FAT? */ + if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */ + } + } else { + res = FR_DISK_ERR; + } + } + return res; +} +#endif + + +static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs, /* Filesystem object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !FF_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->pdrv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if read data is not valid */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize filesystem and data on the storage */ +/*-----------------------------------------------------------------------*/ + +static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS* fs /* Filesystem object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ + /* Create FSInfo structure */ + mem_set(fs->win, 0, sizeof fs->win); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* Write it into the FSInfo sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->pdrv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the lower layer */ + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get physical sector number from cluster number */ +/*-----------------------------------------------------------------------*/ + +static DWORD clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* Filesystem object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; /* Cluster number is origin from 2 */ + if (clst >= fs->n_fatent - 2) return 0; /* Is it invalid cluster number? */ + return fs->database + fs->csize * clst; /* Start sector number of the cluster */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; /* Get 1st byte of the entry */ + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; /* Merge 2nd byte of the entry */ + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); /* Adjust bit position */ + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */ + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; /* Simple DWORD array but mask out upper 4 bits */ + break; +#if FF_FS_EXFAT + case FS_EXFAT : + if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) { /* Object except root dir must have valid data length */ + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2 && cofs <= clen) { /* Is it a contiguous chain? */ + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* No data on the FAT, generate the value */ + break; + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (obj->n_frag != 0) { /* Is it on the growing edge? */ + val = 0x7FFFFFFF; /* Generate EOC */ + } else { + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + } + break; + } + } + /* go to default */ +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding filesystem object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Put 1st byte */ + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Put 2nd byte */ + fs->wflag = 1; + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ + fs->wflag = 1; + break; + + case FS_FAT32 : +#if FF_FS_EXFAT + case FS_EXFAT : +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_FS_EXFAT && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*--------------------------------------*/ +/* Find a contiguous free cluster block */ +/*--------------------------------------*/ + +static DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = SS(fs); + } + if (bv == 0) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ + } else { + scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm != 0); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*----------------------------------------*/ +/* Set/Clear a block of allocation bitmap */ +/*----------------------------------------*/ + +static FRESULT change_bitmap ( + FATFS* fs, /* Filesystem object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Fill the first fragment of the FAT chain */ +/*---------------------------------------------*/ + +static FRESULT fill_first_frag ( + FFOBJID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + + if (obj->stat == 3) { /* Has the object been changed 'fragmented' in this session? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + + +/*---------------------------------------------*/ +/* Fill the last fragment of the FAT chain */ +/*---------------------------------------------*/ + +static FRESULT fill_last_frag ( + FFOBJID* obj, /* Pointer to the corresponding object */ + DWORD lcl, /* Last cluster of the fragment */ + DWORD term /* Value to set the last FAT entry */ +) +{ + FRESULT res; + + + while (obj->n_frag > 0) { /* Create the chain of last fragment */ + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); + if (res != FR_OK) return res; + obj->n_frag--; + } + return FR_OK; +} + +#endif /* FF_FS_EXFAT && !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ + +static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0 if entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if FF_FS_EXFAT || FF_USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if FF_USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if FF_FS_EXFAT || FF_USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } +#endif +#if FF_USE_TRIM + rt[0] = clst2sect(fs, scl); /* Start of data area freed */ + rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area freed */ + disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform device the data in the block is no longer needed */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if FF_FS_EXFAT + /* Some post processes for chain status */ + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Has the entire chain been removed? */ + obj->stat = 0; /* Change the chain status 'initial' */ + } else { + if (obj->stat == 0) { /* Is it a fragmented chain from the beginning of this session? */ + clst = obj->sclust; /* Follow the chain to check if it gets contiguous */ + while (clst != pclst) { + nxt = get_fat(obj, clst); + if (nxt < 2) return FR_INT_ERR; + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; + if (nxt != clst + 1) break; /* Not contiguous? */ + clst++; + } + if (clst == pclst) { /* Has the chain got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Was the chain fragmented in this session and got contiguous again? */ + obj->stat = 2; /* Change the chain status 'contiguous' */ + } + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ + +static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FFOBJID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Suggested cluster to start to find */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch a chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Test for insanity */ + if (cs == 0xFFFFFFFF) return cs; /* Test for disk error */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; /* Cluster to start to find */ + } + if (fs->free_clst == 0) return 0; /* No free cluster */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous' */ + } else { /* It is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + if (obj->stat != 2) { /* Is the file non-contiguous? */ + if (ncl == clst + 1) { /* Is the cluster next to previous one? */ + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */ + } else { /* New fragment */ + if (obj->n_frag == 0) obj->n_frag = 1; + res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */ + if (res == FR_OK) obj->n_frag = 1; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + ncl = 0; + if (scl == clst) { /* Stretching an existing chain? */ + ncl = scl + 1; /* Test if next cluster is free */ + if (ncl >= fs->n_fatent) ncl = 2; + cs = get_fat(obj, ncl); /* Get next cluster status */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (cs != 0) { /* Not free? */ + cs = fs->last_clst; /* Start at suggested cluster if it is valid */ + if (cs >= 2 && cs < fs->n_fatent) scl = cs; + ncl = 0; + } + } + if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster found? */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster? */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* Test for error */ + if (ncl == scl) return 0; /* No free cluster found? */ + } + } + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !FF_FS_READONLY */ + + + + +#if FF_USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* FF_USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Fill a cluster with zeros */ +/*-----------------------------------------------------------------------*/ + +#if !FF_FS_READONLY +static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ + FATFS *fs, /* Filesystem object */ + DWORD clst /* Directory table to clear */ +) +{ + DWORD sect; + UINT n, szb; + BYTE *ibuf; + + + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + sect = clst2sect(fs, clst); /* Top of the cluster */ + fs->winsect = sect; /* Set window to top of the cluster */ + mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ +#if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ + /* Allocate a temporary buffer */ + for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ; + if (szb > SS(fs)) { /* Buffer allocated? */ + mem_set(ibuf, 0, szb); + szb /= SS(fs); /* Bytes -> Sectors */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + ff_memfree(ibuf); + } else +#endif + { + ibuf = fs->win; szb = 1; /* Use window buffer (many single-sector writes may take a time) */ + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ + } + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; +} +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (FF_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory on the FAT volume) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clst2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (dp->sect == 0) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; + + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0; /* Disable it if the offset reached the max value */ + if (dp->sect == 0) return FR_NO_FILE; /* Report EOT if it has been disabled */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (dp->clust == 0) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* It reached end of dynamic table */ +#if !FF_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR; /* Clean up the stretched table */ + if (FF_FS_EXFAT) dp->obj.stat |= 4; /* exFAT: The directory has been stretched */ +#else + if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clst2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if FF_FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !FF_FS_READONLY +static void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if FF_USE_LFN +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ + +static int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ + +static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc != 0) { + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF && wc != 0) { /* Put terminator if it is the last LFN part and not terminated */ + if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !FF_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ + +static void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LFN */ + + + +#if FF_USE_LFN && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC as hash value */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (dbc_1st(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* FF_USE_LFN && !FF_FS_READONLY */ + + + +#if FF_USE_LFN +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do { + sum = (sum >> 1) + (sum << 7) + *dir++; + } while (--n); + return sum; +} + +#endif /* FF_USE_LFN */ + + + +#if FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static WORD xdir_sum ( /* Get checksum of the directoly entry block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */ + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip 2-byte sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = (WCHAR)ff_wtoupper(chr); /* File name needs to be up-case converted */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !FF_FS_READONLY && FF_USE_MKFS +static DWORD xsum32 ( /* Returns 32-bit checksum */ + BYTE dat, /* Byte to be calculated (byte-by-byte processing) */ + DWORD sum /* Previous sum value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static void get_xfileinfo ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + WCHAR wc, hs; + UINT di, si, nc; + + /* Get file name from the entry block */ + si = SZDIRE * 2; /* 1st C1 entry */ + nc = 0; hs = 0; di = 0; + while (nc < dirb[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not support SFN */ + + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Reading direcotry object pointing top of the entry block to load */ +) +{ + FRESULT res; + UINT i, sz_ent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load file-directory entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; + + /* Load stream-extension entry */ + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */ + mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; + + /* Load file-name entries */ + i = 2 * SZDIRE; /* Name offset to load */ + do { + res = dir_next(dp, 0); + if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */ + if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + } while ((i += SZDIRE) < sz_ent); + + /* Sanity check (do it for only accessible object) */ + if (i <= MAXDIRB(FF_MAX_LFN)) { + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + } + return FR_OK; +} + + +/*------------------------------------------------------------------*/ +/* exFAT: Initialize object allocation info with loaded entry block */ +/*------------------------------------------------------------------*/ + +static void init_alloc_info ( + FATFS* fs, /* Filesystem object */ + FFOBJID* obj /* Object allocation information to be initialized */ +) +{ + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Start cluster */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; /* Allocation status */ + obj->n_frag = 0; /* No last fragment info */ +} + + + +#if !FF_FS_READONLY || FF_FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ + +static FRESULT load_obj_xdir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const FFOBJID* obj /* Object with its containing directory information */ +) +{ + FRESULT res; + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->obj.n_frag = 0; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !FF_FS_READONLY +/*----------------------------------------*/ +/* exFAT: Store the directory entry block */ +/*----------------------------------------*/ + +static FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the direcotry entry block to the directory */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the object name */ +) +{ + UINT i; + BYTE nc1, nlen; + WCHAR wc; + + + /* Create file-directory and stream-extension entry */ + mem_set(dirb, 0, 2 * SZDIRE); + dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; + dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; + + /* Create file-name entries */ + i = SZDIRE * 2; /* Top of file_name entries */ + nlen = nc1 = 0; wc = 1; + do { + dirb[i++] = ET_FILENAME; dirb[i++] = 0; + do { /* Fill name field */ + if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ + st_word(dirb + i, wc); /* Store it */ + i += 2; + } while (i % SZDIRE != 0); + nc1++; + } while (lfn[nlen]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nlen; /* Set name length */ + dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_EXFAT */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +#define DIR_READ_FILE(dp) dir_read(dp, 0) +#define DIR_READ_LABEL(dp) dir_read(dp, 1) + +static FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE attr, b; +#if FF_USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + b = dp->dir[DIR_Name]; /* Test for the entry type */ + if (b == 0) { + res = FR_NO_FILE; break; /* Reached to end of the directory */ + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (FF_USE_LABEL && vol) { + if (b == ET_VLABEL) break; /* Volume label entry? */ + } else { + if (b == ET_FILEDIR) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT/FAT32 volume */ + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if FF_USE_LFN /* LFN configuration */ + if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (attr == AM_LFN) { /* An LFN entry is found */ + if (b & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + b &= (BYTE)~LLEF; ord = b; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if FF_USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */ +#if FF_MAX_LFN < 255 + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ +#endif + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT/FAT32 volume */ +#if FF_USE_LFN + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if FF_USE_LFN /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate directory entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + + if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */ + dp->obj.stat &= ~4; + res = fill_first_frag(&dp->obj); /* Fill the first fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill the last fragment on the FAT if needed */ + if (res != FR_OK) return res; + if (dp->obj.sclust != 0) { /* Is it a sub-directory? */ + DIR dj; + + res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */ + if (res != FR_OK) return res; + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT/FAT32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if FF_USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !FF_FS_READONLY */ + + + +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if FF_USE_LFN /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ + } else { /* On the FAT/FAT32 volume */ + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'.*/ + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */ + + + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static void get_fileinfo ( + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT si, di; +#if FF_USE_LFN + BYTE lcf; + WCHAR wc, hs; + FATFS *fs = dp->obj.fs; +#else + TCHAR c; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (dp->sect == 0) return; /* Exit if read pointer has reached end of directory */ + +#if FF_USE_LFN /* LFN configuration */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xfileinfo(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT/FAT32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + si = di = hs = 0; + while (fs->lfnbuf[si] != 0) { + wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ + if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + fno->fname[di] = 0; /* Terminate the LFN (null string means LFN is invalid) */ + } + } + + si = di = 0; + while (si < 11) { /* Get SFN from SFN entry */ + wc = dp->dir[si++]; /* Get a char */ + if (wc == ' ') continue; /* Skip padding spaces */ + if (wc == RDDEM) wc = DDEM; /* Restore replaced DDEM character */ + if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.'; /* Insert a . if extension is exist */ +#if FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */ + wc = wc << 8 | dp->dir[si++]; + } + wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ + if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ + wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */ + if (wc == 0) { di = 0; break; } /* Buffer overflow? */ + di += wc; +#else /* ANSI/OEM output */ + fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ +#endif + } + fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ + + if (fno->fname[0] == 0) { /* If LFN is invalid, altname[] needs to be copied to fname[] */ + if (di == 0) { /* If LFN and SFN both are invalid, this object is inaccesible */ + fno->fname[di++] = '?'; + } else { + for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) { /* Copy altname[] to fname[] with case information */ + wc = (WCHAR)fno->altname[si]; + if (wc == '.') lcf = NS_EXT; + if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20; + fno->fname[di] = (TCHAR)wc; + } + } + fno->fname[di] = 0; /* Terminate the LFN */ + if (!dp->dir[DIR_NTres]) fno->altname[0] = 0; /* Altname is not needed if neither LFN nor case info is exist. */ + } + +#else /* Non-LFN configuration */ + si = di = 0; + while (si < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[si++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ + if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ + fno->fname[di++] = c; + } + fno->fname[di] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ +} + +#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ + + + +#if FF_USE_FIND && FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +static DWORD get_achar ( /* Get a character and advances ptr */ + const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ +) +{ + DWORD chr; + + +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode input */ + chr = tchar2uni(ptr); + if (chr == 0xFFFFFFFF) chr = 0; /* Wrong UTF encoding is recognized as end of the string */ + chr = ff_wtoupper(chr); + +#else /* ANSI/OEM input */ + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#if FF_CODE_PAGE == 0 + if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#elif FF_CODE_PAGE < 900 + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900 + if (dbc_1st((BYTE)chr)) { /* Get DBC 2nd byte if needed */ + chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0; + } +#endif + +#endif + return chr; +} + + +static int pattern_matching ( /* 0:not matched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + DWORD pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (*pat == 0 && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard block */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if FF_USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR wc, *lfn; + DWORD uc; + UINT i, ni, si, di; + const TCHAR *p; + + + /* Create LFN into LFN working buffer */ + p = *path; lfn = dp->obj.fs->lfnbuf; di = 0; + for (;;) { + uc = tchar2uni(&p); /* Get a character */ + if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ + if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ + wc = (WCHAR)uc; + if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ + lfn[di++] = wc; /* Store the Unicode character */ + } + while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + *path = p; /* Return pointer to the next segment */ + cf = (wc < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + +#if FF_FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + } + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + wc = lfn[di - 1]; + if (wc != ' ' && wc != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created into the working buffer */ + if (di == 0) return FR_INVALID_NAME; /* Reject null name */ + + /* Create SFN in directory form */ + for (si = 0; lfn[si] == ' '; si++) ; /* Remove leading spaces */ + if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ + while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ + + mem_set(dp->fn, ' ', 11); + i = b = 0; ni = 8; + for (;;) { + wc = lfn[si++]; /* Get an LFN character */ + if (wc == 0) break; /* Break on end of the LFN */ + if (wc == ' ' || (wc == '.' && si != di)) { /* Remove embedded spaces and dots */ + cf |= NS_LOSS | NS_LFN; + continue; + } + + if (i >= ni || si == di) { /* End of field? */ + if (ni == 11) { /* Name extension overflow? */ + cf |= NS_LOSS | NS_LFN; + break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Name body overflow? */ + if (si > di) break; /* No name extension? */ + si = di; i = 8; ni = 11; b <<= 2; /* Enter name extension */ + continue; + } + + if (wc >= 0x80) { /* Is this a non-ASCII character? */ + cf |= NS_LFN; /* LFN entry needs to be created */ +#if FF_CODE_PAGE == 0 + if (ExCvt) { /* At SBCS */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ + } else { /* At DBCS */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ + } +#elif FF_CODE_PAGE < 900 /* SBCS cfg */ + wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ + if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ +#else /* DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ +#endif + } + + if (wc >= 0x100) { /* Is this a DBC? */ + if (i >= ni - 1) { /* Field overflow? */ + cf |= NS_LOSS | NS_LFN; + i = ni; continue; /* Next field */ + } + dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ + } else { /* SBC */ + if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(wc)) { /* ASCII upper case? */ + b |= 2; + } + if (IsLower(wc)) { /* ASCII lower case? */ + b |= 1; wc -= 0x20; + } + } + } + dp->fn[i++] = (BYTE)wc; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; /* Shift capital flags if no extension */ + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* LFN entry needs to be created if composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if (b & 0x01) cf |= NS_EXT; /* NT flag (Extension has small capital letters only) */ + if (b & 0x04) cf |= NS_BODY; /* NT flag (Body has small capital letters only) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created into dp->fn[] */ + + return FR_OK; + + +#else /* FF_USE_LFN : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if FF_FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; /* Get a byte */ + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or field overflow? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Field overflow or invalid dot? */ + i = 8; ni = 11; /* Enter file extension field */ + continue; + } +#if FF_CODE_PAGE == 0 + if (ExCvt && c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#elif FF_CODE_PAGE < 900 + if (c >= 0x80) { /* Is SBC extended character? */ + c = ExCvt[c & 0x7F]; /* To upper SBC extended character */ + } +#endif + if (dbc_1st(c)) { /* Check if it is a DBC 1st byte */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* FF_USE_LFN */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + FATFS *fs = dp->obj.fs; + + +#if FF_FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + dp->obj.sclust = fs->cdir; /* Start from current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + dp->obj.sclust = 0; /* Start from root directory */ + } +#if FF_FS_EXFAT + dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */ +#if FF_FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && dp->obj.sclust) { /* exFAT: Retrieve the sub-directory's status */ + DIR dj; + + dp->obj.c_scl = fs->cdc_scl; + dp->obj.c_size = fs->cdc_size; + dp->obj.c_ofs = fs->cdc_ofs; + res = load_obj_xdir(&dj, &dp->obj); + if (res != FR_OK) return res; + dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (FF_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + dp->obj.c_scl = dp->obj.sclust; + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Open next directory */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + TCHAR tc; + int i, vol = -1; +#if FF_STR_VOLUME_ID /* Find string volume ID */ + const char *sp; + char c; +#endif + + tt = tp = *path; + if (!tp) return vol; /* Invalid path name? */ + do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */ + + if (tc == ':') { /* DOS/Windows style volume ID? */ + i = FF_VOLUMES; + if (IsDigit(*tp) && tp + 2 == tt) { /* Is there a numeric volume ID + colon? */ + i = (int)*tp - '0'; /* Get the LD number */ + } +#if FF_STR_VOLUME_ID == 1 /* Arbitrary string is enabled */ + else { + i = 0; + do { + sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + do { /* Compare the volume ID with path name */ + c = *sp++; tc = *tp++; + if (IsLower(c)) c -= 0x20; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */ + } +#endif + if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ + vol = i; /* Drive number */ + *path = tt; /* Snip the drive prefix off */ + } + return vol; + } +#if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ + if (*tp == '/') { + i = 0; + do { + sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + do { /* Compare the volume ID with path name */ + c = *sp++; tc = *(++tp); + if (IsLower(c)) c -= 0x20; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ + if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ + vol = i; /* Drive number */ + *path = tp; /* Snip the drive prefix off */ + return vol; + } + } +#endif + /* No drive prefix is found */ +#if FF_FS_RPATH != 0 + vol = CurrVol; /* Default drive is current drive */ +#else + vol = 0; /* Default drive is 0 */ +#endif + return vol; /* Return the default drive */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT VBR */ +/*-----------------------------------------------------------------------*/ + +static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ + FATFS* fs, /* Filesystem object */ + DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ + + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always here regardless of the sector size) */ + +#if FF_FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ +#endif + if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */ + if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */ + if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */ + } + return 2; /* Valid BS but not FAT */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Determine logical drive number and mount the volume if needed */ +/*-----------------------------------------------------------------------*/ + +static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found filesystem object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the filesystem object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the filesystem object */ + if (!fs) return FR_NOT_ENABLED; /* Is the filesystem object available? */ +#if FF_FS_REENTRANT + if (!lock_fs(fs)) return FR_TIMEOUT; /* Lock the volume */ +#endif + *rfs = fs; /* Return pointer to the filesystem object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type != 0) { /* If the volume has been mounted */ + stat = disk_status(fs->pdrv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + EFSPRINTF("WPEN1"); + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The filesystem object is valid */ + } + } + + /* The filesystem object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */ + + fs->fs_type = 0; /* Clear the filesystem object */ + fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + EFSPRINTF("MDNR"); + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + EFSPRINTF("WPEN2"); + return FR_WRITE_PROTECTED; + } +#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + + /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK (MBR) and SFD (w/o partition). */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i != 0) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4); + } + if (fmt == 4) { + EFSPRINTF("BRNL"); + return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + } + if (fmt >= 2) { + EFSPRINTF("NOFAT"); + return FR_NO_FILESYSTEM; /* No FAT volume is found */ + } + + /* An FAT volume is found (bsect). Following code initializes the filesystem object */ + +#if FF_FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + DWORD so, cv, bcl; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + EFSPRINTF("EXSPS"); + return FR_NO_FILESYSTEM; + } + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) { + EFSPRINTF("EXFNF"); + return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + } + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Get bitmap location and check if it is contiguous (implementation assumption) */ + so = i = 0; + for (;;) { /* Find the bitmap entry in the root directory (in only first cluster) */ + if (i == 0) { + if (so >= fs->csize) return FR_NO_FILESYSTEM; /* Not found? */ + if (move_window(fs, clst2sect(fs, fs->dirbase) + so) != FR_OK) { + EFSPRINTF("EXBM1C"); + return FR_DISK_ERR; + } + so++; + } + if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ + i = (i + SZDIRE) % SS(fs); /* Next entry */ + } + bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ + if (bcl < 2 || bcl >= fs->n_fatent) { + EFSPRINTF("EXBMM"); + return FR_NO_FILESYSTEM; + } + fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */ + for (;;) { /* Check if bitmap is contiguous */ + if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR; + cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); + if (cv == 0xFFFFFFFF) break; /* Last link? */ + if (cv != ++bcl) { + EFSPRINTF("EXBMM"); + return FR_NO_FILESYSTEM; /* Fragmented? */ + } + } + +#if !FF_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* FF_FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) { + EFSPRINTF("32SPS"); + return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + } + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = 0; + if (nclst <= MAX_FAT32) fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + if (fmt == 0) return FR_NO_FILESYSTEM; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !FF_FS_READONLY + /* Get FSInfo if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (FF_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (FF_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (FF_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (FF_FS_NOFSINFO & 3) != 3 */ +#endif /* !FF_FS_READONLY */ + } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* Volume mount ID */ +#if FF_USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if FF_FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block scratchpad buffer */ +#endif +#endif +#if FF_FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if FF_FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + FFOBJID* obj, /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */ + FATFS** rfs /* Pointer to pointer to the owner filesystem object to return */ +) +{ + FRESULT res = FR_INVALID_OBJECT; + + + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */ +#if FF_FS_REENTRANT + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } else { + unlock_fs(obj->fs, FR_OK); + } + } else { + res = FR_TIMEOUT; + } +#else + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } +#endif + } + *rfs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */ + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) { + EFSPRINTF("IDRIVE!"); + return FR_INVALID_DRIVE; + } + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if FF_FS_LOCK != 0 + clear_lock(cfs); +#endif +#if FF_FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if FF_FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (opt == 0) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !FF_FS_READONLY + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive number */ + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; + res = find_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !FF_FS_READONLY /* Read/Write configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if FF_FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Check if the file can be used */ + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */ +#if FF_FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + } + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object with the same name is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate the file if overwrite mode */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + init_alloc_info(fs, &fp->obj); + /* Set directory entry block initial state */ + mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ + mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ + fs->dirbuf[XDIR_Attr] = AM_ARC; + st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust != 0) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Set directory entry initial state */ + cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ + st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + if (cl != 0) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Is the object exsiting? */ + if (dj.obj.attr & AM_DIR) { /* File open against a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED; /* Set file change flag if created or overwritten */ + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if FF_FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); /* Lock the file for this session */ + if (fp->obj.lockid == 0) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* Is it a directory? */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + init_alloc_info(fs, &fp->obj); + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if FF_USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !FF_FS_READONLY +#if !FF_FS_TINY + mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clst2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !FF_FS_TINY + if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + UINT br_tmp; + if (!br) + br = &br_tmp; + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) { + EFSPRINTF("FOV"); + LEAVE_FF(fs, res); /* Check validity */ + } + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until btr bytes read */ + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) { + EFSPRINTF("CCHK"); + ABORT(fs, FR_INT_ERR); + } + if (clst == 0xFFFFFFFF) { + EFSPRINTF("DSKC"); + ABORT(fs, FR_DISK_ERR); + } + fp->clust = clst; /* Update current cluster */ + } + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) { + EFSPRINTF("RLIO"); + ABORT(fs, FR_DISK_ERR); + } +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if FF_FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !FF_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { + EFSPRINTF("RDC"); + ABORT(fs, FR_DISK_ERR); + } + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { + EFSPRINTF("RSC"); + ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + UINT bw_tmp; + if (!bw) + bw = &bw_tmp; + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) { + EFSPRINTF("FOV"); + LEAVE_FF(fs, res); /* Check validity */ + } + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */ + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if FF_USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) { + EFSPRINTF("DSKFULL"); + break; /* Could not allocate a new cluster (disk full) */ + } + if (clst == 1) { + EFSPRINTF("CCHK"); + ABORT(fs, FR_INT_ERR); + } + if (clst == 0xFFFFFFFF) { + EFSPRINTF("DERR"); + ABORT(fs, FR_DISK_ERR); + } + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if FF_FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clst2sect(fs, fp->clust); /* Get current sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc > 0) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) { + EFSPRINTF("WLIO"); + ABORT(fs, FR_DISK_ERR); + } +#if FF_FS_MINIMIZE <= 2 +#if FF_FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if FF_FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if FF_FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !FF_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */ + if (res == FR_OK) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } + if (res == FR_OK) { + DIR dj; + DEF_NAMBUF + + INIT_NAMBUF(fs); + res = load_obj_xdir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation information */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive attribute to indicate that the file has been changed */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !FF_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) fp->obj.fs = 0; /* Invalidate file object */ +#else + fp->obj.fs = 0; /* Invalidate file object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if FF_FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chdrive ( + const TCHAR* path /* Drive number to set */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ +#if FF_STR_VOLUME_ID == 2 + UINT i; +#endif + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* Is it the start directory itself? */ + fs->cdir = dj.obj.sclust; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */ + if (res == FR_OK) { + for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */ + CurrVol = (BYTE)i; + } +#endif + } + + LEAVE_FF(fs, res); +} + + +#if FF_FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of buff in unit of TCHAR */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp = buff; +#if FF_VOLUMES >= 2 + UINT vl; +#endif +#if FF_STR_VOLUME_ID + const char *vp; +#endif + FILINFO fno; + DEF_NAMBUF + + + /* Get logical drive */ + buff[0] = 0; /* Set null string to get current volume */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + + /* Follow parent directories and create the path */ + i = len; /* Bottom of buffer (directory stack base) */ + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = DIR_READ_FILE(&dj); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; /* Name length */ + if (i < n + 1) { /* Insufficient space to store the path name? */ + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; /* Stack the name */ + buff[--i] = '/'; + } + } + if (res == FR_OK) { + if (i == len) buff[--i] = '/'; /* Is it the root-directory? */ +#if FF_VOLUMES >= 2 /* Put drive prefix */ + vl = 0; +#if FF_STR_VOLUME_ID >= 1 /* String volume ID */ + for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ; + if (i >= n + 2) { + if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; + for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; + if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; + vl++; + } +#else /* Numeric volume ID */ + if (i >= 3) { + *tp++ = (TCHAR)'0' + CurrVol; + *tp++ = (TCHAR)':'; + vl = 2; + } +#endif + if (vl == 0) res = FR_NOT_ENOUGH_CORE; +#endif + /* Add current directory path */ + if (res == FR_OK) { + do *tp++ = buff[i++]; while (i < len); /* Copy stacked path string */ + } + } + FREE_NAMBUF(); + } + + *tp = 0; + LEAVE_FF(fs, res); +} + +#endif /* FF_FS_RPATH >= 2 */ +#endif /* FF_FS_RPATH >= 1 */ + + + +#if FF_FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File Read/Write Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; +#if FF_USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) res = (FRESULT)fp->err; +#if FF_FS_EXFAT && !FF_FS_READONLY + if (res == FR_OK && fs->fs_type == FS_EXFAT) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } +#endif + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl != 0) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs > 0) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clst2sect(fs, fp->clust); + if (dsc == 0) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4 GiB - 1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs > 0) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !FF_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !FF_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clst2sect(fs, clst); /* Current sector */ + if (nsect == 0) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !FF_FS_TINY +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if FF_FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dp->obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (dp->obj.attr & AM_DIR) { /* This object is a sub-directory */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + dp->obj.c_scl = dp->obj.sclust; /* Get containing directory inforamation */ + dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; + dp->obj.c_ofs = dp->blk_ofs; + init_alloc_info(fs, &dp->obj); /* Get object allocation info */ + } else +#endif + { + dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dp->obj.id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + if (dp->obj.sclust != 0) { + dp->obj.lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->obj.lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->obj.fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if FF_FS_LOCK != 0 + if (dp->obj.lockid) res = dec_lock(dp->obj.lockid); /* Decrement sub-directory open counter */ + if (res == FR_OK) dp->obj.fs = 0; /* Invalidate directory object */ +#else + dp->obj.fs = 0; /* Invalidate directory object */ +#endif +#if FF_FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = DIR_READ_FILE(dp); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if FF_USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if FF_USE_LFN && FF_USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* FF_USE_FIND */ + + + +#if FF_FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + FFOBJID obj; + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full FAT scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Scan FAT to obtain number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Scan bit field FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan allocation bitmap */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; /* Number of clusters */ + sect = fs->bitbase; /* Bitmap sector */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of bits with zero in the bitmap */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Scan WORD/DWORD FAT entries */ + clst = fs->n_fatent; /* Number of entries */ + sect = fs->fatbase; /* Top of the FAT */ + i = 0; /* Offset in the sector */ + do { /* Counts numbuer of entries with zero in the FAT */ + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(fs->win + i) == 0) nfree++; + i += 2; + } else { + if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++; + i += 4; + } + i %= SS(fs); + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */ + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current read/write point */ + fp->flag |= FA_MODIFIED; +#if !FF_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if FF_FS_EXFAT + FFOBJID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if FF_FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if FF_FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + init_alloc_info(fs, &obj); + dclst = obj.sclust; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ +#if FF_FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = DIR_READ_FILE(&sdj); /* Test if the directory is empty */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst != 0) { /* Remove the cluster chain if exist */ +#if FF_FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FFOBJID sobj; + FATFS *fs; + DWORD dcl, pcl, tm; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Name collision? */ + if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { /* Invalid name? */ + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* It is clear to create a new directory */ + sobj.fs = fs; /* New object id to create a new chain */ + dcl = create_chain(&sobj, 0); /* Allocate a cluster for the new directory */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster? */ + if (dcl == 1) res = FR_INT_ERR; /* Any insanity? */ + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; /* Disk error? */ + tm = GET_FATTIME(); + if (res == FR_OK) { + res = dir_clear(fs, dcl); /* Clean up the new table */ + if (res == FR_OK) { + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */ + mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ + fs->win[DIR_Name] = '.'; + fs->win[DIR_Attr] = AM_DIR; + st_dword(fs->win + DIR_ModTime, tm); + st_clust(fs, fs->win, dcl); + mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ + fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + st_clust(fs, fs->win + SZDIRE, pcl); + fs->wflag = 1; + } + res = dir_register(&dj); /* Register the object to the parent directoy */ + } + } + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs)); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs)); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dj.dir, dcl); /* Table start cluster */ + dj.dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } else { + remove_chain(&sobj, dcl, 0); /* Could not register, remove the allocated cluster */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Snip the drive number of new name off */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if FF_FS_LOCK != 0 + if (res == FR_OK) { + res = chk_lock(&djo, 2); + } +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); + if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ +/* Start of critical section where an interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT/FAT32 volume */ + mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy directory entry of the object except name */ + mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + dir[DIR_Attr] = buf[DIR_Attr]; + if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clst2sect(fs, ld_clust(fs, dir)); + if (dw == 0) { + res = FR_INT_ERR; + } else { +/* Start of critical section where an interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of the critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_FS_MINIMIZE == 0 */ +#endif /* FF_FS_MINIMIZE <= 1 */ +#endif /* FF_FS_MINIMIZE <= 2 */ + + + +#if FF_USE_CHMOD && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_CHMOD && !FF_FS_READONLY */ + + + +#if FF_USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Logical drive number */ + TCHAR* label, /* Buffer to store the volume label */ + DWORD* vsn /* Variable to store the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; + WCHAR wc; + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = DIR_READ_LABEL(&dj); /* Find a volume label entry */ + if (res == FR_OK) { +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + WCHAR hs; + + for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + wc = ld_word(dj.dir + XDIR_Label + si * 2); + if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ + hs = wc; continue; + } + wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); + if (wc == 0) { di = 0; break; } + di += wc; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry */ + while (si < 11) { + wc = dj.dir[si++]; +#if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ + if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ + wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ + if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */ + if (wc == 0) { di = 0; break; } + di += wc; +#else /* ANSI/OEM output */ + label[di++] = (TCHAR)wc; +#endif + } + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: + di = BPB_VolIDEx; break; + + case FS_FAT32: + di = BS_VolID32; break; + + default: + di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Volume label to set with heading logical drive number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT di; + WCHAR wc; + static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ +#if FF_USE_LFN + DWORD dc; +#endif + + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + mem_set(dirvn, 0, 22); + di = 0; + while ((UINT)*label >= ' ') { /* Create volume label */ + dc = tchar2uni(&label); /* Get a Unicode character */ + if (dc >= 0x10000) { + if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */ + dc = 0; + } else { + st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; + } + } + if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + di * 2, (WCHAR)dc); di++; + } + } else +#endif + { /* On the FAT/FAT32 volume */ + mem_set(dirvn, ' ', 11); + di = 0; + while ((UINT)*label >= ' ') { /* Create volume label */ +#if FF_USE_LFN + dc = tchar2uni(&label); + wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0; +#else /* ANSI/OEM input */ + wc = (BYTE)*label++; + if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0; + if (IsLower(wc)) wc -= 0x20; /* To upper ASCII characters */ +#if FF_CODE_PAGE == 0 + if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#elif FF_CODE_PAGE < 900 + if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ +#endif +#endif + if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); + dirvn[di++] = (BYTE)wc; + } + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + while (di && dirvn[di - 1] == ' ') di--; /* Snip trailing spaces */ + } + + /* Set volume label */ + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = DIR_READ_LABEL(&dj); /* Get volume label entry */ + if (res == FR_OK) { + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + if (di != 0) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry or an error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (di != 0) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */ + dj.dir[XDIR_NumLabel] = (BYTE)di; + mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_LABEL */ + + + +#if FF_USE_EXPAND && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if FF_FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if FF_FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { /* Is it allocated now? */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (FF_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* FF_USE_EXPAND && !FF_FS_READONLY */ + + + +#if FF_USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward Data to the Stream Directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clst2sect(fs, fp->clust); /* Get current data sector */ + if (sect == 0) ABORT(fs, FR_INT_ERR); + sect += csect; +#if FF_FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !FF_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (rcnt == 0) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* FF_USE_FORWARD */ + + + +#if FF_USE_MKFS && !FF_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create an FAT/exFAT volume */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit (cluster) [byte] */ + void* work, /* Pointer to working buffer (null: use heap memory) */ + UINT len /* Size of working buffer [byte] */ +) +{ + const UINT n_fats = 1; /* Number of FATs for FAT/FAT32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; /* Sector size */ + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i; + int vol; + DSTATUS stat; +#if FF_USE_TRIM || FF_FS_EXFAT + DWORD tbl[3]; +#endif + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume if mounted */ + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ + + /* Check physical drive status */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if FF_MAX_SS != FF_MIN_SS /* Get sector size of the medium if variable sector size cfg. */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = FF_MAX_SS; +#endif + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ +#if FF_USE_LFN == 3 + if (!work) { /* Use heap memory for working buffer */ + for (szb_buf = MAX_MALLOC, buf = 0; szb_buf >= ss && (buf = ff_memalloc(szb_buf)) == 0; szb_buf /= 2) ; + sz_buf = szb_buf / ss; /* Size of working buffer (sector) */ + } else +#endif + { + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + } + if (!buf || sz_buf == 0) return FR_NOT_ENOUGH_CORE; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (FF_MULTI_PARTITION && part != 0) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } else { + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (FF_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) LEAVE_MKFS(FR_INVALID_PARAMETER); /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER); /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if FF_FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ +#if FF_USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (au == 0) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ + if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = 0; si = 0; i = 0; j = 0; szb_case = 0; + do { + switch (st) { + case 0: + ch = (WCHAR)ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* go to next case */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + + default: + ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (si == 0 || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb != 0 && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb != 0 && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry */ + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ + st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */ + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + } + + } else +#endif /* FF_FS_EXFAT */ + { /* Create an FAT/FAT32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (pau == 0) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED); + } else { /* FAT volume */ + if (pau == 0) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (au == 0 && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (au == 0 && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (au == 0 && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + LEAVE_MKFS(FR_MKFS_ABORTED); + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if FF_USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + sect += n; nsect -= n; + } while (nsect); + } + + /* Determine system ID in the partition table */ + if (FF_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ + } else { + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (large) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */ + } + } + } + + /* Update partition information */ + if (FF_MULTI_PARTITION && part != 0) { /* Created in the existing partition */ + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ + } else { /* Created as a new single partition */ + if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(((n >> 2) & 0xC0) | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the MBR */ + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); + + LEAVE_MKFS(FR_OK); +} + + + +#if FF_MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create Partition Table on the Physical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD* szt, /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer (null: use heap memory) */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + FRESULT res; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + buf = (BYTE*)work; +#if FF_USE_LFN == 3 + if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */ +#endif + if (!buf) return FR_NOT_ENOUGH_CORE; + + /* Determine the CHS without any consideration of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = (BYTE)(n - 1); + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, FF_MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */ + if (p_cyl == 0) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; /* End cylinder */ + if (e_cyl >= tot_cyl) LEAVE_MKFS(FR_INVALID_PARAMETER); + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)(((b_cyl >> 2) & 0xC0) | 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x07; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)(((e_cyl >> 2) & 0xC0) | 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Number of sectors */ + + /* Next partition */ + b_cyl += p_cyl; + } + st_word(p, 0xAA55); /* MBR signature (always at offset 510) */ + + /* Write it to the MBR */ + res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + LEAVE_MKFS(res); +} + +#endif /* FF_MULTI_PARTITION */ +#endif /* FF_USE_MKFS && !FF_FS_READONLY */ + + + + +#if FF_USE_STRFUNC +#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3) +#error Wrong FF_STRF_ENCODE setting +#endif +/*-----------------------------------------------------------------------*/ +/* Get a String from the File */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (items) */ + FIL* fp /* Pointer to the file object */ +) +{ + int nc = 0; + TCHAR *p = buff; + BYTE s[4]; + UINT rc; + DWORD dc; +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2 + WCHAR wc; +#endif +#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3 + UINT ct; +#endif + +#if FF_USE_LFN && FF_LFN_UNICODE /* With code conversion (Unicode API) */ + /* Make a room for the character and terminator */ + if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2; + if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4; + if (FF_LFN_UNICODE == 3) len -= 1; + while (nc < len) { +#if FF_STRF_ENCODE == 0 /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + wc = s[0]; + if (dbc_1st((BYTE)wc)) { + f_read(fp, s, 1, &rc); + if (rc != 1 || !dbc_2nd(s[0])) continue; + wc = wc << 8 | s[0]; + } + dc = ff_oem2uni(wc, CODEPAGE); + if (dc == 0) continue; +#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; + if (IsSurrogateL(dc)) continue; + if (IsSurrogateH(dc)) { + f_read(fp, s, 2, &rc); + if (rc != 2) break; + wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1]; + if (!IsSurrogateL(wc)) continue; + dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF); + } +#else /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + dc = s[0]; + if (dc >= 0x80) { /* Multi-byte character? */ + ct = 0; + if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; } /* 2-byte? */ + if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte? */ + if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte? */ + if (ct == 0) continue; + f_read(fp, s, ct, &rc); /* Get trailing bytes */ + if (rc != ct) break; + rc = 0; + do { /* Merge trailing bytes */ + if ((s[rc] & 0xC0) != 0x80) break; + dc = dc << 6 | (s[rc] & 0x3F); + } while (++rc < ct); + if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue; /* Wrong encoding? */ + } +#endif + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */ +#if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */ + if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */ + *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ + dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ + } + *p++ = (TCHAR)dc; nc++; + if (dc == '\n') break; /* End of line? */ +#elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ + if (dc < 0x80) { /* 1-byte */ + *p++ = (TCHAR)dc; + nc++; + if (dc == '\n') break; /* End of line? */ + } else { + if (dc < 0x800) { /* 2-byte */ + *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 2; + } else { + if (dc < 0x10000) { /* 3-byte */ + *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 3; + } else { /* 4-byte */ + *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + nc += 4; + } + } + } +#endif + } + +#else /* Byte-by-byte without any conversion (ANSI/OEM API) */ + len -= 1; /* Make a room for the terminator */ + while (nc < len) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + dc = s[0]; + if (FF_USE_STRFUNC == 2 && dc == '\r') continue; + *p++ = (TCHAR)dc; nc++; + if (dc == '\n') break; + } +#endif + + *p = 0; /* Terminate the string */ + return nc ? buff : 0; /* When no data read due to EOF or error, return with error. */ +} + + + + +#if !FF_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a Character to the File */ +/*-----------------------------------------------------------------------*/ + +typedef struct { /* Putchar output buffer and work area */ + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of encoding units written */ +#if FF_USE_LFN && FF_LFN_UNICODE == 1 + WCHAR hs; +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 + BYTE bs[4]; + UINT wi, ct; +#endif + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +static void putc_bfd ( /* Buffered write with code conversion */ + putbuff* pb, + TCHAR c +) +{ + UINT n; + int i, nc; +#if FF_USE_LFN && FF_LFN_UNICODE + WCHAR hs, wc; +#if FF_LFN_UNICODE == 2 + DWORD dc; + TCHAR *tp; +#endif +#endif + + if (FF_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + nc = pb->nchr; /* Write unit counter */ + +#if FF_USE_LFN && FF_LFN_UNICODE +#if FF_LFN_UNICODE == 1 /* UTF-16 input */ + if (IsSurrogateH(c)) { + pb->hs = c; return; + } + hs = pb->hs; pb->hs = 0; + if (hs != 0) { + if (!IsSurrogateL(c)) hs = 0; + } else { + if (IsSurrogateL(c)) return; + } + wc = c; +#elif FF_LFN_UNICODE == 2 /* UTF-8 input */ + for (;;) { + if (pb->ct == 0) { /* Out of multi-byte sequence? */ + pb->bs[pb->wi = 0] = (BYTE)c; /* Save 1st byte */ + if ((BYTE)c < 0x80) break; /* 1-byte? */ + if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1; /* 2-byte? */ + if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2; /* 3-byte? */ + if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3; /* 4-byte? */ + return; + } else { /* In the multi-byte sequence */ + if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */ + pb->ct = 0; continue; + } + pb->bs[++pb->wi] = (BYTE)c; /* Save the trailing byte */ + if (--pb->ct == 0) break; /* End of multi-byte sequence? */ + return; + } + } + tp = (TCHAR*)pb->bs; + dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ + if (dc == 0xFFFFFFFF) return; + wc = (WCHAR)dc; + hs = (WCHAR)(dc >> 16); +#elif FF_LFN_UNICODE == 3 /* UTF-32 input */ + if (IsSurrogate(c) || c >= 0x110000) return; + if (c >= 0x10000) { + hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40)); /* Make high surrogate */ + wc = 0xDC00 | (c & 0x3FF); /* Make low surrogate */ + } else { + hs = 0; + wc = (WCHAR)c; + } +#endif + +#if FF_STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + if (hs != 0) { + st_word(&pb->buf[i], hs); + i += 2; + nc++; + } + st_word(&pb->buf[i], wc); + i += 2; +#elif FF_STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + if (hs != 0) { + pb->buf[i++] = (BYTE)(hs >> 8); + pb->buf[i++] = (BYTE)hs; + nc++; + } + pb->buf[i++] = (BYTE)(wc >> 8); + pb->buf[i++] = (BYTE)wc; +#elif FF_STRF_ENCODE == 3 /* Write it in UTF-8 */ + if (hs != 0) { /* 4-byte */ + nc += 3; + hs = (hs & 0x3FF) + 0x40; + pb->buf[i++] = (BYTE)(0xF0 | hs >> 8); + pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F)); + pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F)); + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } else { + if (wc < 0x80) { /* 1-byte */ + pb->buf[i++] = (BYTE)wc; + } else { + if (wc < 0x800) { /* 2-byte */ + nc += 1; + pb->buf[i++] = (BYTE)(0xC0 | wc >> 6); + } else { /* 3-byte */ + nc += 2; + pb->buf[i++] = (BYTE)(0xE0 | wc >> 12); + pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F)); + } + } +#else /* Write it in ANSI/OEM */ + if (hs != 0) return; + wc = ff_uni2oem(wc, CODEPAGE); /* UTF-16 ==> ANSI/OEM */ + if (wc == 0) return; + if (wc >= 0x100) { + pb->buf[i++] = (BYTE)(wc >> 8); nc++; + } + pb->buf[i++] = (BYTE)wc; +#endif + +#else /* ANSI/OEM input (without re-encode) */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 4) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &n); + i = (n == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr = nc + 1; +} + + +static int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + mem_set(pb, 0, sizeof (putbuff)); + pb->fp = fp; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a Formatted String to the File */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + if (c == '*') { /* Minimum width by argument */ + w = va_arg(arp, int); + c = *fmt++; + } else { + while (IsDigit(c)) { /* Minimum width */ + w = w * 10 + c - '0'; + c = *fmt++; + } + } + if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (c == 0) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Atgument type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { /* Right padded */ + while (j++ < w) putc_bfd(&pb, ' ') ; + } + while (*p) putc_bfd(&pb, *p++) ; /* String body */ + while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ + continue; + + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + + case 'B' : /* Unsigned binary */ + r = 2; break; + + case 'O' : /* Unsigned octal */ + r = 8; break; + + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + + case 'X' : /* Unsigned hexdecimal */ + r = 16; break; + + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof *str); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, d); /* Right pad */ + } + do { + putc_bfd(&pb, str[--i]); /* Number body */ + } while (i); + while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !FF_FS_READONLY */ +#endif /* FF_USE_STRFUNC */ + + + +#if FF_CODE_PAGE == 0 +/*-----------------------------------------------------------------------*/ +/* Set Active Codepage for the Path Name */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setcp ( + WORD cp /* Value to be set as active code page */ +) +{ + static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; + UINT i; + + + for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */ + if (validcp[i] != cp) return FR_INVALID_PARAMETER; /* Not found? */ + + CodePage = cp; + if (cp >= 900) { /* DBCS */ + ExCvt = 0; + DbcTbl = tables[i]; + } else { /* SBCS */ + ExCvt = tables[i]; + DbcTbl = 0; + } + return FR_OK; +} +#endif /* FF_CODE_PAGE == 0 */ + diff --git a/emummc/source/libs/fatfs/ff.h b/emummc/source/libs/fatfs/ff.h new file mode 100644 index 000000000..27bf89dbd --- /dev/null +++ b/emummc/source/libs/fatfs/ff.h @@ -0,0 +1,379 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem module R0.13c / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2018, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/----------------------------------------------------------------------------*/ + + +#ifndef FF_DEFINED +#define FF_DEFINED 86604 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../utils/types.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if FF_DEFINED != FFCONF_DEF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#endif + +#if FF_STR_VOLUME_ID +#ifndef FF_VOLUME_STRS +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ +#endif +#endif + + + +/* Type of path name strings on FatFs API */ + +#ifndef _INC_TCHAR +#define _INC_TCHAR + +#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ +typedef char TCHAR; +#define _T(x) u8 ## x +#define _TEXT(x) u8 ## x +#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ +typedef DWORD TCHAR; +#define _T(x) U ## x +#define _TEXT(x) U ## x +#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) +#error Wrong FF_LFN_UNICODE setting +#else /* ANSI/OEM code in SBCS/DBCS */ +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* Type of file size variables */ + +#if FF_FS_EXFAT +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* Filesystem object structure (FATFS) */ + +typedef struct { + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ + BYTE fs_type; /* Filesystem type (0:not mounted) */ + BYTE pdrv; /* Associated physical drive */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* Volume mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if FF_MAX_SS != FF_MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if FF_USE_LFN + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if FF_FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ +#endif +#if FF_FS_REENTRANT + FF_SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !FF_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if FF_FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#if FF_FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ +#if FF_FS_EXFAT + DWORD bitbase; /* Allocation bitmap base sector */ +#endif + DWORD winsect; /* Current sector appearing in the win[] */ +} FATFS; + + + +/* Object ID and allocation information (FFOBJID) */ + +typedef struct { + FATFS* fs; /* Pointer to the hosting volume of this object */ + WORD id; /* Hosting volume mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if FF_FS_EXFAT + DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ +#endif +#if FF_FS_LOCK + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} FFOBJID; + + + +/* File object structure (FIL) */ + +typedef struct { +#if !FF_FS_TINY + BYTE buf[FF_MAX_SS]; /* File private data read/write window */ +#endif + FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !FF_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ +#endif +#if FF_USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FFOBJID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if FF_USE_LFN + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if FF_USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if FF_USE_LFN + TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ +#else + TCHAR fname[12 + 1]; /* File name */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (DIR* dp); /* Close an open directory */ +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +FRESULT f_setcp (WORD cp); /* Set current code page */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) +#define f_unmount(path) f_mount(0, path, 0) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !FF_FS_READONLY && !FF_FS_NORTC +DWORD get_fattime (void); +#endif + +/* LFN support functions */ +#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ +WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ +DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ +#endif +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif + +/* Sync functions */ +#if FF_FS_REENTRANT +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* FF_DEFINED */ diff --git a/emummc/source/libs/fatfs/ffconf.h b/emummc/source/libs/fatfs/ffconf.h new file mode 100644 index 000000000..a8be71b0a --- /dev/null +++ b/emummc/source/libs/fatfs/ffconf.h @@ -0,0 +1,287 @@ +/*---------------------------------------------------------------------------/ +/ FatFs Functional Configurations +/---------------------------------------------------------------------------*/ + +#define FFCONF_DEF 86604 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define FF_FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: Basic functions are fully enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define FF_USE_STRFUNC 2 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define FF_USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + + +#define FF_USE_MKFS 0 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define FF_USE_FASTSEEK 0 +/* This option switches fast seek function. (0:Disable or 1:Enable) */ + + +#define FF_USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + + +#define FF_USE_CHMOD 1 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ + + +#define FF_USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define FF_USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define FF_CODE_PAGE 850 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect code page setting can cause a file open failure. +/ +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +/ 0 - Include all code pages above and configured by f_setcp() +*/ + + +#define FF_USE_LFN 3 +#define FF_MAX_LFN 255 +/* The FF_USE_LFN switches the support for LFN (long file name). +/ +/ 0: Disable LFN. FF_MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function +/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and +/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. +/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can +/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN +/ specification. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree() in ffsystem.c, need to be added to the project. */ + + +#define FF_LFN_UNICODE 0 +/* This option switches the character encoding on the API when LFN is enabled. +/ +/ 0: ANSI/OEM in current CP (TCHAR = char) +/ 1: Unicode in UTF-16 (TCHAR = WCHAR) +/ 2: Unicode in UTF-8 (TCHAR = char) +/ 3: Unicode in UTF-32 (TCHAR = DWORD) +/ +/ Also behavior of string I/O functions will be affected by this option. +/ When LFN is not enabled, this option has no effect. */ + + +#define FF_LFN_BUF 255 +#define FF_SFN_BUF 12 +/* This set of options defines size of file name members in the FILINFO structure +/ which is used to read out directory items. These values should be suffcient for +/ the file names to read. The maximum possible length of the read file name depends +/ on character encoding. When LFN is not enabled, these options have no effect. */ + + +#define FF_STRF_ENCODE 0 +/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), +/ f_putc(), f_puts and f_printf() convert the character encoding in it. +/ This option selects assumption of character encoding ON THE FILE to be +/ read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + +#define FF_FS_RPATH 0 +/* This option configures support for relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define FF_VOLUMES 1 +/* Number of volumes (logical drives) to be used. (1-10) */ + + +#define FF_STR_VOLUME_ID 0 +#define FF_VOLUME_STRS "sd" +/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. +/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive +/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each +/ logical drives. Number of items must not be less than FF_VOLUMES. Valid +/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are +/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is +/ not defined, a user defined volume string table needs to be defined as: +/ +/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... +*/ + + +#define FF_MULTI_PARTITION 0 +/* This option switches support for multiple volumes on the physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When this function is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ funciton will be available. */ + + +#define FF_MIN_SS 512 +#define FF_MAX_SS 512 +/* This set of options configures the range of sector size to be supported. (512, +/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured +/ for variable sector size mode and disk_ioctl() function needs to implement +/ GET_SECTOR_SIZE command. */ + + +#define FF_USE_TRIM 0 +/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + +#define FF_FS_NOFSINFO 0 +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define FF_FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the filesystem object (FATFS) is used for the file data transfer. */ + + +#define FF_FS_EXFAT 1 +/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) +/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) +/ Note that enabling exFAT discards ANSI C (C89) compatibility. */ + + +#define FF_FS_NORTC 1 +#define FF_NORTC_MON 1 +#define FF_NORTC_MDAY 1 +#define FF_NORTC_YEAR 2019 +/* The option FF_FS_NORTC switches timestamp function. If the system does not have +/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable +/ the timestamp function. Every object modified by FatFs will have a fixed timestamp +/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. +/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to read current time form real-time clock. FF_NORTC_MON, +/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */ + + +#define FF_FS_LOCK 0 +/* The option FF_FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + + +#define FF_FS_REENTRANT 0 +#define FF_FS_TIMEOUT 1000 +#define FF_SYNC_t HANDLE +/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The FF_FS_TIMEOUT defines timeout period in unit of time tick. +/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + + + +/*--- End of configuration options ---*/ diff --git a/emummc/source/libs/fatfs/ffsystem.c b/emummc/source/libs/fatfs/ffsystem.c new file mode 100644 index 000000000..8485c07d3 --- /dev/null +++ b/emummc/source/libs/fatfs/ffsystem.c @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------*/ +/* Sample Code of OS Dependent Functions for FatFs */ +/* (C) ChaN, 2018 */ +/* (C) CTCaer, 2018 */ +/*------------------------------------------------------------------------*/ + + +#include "ff.h" +#include "../../utils/types.h" +#include + + +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ + +/*------------------------------------------------------------------------*/ +/* Allocate a memory block */ +/*------------------------------------------------------------------------*/ + +void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ + UINT msize /* Number of bytes to allocate */ +) +{ + return malloc(msize); /* Allocate a new memory block with POSIX API */ +} + + +/*------------------------------------------------------------------------*/ +/* Free a memory block */ +/*------------------------------------------------------------------------*/ + +void ff_memfree ( + void* mblock /* Pointer to the memory block to free (nothing to do if null) */ +) +{ + free(mblock); /* Free the memory block with POSIX API */ +} + +#endif + diff --git a/emummc/source/libs/fatfs/ffunicode.c b/emummc/source/libs/fatfs/ffunicode.c new file mode 100644 index 000000000..bc23f806c --- /dev/null +++ b/emummc/source/libs/fatfs/ffunicode.c @@ -0,0 +1,627 @@ +/*------------------------------------------------------------------------*/ +/* Unicode handling functions for FatFs R0.13c */ +/*------------------------------------------------------------------------*/ +/* This module will occupy a huge memory in the .const section when the / +/ FatFs is configured for LFN with DBCS. If the system has any Unicode / +/ utilitiy for the code conversion, this module should be modified to use / +/ that function to avoid silly memory consumption. / +/-------------------------------------------------------------------------*/ +/* +/ Copyright (C) 2018, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +*/ + + +#include "ff.h" + +#if FF_USE_LFN /* This module will be blanked at non-LFN configuration */ + +#if FF_DEFINED != 86604 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + +#define MERGE2(a, b) a ## b +#define CVTBL(tbl, cp) MERGE2(tbl, cp) + + +/*------------------------------------------------------------------------*/ +/* Code Conversion Tables */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0 +static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0 +static const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0 +static const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0 +static const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x0104, 0x0105, 0x010C, 0x010D, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0 +static const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0 +static const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0 +static const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0 +static const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0 +static const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0 +static const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, + 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0 +static const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0 +static const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0 +static const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, + 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, + 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2219, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0 +static const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */ + 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, + 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, + 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, + 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, + 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 +}; +#endif +#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0 +static const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0 +static const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; +#endif +#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0 +static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */ + 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, + 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, + 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, + 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, + 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 +}; +#endif + + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for static code page configuration */ +/* SBCS fixed code page */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900 +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + WCHAR c = 0; + const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */ + for (c = 0; c < 0x80 && uni != p[c]; c++) ; + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + WCHAR c = 0; + const WCHAR *p = CVTBL(uc, FF_CODE_PAGE); + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */ + if (oem < 0x100) c = p[oem - 0x80]; + } + } + + return c; +} + +#endif + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for static code page configuration */ +/* DBCS fixed code page */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE >= 900 +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0, uc; + UINT i = 0, n, li, hi; + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */ + uc = (WCHAR)uni; + p = CVTBL(uni2oem, FF_CODE_PAGE); + hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1; + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + + return c; +} + + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0; + UINT i = 0, n, li, hi; + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + if (cp == FF_CODE_PAGE) { /* Is it valid code page? */ + p = CVTBL(oem2uni, FF_CODE_PAGE); + hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1; + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (oem == p[i * 2]) break; + if (oem > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + + return c; +} +#endif + + + +/*------------------------------------------------------------------------*/ +/* OEM <==> Unicode conversions for dynamic code page configuration */ +/*------------------------------------------------------------------------*/ + +#if FF_CODE_PAGE == 0 + +static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0}; +static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0}; + + +WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */ + DWORD uni, /* UTF-16 encoded character to be converted */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0, uc; + UINT i, n, li, hi; + + + if (uni < 0x80) { /* ASCII? */ + c = (WCHAR)uni; + + } else { /* Non-ASCII */ + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WCHAR)uni; + p = 0; + if (cp < 900) { /* SBCS */ + for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get conversion table */ + p = cp_table[i]; + if (p) { /* Is it valid code page ? */ + for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */ + c = (c + 0x80) & 0xFF; + } + } else { /* DBCS */ + switch (cp) { /* Get conversion table */ + case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break; + case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break; + case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break; + case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break; + } + if (p) { /* Is it valid code page? */ + li = 0; + for (n = 16; n; n--) { /* Find OEM code */ + i = li + (hi - li) / 2; + if (uc == p[i * 2]) break; + if (uc > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + } + } + + return c; +} + + +WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */ + WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */ + WORD cp /* Code page for the conversion */ +) +{ + const WCHAR *p; + WCHAR c = 0; + UINT i, n, li, hi; + + + if (oem < 0x80) { /* ASCII? */ + c = oem; + + } else { /* Extended char */ + p = 0; + if (cp < 900) { /* SBCS */ + for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */ + p = cp_table[i]; + if (p) { /* Is it a valid CP ? */ + if (oem < 0x100) c = p[oem - 0x80]; + } + } else { /* DBCS */ + switch (cp) { + case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break; + case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break; + case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break; + case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break; + } + if (p) { + li = 0; + for (n = 16; n; n--) { + i = li + (hi - li) / 2; + if (oem == p[i * 2]) break; + if (oem > p[i * 2]) { + li = i; + } else { + hi = i; + } + } + if (n != 0) c = p[i * 2 + 1]; + } + } + } + + return c; +} +#endif + + + +/*------------------------------------------------------------------------*/ +/* Unicode up-case conversion */ +/*------------------------------------------------------------------------*/ + +DWORD ff_wtoupper ( /* Returns up-converted code point */ + DWORD uni /* Unicode code point to be up-converted */ +) +{ + const WORD *p; + WORD uc, bc, nc, cmd; + static const WORD cvt1[] = { /* Compressed up conversion table for U+0000 - U+0FFF */ + /* Basic Latin */ + 0x0061,0x031A, + /* Latin-1 Supplement */ + 0x00E0,0x0317, + 0x00F8,0x0307, + 0x00FF,0x0001,0x0178, + /* Latin Extended-A */ + 0x0100,0x0130, + 0x0132,0x0106, + 0x0139,0x0110, + 0x014A,0x012E, + 0x0179,0x0106, + /* Latin Extended-B */ + 0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA, + 0x01CD,0x0110, + 0x01DD,0x0001,0x018E, + 0x01DE,0x0112, + 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, + 0x01F8,0x0128, + 0x0222,0x0112, + 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, + 0x0246,0x010A, + /* IPA Extensions */ + 0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7, + /* Greek, Coptic */ + 0x037B,0x0003,0x03FD,0x03FE,0x03FF, + 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, + 0x03B1,0x0311, + 0x03C2,0x0002,0x03A3,0x03A3, + 0x03C4,0x0308, + 0x03CC,0x0003,0x038C,0x038E,0x038F, + 0x03D8,0x0118, + 0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA, + /* Cyrillic */ + 0x0430,0x0320, + 0x0450,0x0710, + 0x0460,0x0122, + 0x048A,0x0136, + 0x04C1,0x010E, + 0x04CF,0x0001,0x04C0, + 0x04D0,0x0144, + /* Armenian */ + 0x0561,0x0426, + + 0x0000 /* EOT */ + }; + static const WORD cvt2[] = { /* Compressed up conversion table for U+1000 - U+FFFF */ + /* Phonetic Extensions */ + 0x1D7D,0x0001,0x2C63, + /* Latin Extended Additional */ + 0x1E00,0x0196, + 0x1EA0,0x015A, + /* Greek Extended */ + 0x1F00,0x0608, + 0x1F10,0x0606, + 0x1F20,0x0608, + 0x1F30,0x0608, + 0x1F40,0x0606, + 0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, + 0x1F60,0x0608, + 0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB, + 0x1F80,0x0608, + 0x1F90,0x0608, + 0x1FA0,0x0608, + 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC, + 0x1FCC,0x0001,0x1FC3, + 0x1FD0,0x0602, + 0x1FE0,0x0602, + 0x1FE5,0x0001,0x1FEC, + 0x1FF3,0x0001,0x1FFC, + /* Letterlike Symbols */ + 0x214E,0x0001,0x2132, + /* Number forms */ + 0x2170,0x0210, + 0x2184,0x0001,0x2183, + /* Enclosed Alphanumerics */ + 0x24D0,0x051A, + 0x2C30,0x042F, + /* Latin Extended-C */ + 0x2C60,0x0102, + 0x2C67,0x0106, 0x2C75,0x0102, + /* Coptic */ + 0x2C80,0x0164, + /* Georgian Supplement */ + 0x2D00,0x0826, + /* Full-width */ + 0xFF41,0x031A, + + 0x0000 /* EOT */ + }; + + + if (uni < 0x10000) { /* Is it in BMP? */ + uc = (WORD)uni; + p = uc < 0x1000 ? cvt1 : cvt2; + for (;;) { + bc = *p++; /* Get the block base */ + if (bc == 0 || uc < bc) break; /* Not matched? */ + nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */ + if (uc < bc + nc) { /* In the block? */ + switch (cmd) { + case 0: uc = p[uc - bc]; break; /* Table conversion */ + case 1: uc -= (uc - bc) & 1; break; /* Case pairs */ + case 2: uc -= 16; break; /* Shift -16 */ + case 3: uc -= 32; break; /* Shift -32 */ + case 4: uc -= 48; break; /* Shift -48 */ + case 5: uc -= 26; break; /* Shift -26 */ + case 6: uc += 8; break; /* Shift +8 */ + case 7: uc -= 80; break; /* Shift -80 */ + case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */ + } + break; + } + if (cmd == 0) p += nc; /* Skip table if needed */ + } + uni = uc; + } + + return uni; +} + + +#endif /* #if FF_USE_LFN */ diff --git a/emummc/source/main.c b/emummc/source/main.c new file mode 100644 index 000000000..ee9835725 --- /dev/null +++ b/emummc/source/main.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include +#include +#include +#include +#include "nx/svc.h" +#include "nx/smc.h" +#include "emuMMC/emummc.h" +#include "emuMMC/emummc_ctx.h" +#include "FS/FS_offsets.h" +#include "utils/fatal.h" + +// Prototypes +void __init(); +void __initheap(void); +void setup_hooks(void); +void setup_nintendo_paths(void); +void __libc_init_array(void); +void hook_function(uintptr_t source, uintptr_t target); + +void *__stack_top; +uintptr_t text_base; +char inner_heap[INNER_HEAP_SIZE]; +size_t inner_heap_size = INNER_HEAP_SIZE; +extern char _start; +extern char __injected_size__; + +// Nintendo Path +// TODO +static char nintendo_path[0x80] = "Nintendo"; + +static char nintendo_path_album_100[0x100] = "/Nintendo/Album"; +static char nintendo_path_contents_100[0x100] = "/Nintendo/Contents"; + +// FS offsets +static const fs_offsets_t *fs_offsets; + +// Defined by linkerscript +#define INJECTED_SIZE ((uintptr_t)&__injected_size__ - (uintptr_t)&_start) +#define INJECT_OFFSET(type, offset) (type)(text_base + INJECTED_SIZE + offset) + +#define GENERATE_ADD(register, register_target, value) (0x91000000 | value << 10 | register << 5 | register_target) +#define GENERATE_ADRP(register, page_addr) (0x90000000 | ((((page_addr) >> 12) & 0x3) << 29) | ((((page_addr) >> 12) & 0x1FFFFC) << 3) | ((register) & 0x1F)) +#define GENERATE_BRANCH(source, destination) (0x14000000 | ((((destination) - (source)) >> 2) & 0x3FFFFFF)) +#define GENERATE_NOP() (0xD503201F) + +#define INJECT_HOOK(offset, destination) hook_function(INJECT_OFFSET(uintptr_t, offset), (uintptr_t)&destination) +#define INJECT_HOOK_RELATIVE(offset, relative_destination) hook_function(INJECT_OFFSET(uintptr_t, offset), INJECT_OFFSET(uintptr_t, offset) + relative_destination) +#define INJECT_NOP(offset) write_nop(INJECT_OFFSET(uintptr_t, offset)) + +// emuMMC +extern _sdmmc_accessor_gc sdmmc_accessor_gc; +extern _sdmmc_accessor_sd sdmmc_accessor_sd; +extern _sdmmc_accessor_nand sdmmc_accessor_nand; +extern _lock_mutex lock_mutex; +extern _unlock_mutex unlock_mutex; +extern void *sd_mutex; +extern void *nand_mutex; +extern volatile int *active_partition; +extern volatile Handle *sdmmc_das_handle; + +// Storage +volatile __attribute__((aligned(0x1000))) emuMMC_ctx_t emuMMC_ctx = { + .magic = EMUMMC_STORAGE_MAGIC, + .id = 0, + .fs_ver = FS_VER_MAX, + + // SD Default Metadata + .SD_Type = emuMMC_SD, + .SD_StoragePartitionOffset = 0, + + // EMMC Default Metadata + .EMMC_Type = emuMMC_EMMC, + .EMMC_StoragePartitionOffset = 0, + + // File Default Path + .storagePath = "", +}; + +// TODO: move into another file +typedef struct +{ + void *_0x0; + void *_0x8; + void *_0x10; + void *_0x18; + void *_0x20; + void *_0x28; + void *_0x30; + void *_0x38; + void *_0x40; + Result (*set_min_v_clock_rate)(void *, uint32_t, uint32_t); +} nn_clkrst_session_vt_t; + +typedef struct +{ + nn_clkrst_session_vt_t *vt; +} nn_clkrst_session_t; + +Result clkrst_set_min_v_clock_rate(nn_clkrst_session_t **_this, uint32_t clk_rate) +{ + Result rc = (*_this)->vt->set_min_v_clock_rate((void *)*_this, clk_rate, clk_rate); + + if (rc == 0x6C0 || rc == 0) + { // TODO #define + return 0; + } + if (rc != 0xAC0) + { // TODO #define + fatal_abort(Fatal_BadResult); + } + + return rc; +} + +void __initheap(void) +{ + void *addr = inner_heap; + size_t size = inner_heap_size; + + /* Newlib Heap Management */ + extern char *fake_heap_start; + extern char *fake_heap_end; + + fake_heap_start = (char *)addr; + fake_heap_end = (char *)addr + size; +} + +void hook_function(uintptr_t source, uintptr_t target) +{ + u32 branch_opcode = GENERATE_BRANCH(source, target); + smcWriteAddress32((void *)source, branch_opcode); +} + +void write_nop(uintptr_t source) +{ + smcWriteAddress32((void *)source, GENERATE_NOP()); +} + +void write_adrp_add(int reg, uintptr_t pc, intptr_t destination) +{ + uintptr_t add_opcode_location = pc + sizeof(uint32_t); + + intptr_t offset = (destination & 0xFFFFF000) - (pc & 0xFFFFF000); + uint32_t opcode_adrp = GENERATE_ADRP(reg, offset); + uint32_t opcode_add = GENERATE_ADD(reg, reg, (destination & 0x00000FFF)); + + // TODO: use 64 write? + smcWriteAddress32((void *)pc, opcode_adrp); + smcWriteAddress32((void *)add_opcode_location, opcode_add); +} + +void setup_hooks(void) +{ + // rtld + INJECT_HOOK_RELATIVE(fs_offsets->rtld, fs_offsets->rtld_destination); + // sdmmc_wrapper_read hook + INJECT_HOOK(fs_offsets->sdmmc_wrapper_read, sdmmc_wrapper_read); + // sdmmc_wrapper_write hook + INJECT_HOOK(fs_offsets->sdmmc_wrapper_write, sdmmc_wrapper_write); + + // On 8.0.0+, we need to hook the regulator setup, because + // otherwise it will abort because we have already turned it on. + if (emuMMC_ctx.fs_ver >= FS_VER_8_0_0) + { + INJECT_HOOK(fs_offsets->clkrst_set_min_v_clock_rate, clkrst_set_min_v_clock_rate); + } +} + +void populate_function_pointers(void) +{ + // Accessor getters + sdmmc_accessor_gc = INJECT_OFFSET(_sdmmc_accessor_gc, fs_offsets->sdmmc_accessor_gc); + sdmmc_accessor_sd = INJECT_OFFSET(_sdmmc_accessor_sd, fs_offsets->sdmmc_accessor_sd); + sdmmc_accessor_nand = INJECT_OFFSET(_sdmmc_accessor_nand, fs_offsets->sdmmc_accessor_nand); + + // MutexLock functions + lock_mutex = INJECT_OFFSET(_lock_mutex, fs_offsets->lock_mutex); + unlock_mutex = INJECT_OFFSET(_unlock_mutex, fs_offsets->unlock_mutex); + + // Other + sd_mutex = INJECT_OFFSET(void *, fs_offsets->sd_mutex); + nand_mutex = INJECT_OFFSET(void *, fs_offsets->nand_mutex); + active_partition = INJECT_OFFSET(volatile int *, fs_offsets->active_partition); + sdmmc_das_handle = INJECT_OFFSET(volatile Handle *, fs_offsets->sdmmc_das_handle); +} + +void write_nops(void) +{ + // This NOPs out a call to ShutdownSdCard when preparing for shutdown/reboot. + // This prevents the PatrolReader from hanging when saving its state, which + // occurs immediately afterwards (in ShutdownMmc). + INJECT_NOP(fs_offsets->shutdown_sd); + // On 7.0.0+, we need to attach to device address space ourselves. + // This patches an abort that happens when Nintendo's code sees SD + // is already attached + if (emuMMC_ctx.fs_ver >= FS_VER_7_0_0) + { + INJECT_NOP(fs_offsets->sd_das_init); + } +} + +static void load_emummc_ctx(void) +{ + exo_emummc_config_t config; + static struct + { + char storage_path[sizeof(emuMMC_ctx.storagePath)]; + char nintendo_path[sizeof(nintendo_path)]; + } __attribute__((aligned(0x1000))) paths; + + int x = smcGetEmummcConfig(EXO_EMUMMC_MMC_NAND, &config, &paths); + if (x != 0) + { + fatal_abort(Fatal_GetConfig); + } + + if (config.base_cfg.magic == EMUMMC_STORAGE_MAGIC) + { + emuMMC_ctx.magic = config.base_cfg.magic; + emuMMC_ctx.id = config.base_cfg.id; + emuMMC_ctx.EMMC_Type = (enum emuMMC_Type)config.base_cfg.type; + emuMMC_ctx.fs_ver = (enum FS_VER)config.base_cfg.fs_version; + if (emuMMC_ctx.EMMC_Type == emuMMC_SD) + { + emuMMC_ctx.EMMC_StoragePartitionOffset = config.partition_cfg.start_sector; + } + else if (emuMMC_ctx.EMMC_Type == emuMMC_SD_File) + { + memcpy((void *)emuMMC_ctx.storagePath, paths.storage_path, sizeof(emuMMC_ctx.storagePath) - 1); + emuMMC_ctx.storagePath[sizeof(emuMMC_ctx.storagePath) - 1] = 0; + } + memcpy(nintendo_path, paths.nintendo_path, sizeof(nintendo_path) - 1); + nintendo_path[sizeof(nintendo_path) - 1] = 0; + if (strcmp(nintendo_path, "") == 0) + { + snprintf(nintendo_path, sizeof(nintendo_path), "emummc/Nintendo_%04x", emuMMC_ctx.id); + } + } + else + { + fatal_abort(Fatal_GetConfig); + } +} + +void setup_nintendo_paths(void) +{ + if (emuMMC_ctx.fs_ver > FS_VER_1_0_0) + { + for (int i = 0; fs_offsets->nintendo_paths[i].adrp_offset; i++) + { + intptr_t nintendo_path_location = (intptr_t)&nintendo_path; + uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[i].adrp_offset); + write_adrp_add(fs_offsets->nintendo_paths[i].opcode_reg, fs_adrp_opcode_location, nintendo_path_location); + } + } + else + { + // 1.0.0 needs special handling because it uses two paths. + // Do album path + { + int path_len = snprintf(nintendo_path_album_100, sizeof(nintendo_path_album_100), "/%s/Album", nintendo_path); + intptr_t nintendo_album_path_location = (intptr_t)&nintendo_path_album_100; + intptr_t album_path_location = nintendo_album_path_location + path_len - 6; // "/Album" + uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[0].adrp_offset); + uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[1].adrp_offset); + write_adrp_add(fs_offsets->nintendo_paths[0].opcode_reg, fs_n_adrp_opcode_location, nintendo_album_path_location); + write_adrp_add(fs_offsets->nintendo_paths[1].opcode_reg, fs_adrp_opcode_location, album_path_location); + } + // Do contents path + { + int path_len = snprintf(nintendo_path_contents_100, sizeof(nintendo_path_contents_100), "/%s/Contents", nintendo_path); + intptr_t nintendo_contents_path_location = (intptr_t)&nintendo_path_contents_100; + intptr_t contents_path_location = nintendo_contents_path_location + path_len - 9; // "/Contents" + uintptr_t fs_n_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[2].adrp_offset); + uintptr_t fs_adrp_opcode_location = INJECT_OFFSET(uintptr_t, fs_offsets->nintendo_paths[3].adrp_offset); + write_adrp_add(fs_offsets->nintendo_paths[2].opcode_reg, fs_n_adrp_opcode_location, nintendo_contents_path_location); + write_adrp_add(fs_offsets->nintendo_paths[3].opcode_reg, fs_adrp_opcode_location, contents_path_location); + } + } +} + +// inject main func +void __init() +{ + // Call constructors. + __libc_init_array(); + + MemoryInfo meminfo; + u32 pageinfo; + svcQueryMemory(&meminfo, &pageinfo, (u64)&_start); + + text_base = meminfo.addr; + + load_emummc_ctx(); + + fs_offsets = get_fs_offsets(emuMMC_ctx.fs_ver); + + setup_hooks(); + populate_function_pointers(); + write_nops(); + setup_nintendo_paths(); + + sdmmc_initialize(); +} diff --git a/emummc/source/nx/cache.h b/emummc/source/nx/cache.h new file mode 100644 index 000000000..f3a646d2d --- /dev/null +++ b/emummc/source/nx/cache.h @@ -0,0 +1,41 @@ +/** + * @file cache.h + * @brief AArch64 cache operations. + * @author plutoo + * @copyright libnx Authors + */ +#pragma once +#include "../utils/types.h" + +/** + * @brief Performs a data cache flush on the specified buffer. + * @param addr Address of the buffer. + * @param size Size of the buffer, in bytes. + * @remarks Cache flush is defined as Clean + Invalidate. + * @note The start and end addresses of the buffer are forcibly rounded to cache line boundaries (read from CTR_EL0 system register). + */ +void armDCacheFlush(void* addr, size_t size); + +/** + * @brief Performs a data cache clean on the specified buffer. + * @param addr Address of the buffer. + * @param size Size of the buffer, in bytes. + * @note The start and end addresses of the buffer are forcibly rounded to cache line boundaries (read from CTR_EL0 system register). + */ +void armDCacheClean(void* addr, size_t size); + +/** + * @brief Performs an instruction cache invalidation clean on the specified buffer. + * @param addr Address of the buffer. + * @param size Size of the buffer, in bytes. + * @note The start and end addresses of the buffer are forcibly rounded to cache line boundaries (read from CTR_EL0 system register). + */ +void armICacheInvalidate(void* addr, size_t size); + +/** + * @brief Performs a data cache zeroing operation on the specified buffer. + * @param addr Address of the buffer. + * @param size Size of the buffer, in bytes. + * @note The start and end addresses of the buffer are forcibly rounded to cache line boundaries (read from CTR_EL0 system register). + */ +void armDCacheZero(void* addr, size_t size); diff --git a/emummc/source/nx/cache.s b/emummc/source/nx/cache.s new file mode 100644 index 000000000..7facecb1f --- /dev/null +++ b/emummc/source/nx/cache.s @@ -0,0 +1,100 @@ +/** + * @file cache.s + * @copyright libnx Authors + */ + +.macro CODE_BEGIN name + .section .text.\name, "ax", %progbits + .global \name + .type \name, %function + .align 2 + .cfi_startproc +\name: +.endm + +.macro CODE_END + .cfi_endproc +.endm + +CODE_BEGIN armDCacheFlush + add x1, x1, x0 + mrs x8, CTR_EL0 + lsr x8, x8, #16 + and x8, x8, #0xf + mov x9, #4 + lsl x9, x9, x8 + sub x10, x9, #1 + bic x8, x0, x10 + mov x10, x1 + +armDCacheFlush_L0: + dc civac, x8 + add x8, x8, x9 + cmp x8, x10 + bcc armDCacheFlush_L0 + + dsb sy + ret +CODE_END + +CODE_BEGIN armDCacheClean + add x1, x1, x0 + mrs x8, CTR_EL0 + lsr x8, x8, #16 + and x8, x8, #0xf + mov x9, #4 + lsl x9, x9, x8 + sub x10, x9, #1 + bic x8, x0, x10 + mov x10, x1 + +armDCacheClean_L0: + dc cvac, x8 + add x8, x8, x9 + cmp x8, x10 + bcc armDCacheClean_L0 + + dsb sy + ret +CODE_END + +CODE_BEGIN armICacheInvalidate + add x1, x1, x0 + mrs x8, CTR_EL0 + and x8, x8, #0xf + mov x9, #4 + lsl x9, x9, x8 + sub x10, x9, #1 + bic x8, x0, x10 + mov x10, x1 + +armICacheInvalidate_L0: + ic ivau, x8 + add x8, x8, x9 + cmp x8, x10 + bcc armICacheInvalidate_L0 + + dsb sy + ret +CODE_END + +CODE_BEGIN armDCacheZero + add x1, x1, x0 + mrs x8, CTR_EL0 + lsr x8, x8, #16 + and x8, x8, #0xf + mov x9, #4 + lsl x9, x9, x8 + sub x10, x9, #1 + bic x8, x0, x10 + mov x10, x1 + +armDCacheZero_L0: + dc zva, x8 + add x8, x8, x9 + cmp x8, x10 + bcc armDCacheZero_L0 + + dsb sy + ret +CODE_END diff --git a/emummc/source/nx/counter.h b/emummc/source/nx/counter.h new file mode 100644 index 000000000..072be6417 --- /dev/null +++ b/emummc/source/nx/counter.h @@ -0,0 +1,46 @@ +/** + * @file counter.h + * @brief AArch64 system counter-timer. + * @author fincs + * @copyright libnx Authors + */ +#pragma once +#include "../utils/types.h" + +/** + * @brief Gets the current system tick. + * @return The current system tick. + */ +static inline u64 armGetSystemTick(void) { + u64 ret; + __asm__ __volatile__ ("mrs %x[data], cntpct_el0" : [data] "=r" (ret)); + return ret; +} + +/** + * @brief Gets the system counter-timer frequency + * @return The system counter-timer frequency, in Hz. + */ +static inline u64 armGetSystemTickFreq(void) { + u64 ret; + __asm__ ("mrs %x[data], cntfrq_el0" : [data] "=r" (ret)); + return ret; +} + +/** + * @brief Converts from nanoseconds to CPU ticks unit. + * @param ns Time in nanoseconds. + * @return Time in CPU ticks. + */ +static inline u64 armNsToTicks(u64 ns) { + return (ns * 12) / 625; +} + +/** + * @brief Converts from CPU ticks unit to nanoseconds. + * @param tick Time in ticks. + * @return Time in nanoseconds. + */ +static inline u64 armTicksToNs(u64 tick) { + return (tick * 625) / 12; +} diff --git a/emummc/source/nx/dynamic.c b/emummc/source/nx/dynamic.c new file mode 100644 index 000000000..f1a24f88c --- /dev/null +++ b/emummc/source/nx/dynamic.c @@ -0,0 +1,45 @@ +/** + * @file dynamic.c + * @copyright libnx Authors + */ + +#include +#include "../utils/types.h" +#include + +void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn) +{ + const Elf64_Rela* rela = NULL; + u64 relasz = 0; + + for (; dyn->d_tag != DT_NULL; dyn++) + { + switch (dyn->d_tag) + { + case DT_RELA: + rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr); + break; + case DT_RELASZ: + relasz = dyn->d_un.d_val / sizeof(Elf64_Rela); + break; + } + } + + if (rela == NULL) { + while(true) + ; + } + + for (; relasz--; rela++) + { + switch (ELF64_R_TYPE(rela->r_info)) + { + case R_AARCH64_RELATIVE: + { + u64* ptr = (u64*)(base + rela->r_offset); + *ptr = base + rela->r_addend; + break; + } + } + } +} diff --git a/emummc/source/nx/elf.h b/emummc/source/nx/elf.h new file mode 100644 index 000000000..79d3b974b --- /dev/null +++ b/emummc/source/nx/elf.h @@ -0,0 +1,3147 @@ +/* +From musl include/elf.h + +Copyright © 2005-2014 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _ELF_H +#define _ELF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + +#define EI_NIDENT (16) + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +#define EI_MAG0 0 +#define ELFMAG0 0x7f + +#define EI_MAG1 1 +#define ELFMAG1 'E' + +#define EI_MAG2 2 +#define ELFMAG2 'L' + +#define EI_MAG3 3 +#define ELFMAG3 'F' + + +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define EI_DATA 5 +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 +#define ELFDATANUM 3 + +#define EI_VERSION 6 + + +#define EI_OSABI 7 +#define ELFOSABI_NONE 0 +#define ELFOSABI_SYSV 0 +#define ELFOSABI_HPUX 1 +#define ELFOSABI_NETBSD 2 +#define ELFOSABI_LINUX 3 +#define ELFOSABI_GNU 3 +#define ELFOSABI_SOLARIS 6 +#define ELFOSABI_AIX 7 +#define ELFOSABI_IRIX 8 +#define ELFOSABI_FREEBSD 9 +#define ELFOSABI_TRU64 10 +#define ELFOSABI_MODESTO 11 +#define ELFOSABI_OPENBSD 12 +#define ELFOSABI_ARM 97 +#define ELFOSABI_STANDALONE 255 + +#define EI_ABIVERSION 8 + +#define EI_PAD 9 + + + +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_NUM 5 +#define ET_LOOS 0xfe00 +#define ET_HIOS 0xfeff +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + + + +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_S370 9 +#define EM_MIPS_RS3_LE 10 + +#define EM_PARISC 15 +#define EM_VPP500 17 +#define EM_SPARC32PLUS 18 +#define EM_960 19 +#define EM_PPC 20 +#define EM_PPC64 21 +#define EM_S390 22 + +#define EM_V800 36 +#define EM_FR20 37 +#define EM_RH32 38 +#define EM_RCE 39 +#define EM_ARM 40 +#define EM_FAKE_ALPHA 41 +#define EM_SH 42 +#define EM_SPARCV9 43 +#define EM_TRICORE 44 +#define EM_ARC 45 +#define EM_H8_300 46 +#define EM_H8_300H 47 +#define EM_H8S 48 +#define EM_H8_500 49 +#define EM_IA_64 50 +#define EM_MIPS_X 51 +#define EM_COLDFIRE 52 +#define EM_68HC12 53 +#define EM_MMA 54 +#define EM_PCP 55 +#define EM_NCPU 56 +#define EM_NDR1 57 +#define EM_STARCORE 58 +#define EM_ME16 59 +#define EM_ST100 60 +#define EM_TINYJ 61 +#define EM_X86_64 62 +#define EM_PDSP 63 + +#define EM_FX66 66 +#define EM_ST9PLUS 67 +#define EM_ST7 68 +#define EM_68HC16 69 +#define EM_68HC11 70 +#define EM_68HC08 71 +#define EM_68HC05 72 +#define EM_SVX 73 +#define EM_ST19 74 +#define EM_VAX 75 +#define EM_CRIS 76 +#define EM_JAVELIN 77 +#define EM_FIREPATH 78 +#define EM_ZSP 79 +#define EM_MMIX 80 +#define EM_HUANY 81 +#define EM_PRISM 82 +#define EM_AVR 83 +#define EM_FR30 84 +#define EM_D10V 85 +#define EM_D30V 86 +#define EM_V850 87 +#define EM_M32R 88 +#define EM_MN10300 89 +#define EM_MN10200 90 +#define EM_PJ 91 +#define EM_OR1K 92 +#define EM_OPENRISC 92 +#define EM_ARC_A5 93 +#define EM_ARC_COMPACT 93 +#define EM_XTENSA 94 +#define EM_VIDEOCORE 95 +#define EM_TMM_GPP 96 +#define EM_NS32K 97 +#define EM_TPC 98 +#define EM_SNP1K 99 +#define EM_ST200 100 +#define EM_IP2K 101 +#define EM_MAX 102 +#define EM_CR 103 +#define EM_F2MC16 104 +#define EM_MSP430 105 +#define EM_BLACKFIN 106 +#define EM_SE_C33 107 +#define EM_SEP 108 +#define EM_ARCA 109 +#define EM_UNICORE 110 +#define EM_EXCESS 111 +#define EM_DXP 112 +#define EM_ALTERA_NIOS2 113 +#define EM_CRX 114 +#define EM_XGATE 115 +#define EM_C166 116 +#define EM_M16C 117 +#define EM_DSPIC30F 118 +#define EM_CE 119 +#define EM_M32C 120 +#define EM_TSK3000 131 +#define EM_RS08 132 +#define EM_SHARC 133 +#define EM_ECOG2 134 +#define EM_SCORE7 135 +#define EM_DSP24 136 +#define EM_VIDEOCORE3 137 +#define EM_LATTICEMICO32 138 +#define EM_SE_C17 139 +#define EM_TI_C6000 140 +#define EM_TI_C2000 141 +#define EM_TI_C5500 142 +#define EM_TI_ARP32 143 +#define EM_TI_PRU 144 +#define EM_MMDSP_PLUS 160 +#define EM_CYPRESS_M8C 161 +#define EM_R32C 162 +#define EM_TRIMEDIA 163 +#define EM_QDSP6 164 +#define EM_8051 165 +#define EM_STXP7X 166 +#define EM_NDS32 167 +#define EM_ECOG1X 168 +#define EM_MAXQ30 169 +#define EM_XIMO16 170 +#define EM_MANIK 171 +#define EM_CRAYNV2 172 +#define EM_RX 173 +#define EM_METAG 174 +#define EM_MCST_ELBRUS 175 +#define EM_ECOG16 176 +#define EM_CR16 177 +#define EM_ETPU 178 +#define EM_SLE9X 179 +#define EM_L10M 180 +#define EM_K10M 181 +#define EM_AARCH64 183 +#define EM_AVR32 185 +#define EM_STM8 186 +#define EM_TILE64 187 +#define EM_TILEPRO 188 +#define EM_MICROBLAZE 189 +#define EM_CUDA 190 +#define EM_TILEGX 191 +#define EM_CLOUDSHIELD 192 +#define EM_COREA_1ST 193 +#define EM_COREA_2ND 194 +#define EM_ARC_COMPACT2 195 +#define EM_OPEN8 196 +#define EM_RL78 197 +#define EM_VIDEOCORE5 198 +#define EM_78KOR 199 +#define EM_56800EX 200 +#define EM_BA1 201 +#define EM_BA2 202 +#define EM_XCORE 203 +#define EM_MCHP_PIC 204 +#define EM_KM32 210 +#define EM_KMX32 211 +#define EM_EMX16 212 +#define EM_EMX8 213 +#define EM_KVARC 214 +#define EM_CDP 215 +#define EM_COGE 216 +#define EM_COOL 217 +#define EM_NORC 218 +#define EM_CSR_KALIMBA 219 +#define EM_Z80 220 +#define EM_VISIUM 221 +#define EM_FT32 222 +#define EM_MOXIE 223 +#define EM_AMDGPU 224 +#define EM_RISCV 243 +#define EM_BPF 247 +#define EM_NUM 248 + +#define EM_ALPHA 0x9026 + +#define EV_NONE 0 +#define EV_CURRENT 1 +#define EV_NUM 2 + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + + + +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_BEFORE 0xff00 + +#define SHN_AFTER 0xff01 + +#define SHN_HIPROC 0xff1f +#define SHN_LOOS 0xff20 +#define SHN_HIOS 0xff3f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_XINDEX 0xffff +#define SHN_HIRESERVE 0xffff + + + +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_NUM 19 +#define SHT_LOOS 0x60000000 +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 +#define SHT_GNU_HASH 0x6ffffff6 +#define SHT_GNU_LIBLIST 0x6ffffff7 +#define SHT_CHECKSUM 0x6ffffff8 +#define SHT_LOSUNW 0x6ffffffa +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd +#define SHT_GNU_verneed 0x6ffffffe +#define SHT_GNU_versym 0x6fffffff +#define SHT_HISUNW 0x6fffffff +#define SHT_HIOS 0x6fffffff +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0x8fffffff + +#define SHF_WRITE (1 << 0) +#define SHF_ALLOC (1 << 1) +#define SHF_EXECINSTR (1 << 2) +#define SHF_MERGE (1 << 4) +#define SHF_STRINGS (1 << 5) +#define SHF_INFO_LINK (1 << 6) +#define SHF_LINK_ORDER (1 << 7) +#define SHF_OS_NONCONFORMING (1 << 8) + +#define SHF_GROUP (1 << 9) +#define SHF_TLS (1 << 10) +#define SHF_COMPRESSED (1 << 11) +#define SHF_MASKOS 0x0ff00000 +#define SHF_MASKPROC 0xf0000000 +#define SHF_ORDERED (1 << 30) +#define SHF_EXCLUDE (1U << 31) + +typedef struct { + Elf32_Word ch_type; + Elf32_Word ch_size; + Elf32_Word ch_addralign; +} Elf32_Chdr; + +typedef struct { + Elf64_Word ch_type; + Elf64_Word ch_reserved; + Elf64_Xword ch_size; + Elf64_Xword ch_addralign; +} Elf64_Chdr; + +#define ELFCOMPRESS_ZLIB 1 +#define ELFCOMPRESS_LOOS 0x60000000 +#define ELFCOMPRESS_HIOS 0x6fffffff +#define ELFCOMPRESS_LOPROC 0x70000000 +#define ELFCOMPRESS_HIPROC 0x7fffffff + + +#define GRP_COMDAT 0x1 + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Section st_shndx; +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Section st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +typedef struct { + Elf32_Half si_boundto; + Elf32_Half si_flags; +} Elf32_Syminfo; + +typedef struct { + Elf64_Half si_boundto; + Elf64_Half si_flags; +} Elf64_Syminfo; + +#define SYMINFO_BT_SELF 0xffff +#define SYMINFO_BT_PARENT 0xfffe +#define SYMINFO_BT_LOWRESERVE 0xff00 + +#define SYMINFO_FLG_DIRECT 0x0001 +#define SYMINFO_FLG_PASSTHRU 0x0002 +#define SYMINFO_FLG_COPY 0x0004 +#define SYMINFO_FLG_LAZYLOAD 0x0008 + +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_NUM 3 +#define STB_LOOS 10 +#define STB_GNU_UNIQUE 10 +#define STB_HIOS 12 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 +#define STT_NUM 7 +#define STT_LOOS 10 +#define STT_GNU_IFUNC 10 +#define STT_HIOS 12 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +#define STN_UNDEF 0 + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +#define STV_DEFAULT 0 +#define STV_INTERNAL 1 +#define STV_HIDDEN 2 +#define STV_PROTECTED 3 + + + + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + + + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + + + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + + + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + + + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 +#define PT_NUM 8 +#define PT_LOOS 0x60000000 +#define PT_GNU_EH_FRAME 0x6474e550 +#define PT_GNU_STACK 0x6474e551 +#define PT_GNU_RELRO 0x6474e552 +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa +#define PT_SUNWSTACK 0x6ffffffb +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + + +#define PN_XNUM 0xffff + + +#define PF_X (1 << 0) +#define PF_W (1 << 1) +#define PF_R (1 << 2) +#define PF_MASKOS 0x0ff00000 +#define PF_MASKPROC 0xf0000000 + + + +#define NT_PRSTATUS 1 +#define NT_FPREGSET 2 +#define NT_PRPSINFO 3 +#define NT_PRXREG 4 +#define NT_TASKSTRUCT 4 +#define NT_PLATFORM 5 +#define NT_AUXV 6 +#define NT_GWINDOWS 7 +#define NT_ASRS 8 +#define NT_PSTATUS 10 +#define NT_PSINFO 13 +#define NT_PRCRED 14 +#define NT_UTSNAME 15 +#define NT_LWPSTATUS 16 +#define NT_LWPSINFO 17 +#define NT_PRFPXREG 20 +#define NT_SIGINFO 0x53494749 +#define NT_FILE 0x46494c45 +#define NT_PRXFPREG 0x46e62b7f +#define NT_PPC_VMX 0x100 +#define NT_PPC_SPE 0x101 +#define NT_PPC_VSX 0x102 +#define NT_386_TLS 0x200 +#define NT_386_IOPERM 0x201 +#define NT_X86_XSTATE 0x202 +#define NT_S390_HIGH_GPRS 0x300 +#define NT_S390_TIMER 0x301 +#define NT_S390_TODCMP 0x302 +#define NT_S390_TODPREG 0x303 +#define NT_S390_CTRS 0x304 +#define NT_S390_PREFIX 0x305 +#define NT_S390_LAST_BREAK 0x306 +#define NT_S390_SYSTEM_CALL 0x307 +#define NT_S390_TDB 0x308 +#define NT_ARM_VFP 0x400 +#define NT_ARM_TLS 0x401 +#define NT_ARM_HW_BREAK 0x402 +#define NT_ARM_HW_WATCH 0x403 +#define NT_ARM_SYSTEM_CALL 0x404 +#define NT_ARM_SVE 0x405 +#define NT_METAG_CBUF 0x500 +#define NT_METAG_RPIPE 0x501 +#define NT_METAG_TLS 0x502 +#define NT_VERSION 1 + + + + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + + + +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_BIND_NOW 24 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_ENCODING 32 +#define DT_PREINIT_ARRAY 32 +#define DT_PREINIT_ARRAYSZ 33 +#define DT_NUM 34 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff +#define DT_PROCNUM DT_MIPS_NUM + +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc +#define DT_POSFLAG_1 0x6ffffdfd + +#define DT_SYMINSZ 0x6ffffdfe +#define DT_SYMINENT 0x6ffffdff +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) +#define DT_VALNUM 12 + +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 +#define DT_GNU_LIBLIST 0x6ffffef9 +#define DT_CONFIG 0x6ffffefa +#define DT_DEPAUDIT 0x6ffffefb +#define DT_AUDIT 0x6ffffefc +#define DT_PLTPAD 0x6ffffefd +#define DT_MOVETAB 0x6ffffefe +#define DT_SYMINFO 0x6ffffeff +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) +#define DT_ADDRNUM 11 + + + +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + + +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc + +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe + +#define DT_VERNEEDNUM 0x6fffffff +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) +#define DT_VERSIONTAGNUM 16 + + + +#define DT_AUXILIARY 0x7ffffffd +#define DT_FILTER 0x7fffffff +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + + +#define DF_ORIGIN 0x00000001 +#define DF_SYMBOLIC 0x00000002 +#define DF_TEXTREL 0x00000004 +#define DF_BIND_NOW 0x00000008 +#define DF_STATIC_TLS 0x00000010 + + + +#define DF_1_NOW 0x00000001 +#define DF_1_GLOBAL 0x00000002 +#define DF_1_GROUP 0x00000004 +#define DF_1_NODELETE 0x00000008 +#define DF_1_LOADFLTR 0x00000010 +#define DF_1_INITFIRST 0x00000020 +#define DF_1_NOOPEN 0x00000040 +#define DF_1_ORIGIN 0x00000080 +#define DF_1_DIRECT 0x00000100 +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 +#define DF_1_NODEFLIB 0x00000800 +#define DF_1_NODUMP 0x00001000 +#define DF_1_CONFALT 0x00002000 +#define DF_1_ENDFILTEE 0x00004000 +#define DF_1_DISPRELDNE 0x00008000 +#define DF_1_DISPRELPND 0x00010000 +#define DF_1_NODIRECT 0x00020000 +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 +#define DF_1_GLOBAUDIT 0x01000000 +#define DF_1_SINGLETON 0x02000000 + +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + + +#define DF_P1_LAZYLOAD 0x00000001 +#define DF_P1_GROUPPERM 0x00000002 + + + + +typedef struct { + Elf32_Half vd_version; + Elf32_Half vd_flags; + Elf32_Half vd_ndx; + Elf32_Half vd_cnt; + Elf32_Word vd_hash; + Elf32_Word vd_aux; + Elf32_Word vd_next; +} Elf32_Verdef; + +typedef struct { + Elf64_Half vd_version; + Elf64_Half vd_flags; + Elf64_Half vd_ndx; + Elf64_Half vd_cnt; + Elf64_Word vd_hash; + Elf64_Word vd_aux; + Elf64_Word vd_next; +} Elf64_Verdef; + + + +#define VER_DEF_NONE 0 +#define VER_DEF_CURRENT 1 +#define VER_DEF_NUM 2 + + +#define VER_FLG_BASE 0x1 +#define VER_FLG_WEAK 0x2 + + +#define VER_NDX_LOCAL 0 +#define VER_NDX_GLOBAL 1 +#define VER_NDX_LORESERVE 0xff00 +#define VER_NDX_ELIMINATE 0xff01 + + + +typedef struct { + Elf32_Word vda_name; + Elf32_Word vda_next; +} Elf32_Verdaux; + +typedef struct { + Elf64_Word vda_name; + Elf64_Word vda_next; +} Elf64_Verdaux; + + + + +typedef struct { + Elf32_Half vn_version; + Elf32_Half vn_cnt; + Elf32_Word vn_file; + Elf32_Word vn_aux; + Elf32_Word vn_next; +} Elf32_Verneed; + +typedef struct { + Elf64_Half vn_version; + Elf64_Half vn_cnt; + Elf64_Word vn_file; + Elf64_Word vn_aux; + Elf64_Word vn_next; +} Elf64_Verneed; + + + +#define VER_NEED_NONE 0 +#define VER_NEED_CURRENT 1 +#define VER_NEED_NUM 2 + + + +typedef struct { + Elf32_Word vna_hash; + Elf32_Half vna_flags; + Elf32_Half vna_other; + Elf32_Word vna_name; + Elf32_Word vna_next; +} Elf32_Vernaux; + +typedef struct { + Elf64_Word vna_hash; + Elf64_Half vna_flags; + Elf64_Half vna_other; + Elf64_Word vna_name; + Elf64_Word vna_next; +} Elf64_Vernaux; + + + +#define VER_FLG_WEAK 0x2 + + + +typedef struct { + uint32_t a_type; + union { + uint32_t a_val; + } a_un; +} Elf32_auxv_t; + +typedef struct { + uint64_t a_type; + union { + uint64_t a_val; + } a_un; +} Elf64_auxv_t; + + + +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 + + +#define AT_PLATFORM 15 +#define AT_HWCAP 16 + + + + +#define AT_FPUCW 18 + + +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 + + + +#define AT_IGNOREPPC 22 + +#define AT_SECURE 23 + +#define AT_BASE_PLATFORM 24 + +#define AT_RANDOM 25 + +#define AT_HWCAP2 26 + +#define AT_EXECFN 31 + + + +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + + + +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + + + + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + + + + +#define ELF_NOTE_SOLARIS "SUNW Solaris" + + +#define ELF_NOTE_GNU "GNU" + + + + + +#define ELF_NOTE_PAGESIZE_HINT 1 + + +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG + + + +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +#define NT_GNU_BUILD_ID 3 +#define NT_GNU_GOLD_VERSION 4 + + + +typedef struct { + Elf32_Xword m_value; + Elf32_Word m_info; + Elf32_Word m_poffset; + Elf32_Half m_repeat; + Elf32_Half m_stride; +} Elf32_Move; + +typedef struct { + Elf64_Xword m_value; + Elf64_Xword m_info; + Elf64_Xword m_poffset; + Elf64_Half m_repeat; + Elf64_Half m_stride; +} Elf64_Move; + + +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + +#define EF_CPU32 0x00810000 + +#define R_68K_NONE 0 +#define R_68K_32 1 +#define R_68K_16 2 +#define R_68K_8 3 +#define R_68K_PC32 4 +#define R_68K_PC16 5 +#define R_68K_PC8 6 +#define R_68K_GOT32 7 +#define R_68K_GOT16 8 +#define R_68K_GOT8 9 +#define R_68K_GOT32O 10 +#define R_68K_GOT16O 11 +#define R_68K_GOT8O 12 +#define R_68K_PLT32 13 +#define R_68K_PLT16 14 +#define R_68K_PLT8 15 +#define R_68K_PLT32O 16 +#define R_68K_PLT16O 17 +#define R_68K_PLT8O 18 +#define R_68K_COPY 19 +#define R_68K_GLOB_DAT 20 +#define R_68K_JMP_SLOT 21 +#define R_68K_RELATIVE 22 +#define R_68K_NUM 23 + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 +#define R_386_TLS_IE 15 +#define R_386_TLS_GOTIE 16 +#define R_386_TLS_LE 17 +#define R_386_TLS_GD 18 +#define R_386_TLS_LDM 19 +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 +#define R_386_TLS_GD_PUSH 25 +#define R_386_TLS_GD_CALL 26 +#define R_386_TLS_GD_POP 27 +#define R_386_TLS_LDM_32 28 +#define R_386_TLS_LDM_PUSH 29 +#define R_386_TLS_LDM_CALL 30 +#define R_386_TLS_LDM_POP 31 +#define R_386_TLS_LDO_32 32 +#define R_386_TLS_IE_32 33 +#define R_386_TLS_LE_32 34 +#define R_386_TLS_DTPMOD32 35 +#define R_386_TLS_DTPOFF32 36 +#define R_386_TLS_TPOFF32 37 +#define R_386_SIZE32 38 +#define R_386_TLS_GOTDESC 39 +#define R_386_TLS_DESC_CALL 40 +#define R_386_TLS_DESC 41 +#define R_386_IRELATIVE 42 +#define R_386_GOT32X 43 +#define R_386_NUM 44 + + + + + +#define STT_SPARC_REGISTER 13 + + + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 +#define EF_SPARC_SUN_US1 0x000200 +#define EF_SPARC_HAL_R1 0x000400 +#define EF_SPARC_SUN_US3 0x000800 + + + +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 + + + +#define R_SPARC_PLT32 24 +#define R_SPARC_HIPLT22 25 +#define R_SPARC_LOPLT10 26 +#define R_SPARC_PCPLT32 27 +#define R_SPARC_PCPLT22 28 +#define R_SPARC_PCPLT10 29 +#define R_SPARC_10 30 +#define R_SPARC_11 31 +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_PC_HH22 37 +#define R_SPARC_PC_HM10 38 +#define R_SPARC_PC_LM22 39 +#define R_SPARC_WDISP16 40 +#define R_SPARC_WDISP19 41 +#define R_SPARC_GLOB_JMP 42 +#define R_SPARC_7 43 +#define R_SPARC_5 44 +#define R_SPARC_6 45 +#define R_SPARC_DISP64 46 +#define R_SPARC_PLT64 47 +#define R_SPARC_HIX22 48 +#define R_SPARC_LOX10 49 +#define R_SPARC_H44 50 +#define R_SPARC_M44 51 +#define R_SPARC_L44 52 +#define R_SPARC_REGISTER 53 +#define R_SPARC_UA64 54 +#define R_SPARC_UA16 55 +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 + +#define R_SPARC_NUM 253 + + + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + + +#define EF_MIPS_NOREORDER 1 +#define EF_MIPS_PIC 2 +#define EF_MIPS_CPIC 4 +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_FP64 512 +#define EF_MIPS_NAN2008 1024 +#define EF_MIPS_ARCH 0xf0000000 + + + +#define EF_MIPS_ARCH_1 0x00000000 +#define EF_MIPS_ARCH_2 0x10000000 +#define EF_MIPS_ARCH_3 0x20000000 +#define EF_MIPS_ARCH_4 0x30000000 +#define EF_MIPS_ARCH_5 0x40000000 +#define EF_MIPS_ARCH_32 0x50000000 +#define EF_MIPS_ARCH_64 0x60000000 +#define EF_MIPS_ARCH_32R2 0x70000000 +#define EF_MIPS_ARCH_64R2 0x80000000 + + +#define E_MIPS_ARCH_1 0x00000000 +#define E_MIPS_ARCH_2 0x10000000 +#define E_MIPS_ARCH_3 0x20000000 +#define E_MIPS_ARCH_4 0x30000000 +#define E_MIPS_ARCH_5 0x40000000 +#define E_MIPS_ARCH_32 0x50000000 +#define E_MIPS_ARCH_64 0x60000000 + + + +#define SHN_MIPS_ACOMMON 0xff00 +#define SHN_MIPS_TEXT 0xff01 +#define SHN_MIPS_DATA 0xff02 +#define SHN_MIPS_SCOMMON 0xff03 +#define SHN_MIPS_SUNDEFINED 0xff04 + + + +#define SHT_MIPS_LIBLIST 0x70000000 +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 +#define SHT_MIPS_GPTAB 0x70000003 +#define SHT_MIPS_UCODE 0x70000004 +#define SHT_MIPS_DEBUG 0x70000005 +#define SHT_MIPS_REGINFO 0x70000006 +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + + + +#define SHF_MIPS_GPREL 0x10000000 +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + + + + +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + + +#define STB_MIPS_SPLIT_COMMON 13 + + + +typedef union { + struct { + Elf32_Word gt_current_g_value; + Elf32_Word gt_unused; + } gt_header; + struct { + Elf32_Word gt_g_value; + Elf32_Word gt_bytes; + } gt_entry; +} Elf32_gptab; + + + +typedef struct { + Elf32_Word ri_gprmask; + Elf32_Word ri_cprmask[4]; + Elf32_Sword ri_gp_value; +} Elf32_RegInfo; + + + +typedef struct { + unsigned char kind; + + unsigned char size; + Elf32_Section section; + + Elf32_Word info; +} Elf_Options; + + + +#define ODK_NULL 0 +#define ODK_REGINFO 1 +#define ODK_EXCEPTIONS 2 +#define ODK_PAD 3 +#define ODK_HWPATCH 4 +#define ODK_FILL 5 +#define ODK_TAGS 6 +#define ODK_HWAND 7 +#define ODK_HWOR 8 + + + +#define OEX_FPU_MIN 0x1f +#define OEX_FPU_MAX 0x1f00 +#define OEX_PAGE0 0x10000 +#define OEX_SMM 0x20000 +#define OEX_FPDBUG 0x40000 +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + + + +#define OHW_R4KEOP 0x1 +#define OHW_R8KPFETCH 0x2 +#define OHW_R5KEOP 0x4 +#define OHW_R5KCVTL 0x8 + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + + + +typedef struct { + Elf32_Word hwp_flags1; + Elf32_Word hwp_flags2; +} Elf_Options_Hw; + + + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + + + +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 +#define R_MIPS_TLS_DTPREL32 39 +#define R_MIPS_TLS_DTPMOD64 40 +#define R_MIPS_TLS_DTPREL64 41 +#define R_MIPS_TLS_GD 42 +#define R_MIPS_TLS_LDM 43 +#define R_MIPS_TLS_DTPREL_HI16 44 +#define R_MIPS_TLS_DTPREL_LO16 45 +#define R_MIPS_TLS_GOTTPREL 46 +#define R_MIPS_TLS_TPREL32 47 +#define R_MIPS_TLS_TPREL64 48 +#define R_MIPS_TLS_TPREL_HI16 49 +#define R_MIPS_TLS_TPREL_LO16 50 +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 + +#define R_MIPS_NUM 128 + + + +#define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_RTPROC 0x70000001 +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 + + + +#define PF_MIPS_LOCAL 0x10000000 + + + +#define DT_MIPS_RLD_VERSION 0x70000001 +#define DT_MIPS_TIME_STAMP 0x70000002 +#define DT_MIPS_ICHECKSUM 0x70000003 +#define DT_MIPS_IVERSION 0x70000004 +#define DT_MIPS_FLAGS 0x70000005 +#define DT_MIPS_BASE_ADDRESS 0x70000006 +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 +#define DT_MIPS_LIBLIST 0x70000009 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a +#define DT_MIPS_CONFLICTNO 0x7000000b +#define DT_MIPS_LIBLISTNO 0x70000010 +#define DT_MIPS_SYMTABNO 0x70000011 +#define DT_MIPS_UNREFEXTNO 0x70000012 +#define DT_MIPS_GOTSYM 0x70000013 +#define DT_MIPS_HIPAGENO 0x70000014 +#define DT_MIPS_RLD_MAP 0x70000016 +#define DT_MIPS_DELTA_CLASS 0x70000017 +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 + +#define DT_MIPS_DELTA_INSTANCE 0x70000019 +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a + +#define DT_MIPS_DELTA_RELOC 0x7000001b +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c + +#define DT_MIPS_DELTA_SYM 0x7000001d + +#define DT_MIPS_DELTA_SYM_NO 0x7000001e + +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 + +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 + +#define DT_MIPS_CXX_FLAGS 0x70000022 +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 +#define DT_MIPS_INTERFACE 0x7000002a +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d + +#define DT_MIPS_PERF_SUFFIX 0x7000002e + +#define DT_MIPS_COMPACT_SIZE 0x7000002f +#define DT_MIPS_GP_VALUE 0x70000030 +#define DT_MIPS_AUX_DYNAMIC 0x70000031 + +#define DT_MIPS_PLTGOT 0x70000032 + +#define DT_MIPS_RWPLT 0x70000034 +#define DT_MIPS_RLD_MAP_REL 0x70000035 +#define DT_MIPS_NUM 0x36 + + + +#define RHF_NONE 0 +#define RHF_QUICKSTART (1 << 0) +#define RHF_NOTPOT (1 << 1) +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + + + +typedef struct { + Elf32_Word l_name; + Elf32_Word l_time_stamp; + Elf32_Word l_checksum; + Elf32_Word l_version; + Elf32_Word l_flags; +} Elf32_Lib; + +typedef struct { + Elf64_Word l_name; + Elf64_Word l_time_stamp; + Elf64_Word l_checksum; + Elf64_Word l_version; + Elf64_Word l_flags; +} Elf64_Lib; + + + + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) +#define LL_IGNORE_INT_VER (1 << 1) +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + + + +typedef Elf32_Addr Elf32_Conflict; + +typedef struct { + Elf32_Half version; + unsigned char isa_level; + unsigned char isa_rev; + unsigned char gpr_size; + unsigned char cpr1_size; + unsigned char cpr2_size; + unsigned char fp_abi; + Elf32_Word isa_ext; + Elf32_Word ases; + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +#define MIPS_AFL_REG_NONE 0x00 +#define MIPS_AFL_REG_32 0x01 +#define MIPS_AFL_REG_64 0x02 +#define MIPS_AFL_REG_128 0x03 + +#define MIPS_AFL_ASE_DSP 0x00000001 +#define MIPS_AFL_ASE_DSPR2 0x00000002 +#define MIPS_AFL_ASE_EVA 0x00000004 +#define MIPS_AFL_ASE_MCU 0x00000008 +#define MIPS_AFL_ASE_MDMX 0x00000010 +#define MIPS_AFL_ASE_MIPS3D 0x00000020 +#define MIPS_AFL_ASE_MT 0x00000040 +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 +#define MIPS_AFL_ASE_VIRT 0x00000100 +#define MIPS_AFL_ASE_MSA 0x00000200 +#define MIPS_AFL_ASE_MIPS16 0x00000400 +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 +#define MIPS_AFL_ASE_XPA 0x00001000 +#define MIPS_AFL_ASE_MASK 0x00001fff + +#define MIPS_AFL_EXT_XLR 1 +#define MIPS_AFL_EXT_OCTEON2 2 +#define MIPS_AFL_EXT_OCTEONP 3 +#define MIPS_AFL_EXT_LOONGSON_3A 4 +#define MIPS_AFL_EXT_OCTEON 5 +#define MIPS_AFL_EXT_5900 6 +#define MIPS_AFL_EXT_4650 7 +#define MIPS_AFL_EXT_4010 8 +#define MIPS_AFL_EXT_4100 9 +#define MIPS_AFL_EXT_3900 10 +#define MIPS_AFL_EXT_10000 11 +#define MIPS_AFL_EXT_SB1 12 +#define MIPS_AFL_EXT_4111 13 +#define MIPS_AFL_EXT_4120 14 +#define MIPS_AFL_EXT_5400 15 +#define MIPS_AFL_EXT_5500 16 +#define MIPS_AFL_EXT_LOONGSON_2E 17 +#define MIPS_AFL_EXT_LOONGSON_2F 18 + +#define MIPS_AFL_FLAGS1_ODDSPREG 1 + +enum +{ + Val_GNU_MIPS_ABI_FP_ANY = 0, + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + Val_GNU_MIPS_ABI_FP_SOFT = 3, + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + Val_GNU_MIPS_ABI_FP_XX = 5, + Val_GNU_MIPS_ABI_FP_64 = 6, + Val_GNU_MIPS_ABI_FP_64A = 7, + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + + + + +#define EF_PARISC_TRAPNIL 0x00010000 +#define EF_PARISC_EXT 0x00020000 +#define EF_PARISC_LSB 0x00040000 +#define EF_PARISC_WIDE 0x00080000 +#define EF_PARISC_NO_KABP 0x00100000 + +#define EF_PARISC_LAZYSWAP 0x00400000 +#define EF_PARISC_ARCH 0x0000ffff + + + +#define EFA_PARISC_1_0 0x020b +#define EFA_PARISC_1_1 0x0210 +#define EFA_PARISC_2_0 0x0214 + + + +#define SHN_PARISC_ANSI_COMMON 0xff00 + +#define SHN_PARISC_HUGE_COMMON 0xff01 + + + +#define SHT_PARISC_EXT 0x70000000 +#define SHT_PARISC_UNWIND 0x70000001 +#define SHT_PARISC_DOC 0x70000002 + + + +#define SHF_PARISC_SHORT 0x20000000 +#define SHF_PARISC_HUGE 0x40000000 +#define SHF_PARISC_SBP 0x80000000 + + + +#define STT_PARISC_MILLICODE 13 + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + + + +#define R_PARISC_NONE 0 +#define R_PARISC_DIR32 1 +#define R_PARISC_DIR21L 2 +#define R_PARISC_DIR17R 3 +#define R_PARISC_DIR17F 4 +#define R_PARISC_DIR14R 6 +#define R_PARISC_PCREL32 9 +#define R_PARISC_PCREL21L 10 +#define R_PARISC_PCREL17R 11 +#define R_PARISC_PCREL17F 12 +#define R_PARISC_PCREL14R 14 +#define R_PARISC_DPREL21L 18 +#define R_PARISC_DPREL14R 22 +#define R_PARISC_GPREL21L 26 +#define R_PARISC_GPREL14R 30 +#define R_PARISC_LTOFF21L 34 +#define R_PARISC_LTOFF14R 38 +#define R_PARISC_SECREL32 41 +#define R_PARISC_SEGBASE 48 +#define R_PARISC_SEGREL32 49 +#define R_PARISC_PLTOFF21L 50 +#define R_PARISC_PLTOFF14R 54 +#define R_PARISC_LTOFF_FPTR32 57 +#define R_PARISC_LTOFF_FPTR21L 58 +#define R_PARISC_LTOFF_FPTR14R 62 +#define R_PARISC_FPTR64 64 +#define R_PARISC_PLABEL32 65 +#define R_PARISC_PLABEL21L 66 +#define R_PARISC_PLABEL14R 70 +#define R_PARISC_PCREL64 72 +#define R_PARISC_PCREL22F 74 +#define R_PARISC_PCREL14WR 75 +#define R_PARISC_PCREL14DR 76 +#define R_PARISC_PCREL16F 77 +#define R_PARISC_PCREL16WF 78 +#define R_PARISC_PCREL16DF 79 +#define R_PARISC_DIR64 80 +#define R_PARISC_DIR14WR 83 +#define R_PARISC_DIR14DR 84 +#define R_PARISC_DIR16F 85 +#define R_PARISC_DIR16WF 86 +#define R_PARISC_DIR16DF 87 +#define R_PARISC_GPREL64 88 +#define R_PARISC_GPREL14WR 91 +#define R_PARISC_GPREL14DR 92 +#define R_PARISC_GPREL16F 93 +#define R_PARISC_GPREL16WF 94 +#define R_PARISC_GPREL16DF 95 +#define R_PARISC_LTOFF64 96 +#define R_PARISC_LTOFF14WR 99 +#define R_PARISC_LTOFF14DR 100 +#define R_PARISC_LTOFF16F 101 +#define R_PARISC_LTOFF16WF 102 +#define R_PARISC_LTOFF16DF 103 +#define R_PARISC_SECREL64 104 +#define R_PARISC_SEGREL64 112 +#define R_PARISC_PLTOFF14WR 115 +#define R_PARISC_PLTOFF14DR 116 +#define R_PARISC_PLTOFF16F 117 +#define R_PARISC_PLTOFF16WF 118 +#define R_PARISC_PLTOFF16DF 119 +#define R_PARISC_LTOFF_FPTR64 120 +#define R_PARISC_LTOFF_FPTR14WR 123 +#define R_PARISC_LTOFF_FPTR14DR 124 +#define R_PARISC_LTOFF_FPTR16F 125 +#define R_PARISC_LTOFF_FPTR16WF 126 +#define R_PARISC_LTOFF_FPTR16DF 127 +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 +#define R_PARISC_IPLT 129 +#define R_PARISC_EPLT 130 +#define R_PARISC_TPREL32 153 +#define R_PARISC_TPREL21L 154 +#define R_PARISC_TPREL14R 158 +#define R_PARISC_LTOFF_TP21L 162 +#define R_PARISC_LTOFF_TP14R 166 +#define R_PARISC_LTOFF_TP14F 167 +#define R_PARISC_TPREL64 216 +#define R_PARISC_TPREL14WR 219 +#define R_PARISC_TPREL14DR 220 +#define R_PARISC_TPREL16F 221 +#define R_PARISC_TPREL16WF 222 +#define R_PARISC_TPREL16DF 223 +#define R_PARISC_LTOFF_TP64 224 +#define R_PARISC_LTOFF_TP14WR 227 +#define R_PARISC_LTOFF_TP14DR 228 +#define R_PARISC_LTOFF_TP16F 229 +#define R_PARISC_LTOFF_TP16WF 230 +#define R_PARISC_LTOFF_TP16DF 231 +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 +#define R_PARISC_TLS_GD14R 235 +#define R_PARISC_TLS_GDCALL 236 +#define R_PARISC_TLS_LDM21L 237 +#define R_PARISC_TLS_LDM14R 238 +#define R_PARISC_TLS_LDMCALL 239 +#define R_PARISC_TLS_LDO21L 240 +#define R_PARISC_TLS_LDO14R 241 +#define R_PARISC_TLS_DTPMOD32 242 +#define R_PARISC_TLS_DTPMOD64 243 +#define R_PARISC_TLS_DTPOFF32 244 +#define R_PARISC_TLS_DTPOFF64 245 +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + + + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + + + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + + + + + +#define EF_ALPHA_32BIT 1 +#define EF_ALPHA_CANRELAX 2 + + + + +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + + + +#define SHF_ALPHA_GPREL 0x10000000 + + +#define STO_ALPHA_NOPV 0x80 +#define STO_ALPHA_STD_GPLOAD 0x88 + + + +#define R_ALPHA_NONE 0 +#define R_ALPHA_REFLONG 1 +#define R_ALPHA_REFQUAD 2 +#define R_ALPHA_GPREL32 3 +#define R_ALPHA_LITERAL 4 +#define R_ALPHA_LITUSE 5 +#define R_ALPHA_GPDISP 6 +#define R_ALPHA_BRADDR 7 +#define R_ALPHA_HINT 8 +#define R_ALPHA_SREL16 9 +#define R_ALPHA_SREL32 10 +#define R_ALPHA_SREL64 11 +#define R_ALPHA_GPRELHIGH 17 +#define R_ALPHA_GPRELLOW 18 +#define R_ALPHA_GPREL16 19 +#define R_ALPHA_COPY 24 +#define R_ALPHA_GLOB_DAT 25 +#define R_ALPHA_JMP_SLOT 26 +#define R_ALPHA_RELATIVE 27 +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 + +#define R_ALPHA_NUM 46 + + +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + + +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + + + + +#define EF_PPC_EMB 0x80000000 + + +#define EF_PPC_RELOCATABLE 0x00010000 +#define EF_PPC_RELOCATABLE_LIB 0x00008000 + + + +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 +#define R_PPC_ADDR24 2 +#define R_PPC_ADDR16 3 +#define R_PPC_ADDR16_LO 4 +#define R_PPC_ADDR16_HI 5 +#define R_PPC_ADDR16_HA 6 +#define R_PPC_ADDR14 7 +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 +#define R_PPC_REL14 11 +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + + +#define R_PPC_TLS 67 +#define R_PPC_DTPMOD32 68 +#define R_PPC_TPREL16 69 +#define R_PPC_TPREL16_LO 70 +#define R_PPC_TPREL16_HI 71 +#define R_PPC_TPREL16_HA 72 +#define R_PPC_TPREL32 73 +#define R_PPC_DTPREL16 74 +#define R_PPC_DTPREL16_LO 75 +#define R_PPC_DTPREL16_HI 76 +#define R_PPC_DTPREL16_HA 77 +#define R_PPC_DTPREL32 78 +#define R_PPC_GOT_TLSGD16 79 +#define R_PPC_GOT_TLSGD16_LO 80 +#define R_PPC_GOT_TLSGD16_HI 81 +#define R_PPC_GOT_TLSGD16_HA 82 +#define R_PPC_GOT_TLSLD16 83 +#define R_PPC_GOT_TLSLD16_LO 84 +#define R_PPC_GOT_TLSLD16_HI 85 +#define R_PPC_GOT_TLSLD16_HA 86 +#define R_PPC_GOT_TPREL16 87 +#define R_PPC_GOT_TPREL16_LO 88 +#define R_PPC_GOT_TPREL16_HI 89 +#define R_PPC_GOT_TPREL16_HA 90 +#define R_PPC_GOT_DTPREL16 91 +#define R_PPC_GOT_DTPREL16_LO 92 +#define R_PPC_GOT_DTPREL16_HI 93 +#define R_PPC_GOT_DTPREL16_HA 94 +#define R_PPC_TLSGD 95 +#define R_PPC_TLSLD 96 + + +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 + + +#define R_PPC_DIAB_SDA21_LO 180 +#define R_PPC_DIAB_SDA21_HI 181 +#define R_PPC_DIAB_SDA21_HA 182 +#define R_PPC_DIAB_RELSDA_LO 183 +#define R_PPC_DIAB_RELSDA_HI 184 +#define R_PPC_DIAB_RELSDA_HA 185 + + +#define R_PPC_IRELATIVE 248 + + +#define R_PPC_REL16 249 +#define R_PPC_REL16_LO 250 +#define R_PPC_REL16_HI 251 +#define R_PPC_REL16_HA 252 + + + +#define R_PPC_TOC16 255 + + +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +#define PPC_OPT_TLS 1 + + +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 +#define R_PPC64_ADDR24 R_PPC_ADDR24 +#define R_PPC64_ADDR16 R_PPC_ADDR16 +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA +#define R_PPC64_ADDR14 R_PPC_ADDR14 +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 +#define R_PPC64_REL14 R_PPC_REL14 +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 +#define R_PPC64_ADDR64 38 +#define R_PPC64_ADDR16_HIGHER 39 +#define R_PPC64_ADDR16_HIGHERA 40 +#define R_PPC64_ADDR16_HIGHEST 41 +#define R_PPC64_ADDR16_HIGHESTA 42 +#define R_PPC64_UADDR64 43 +#define R_PPC64_REL64 44 +#define R_PPC64_PLT64 45 +#define R_PPC64_PLTREL64 46 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC 51 +#define R_PPC64_PLTGOT16 52 +#define R_PPC64_PLTGOT16_LO 53 +#define R_PPC64_PLTGOT16_HI 54 +#define R_PPC64_PLTGOT16_HA 55 + +#define R_PPC64_ADDR16_DS 56 +#define R_PPC64_ADDR16_LO_DS 57 +#define R_PPC64_GOT16_DS 58 +#define R_PPC64_GOT16_LO_DS 59 +#define R_PPC64_PLT16_LO_DS 60 +#define R_PPC64_SECTOFF_DS 61 +#define R_PPC64_SECTOFF_LO_DS 62 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_PLTGOT16_DS 65 +#define R_PPC64_PLTGOT16_LO_DS 66 + + +#define R_PPC64_TLS 67 +#define R_PPC64_DTPMOD64 68 +#define R_PPC64_TPREL16 69 +#define R_PPC64_TPREL16_LO 70 +#define R_PPC64_TPREL16_HI 71 +#define R_PPC64_TPREL16_HA 72 +#define R_PPC64_TPREL64 73 +#define R_PPC64_DTPREL16 74 +#define R_PPC64_DTPREL16_LO 75 +#define R_PPC64_DTPREL16_HI 76 +#define R_PPC64_DTPREL16_HA 77 +#define R_PPC64_DTPREL64 78 +#define R_PPC64_GOT_TLSGD16 79 +#define R_PPC64_GOT_TLSGD16_LO 80 +#define R_PPC64_GOT_TLSGD16_HI 81 +#define R_PPC64_GOT_TLSGD16_HA 82 +#define R_PPC64_GOT_TLSLD16 83 +#define R_PPC64_GOT_TLSLD16_LO 84 +#define R_PPC64_GOT_TLSLD16_HI 85 +#define R_PPC64_GOT_TLSLD16_HA 86 +#define R_PPC64_GOT_TPREL16_DS 87 +#define R_PPC64_GOT_TPREL16_LO_DS 88 +#define R_PPC64_GOT_TPREL16_HI 89 +#define R_PPC64_GOT_TPREL16_HA 90 +#define R_PPC64_GOT_DTPREL16_DS 91 +#define R_PPC64_GOT_DTPREL16_LO_DS 92 +#define R_PPC64_GOT_DTPREL16_HI 93 +#define R_PPC64_GOT_DTPREL16_HA 94 +#define R_PPC64_TPREL16_DS 95 +#define R_PPC64_TPREL16_LO_DS 96 +#define R_PPC64_TPREL16_HIGHER 97 +#define R_PPC64_TPREL16_HIGHERA 98 +#define R_PPC64_TPREL16_HIGHEST 99 +#define R_PPC64_TPREL16_HIGHESTA 100 +#define R_PPC64_DTPREL16_DS 101 +#define R_PPC64_DTPREL16_LO_DS 102 +#define R_PPC64_DTPREL16_HIGHER 103 +#define R_PPC64_DTPREL16_HIGHERA 104 +#define R_PPC64_DTPREL16_HIGHEST 105 +#define R_PPC64_DTPREL16_HIGHESTA 106 +#define R_PPC64_TLSGD 107 +#define R_PPC64_TLSLD 108 +#define R_PPC64_TOCSAVE 109 +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + + +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 +#define R_PPC64_REL16_LO 250 +#define R_PPC64_REL16_HI 251 +#define R_PPC64_REL16_HA 252 + +#define EF_PPC64_ABI 3 + +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 + +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK 0xe0 +#define PPC64_LOCAL_ENTRY_OFFSET(x) (1 << (((x)&0xe0)>>5) & 0xfc) + + +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 +#define EF_ARM_ABI_FLOAT_HARD 0x400 + + +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + + +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + + +#define STT_ARM_TFUNC STT_LOPROC +#define STT_ARM_16BIT STT_HIPROC + + +#define SHF_ARM_ENTRYSECT 0x10000000 +#define SHF_ARM_COMDEF 0x80000000 + + + +#define PF_ARM_SB 0x10000000 + +#define PF_ARM_PI 0x20000000 +#define PF_ARM_ABS 0x40000000 + + +#define PT_ARM_EXIDX (PT_LOPROC + 1) + + +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) + +#define R_AARCH64_NONE 0 +#define R_AARCH64_P32_ABS32 1 +#define R_AARCH64_P32_COPY 180 +#define R_AARCH64_P32_GLOB_DAT 181 +#define R_AARCH64_P32_JUMP_SLOT 182 +#define R_AARCH64_P32_RELATIVE 183 +#define R_AARCH64_P32_TLS_DTPMOD 184 +#define R_AARCH64_P32_TLS_DTPREL 185 +#define R_AARCH64_P32_TLS_TPREL 186 +#define R_AARCH64_P32_TLSDESC 187 +#define R_AARCH64_P32_IRELATIVE 188 +#define R_AARCH64_ABS64 257 +#define R_AARCH64_ABS32 258 +#define R_AARCH64_ABS16 259 +#define R_AARCH64_PREL64 260 +#define R_AARCH64_PREL32 261 +#define R_AARCH64_PREL16 262 +#define R_AARCH64_MOVW_UABS_G0 263 +#define R_AARCH64_MOVW_UABS_G0_NC 264 +#define R_AARCH64_MOVW_UABS_G1 265 +#define R_AARCH64_MOVW_UABS_G1_NC 266 +#define R_AARCH64_MOVW_UABS_G2 267 +#define R_AARCH64_MOVW_UABS_G2_NC 268 +#define R_AARCH64_MOVW_UABS_G3 269 +#define R_AARCH64_MOVW_SABS_G0 270 +#define R_AARCH64_MOVW_SABS_G1 271 +#define R_AARCH64_MOVW_SABS_G2 272 +#define R_AARCH64_LD_PREL_LO19 273 +#define R_AARCH64_ADR_PREL_LO21 274 +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 +#define R_AARCH64_ADD_ABS_LO12_NC 277 +#define R_AARCH64_LDST8_ABS_LO12_NC 278 +#define R_AARCH64_TSTBR14 279 +#define R_AARCH64_CONDBR19 280 +#define R_AARCH64_JUMP26 282 +#define R_AARCH64_CALL26 283 +#define R_AARCH64_LDST16_ABS_LO12_NC 284 +#define R_AARCH64_LDST32_ABS_LO12_NC 285 +#define R_AARCH64_LDST64_ABS_LO12_NC 286 +#define R_AARCH64_MOVW_PREL_G0 287 +#define R_AARCH64_MOVW_PREL_G0_NC 288 +#define R_AARCH64_MOVW_PREL_G1 289 +#define R_AARCH64_MOVW_PREL_G1_NC 290 +#define R_AARCH64_MOVW_PREL_G2 291 +#define R_AARCH64_MOVW_PREL_G2_NC 292 +#define R_AARCH64_MOVW_PREL_G3 293 +#define R_AARCH64_LDST128_ABS_LO12_NC 299 +#define R_AARCH64_MOVW_GOTOFF_G0 300 +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 +#define R_AARCH64_MOVW_GOTOFF_G1 302 +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 +#define R_AARCH64_MOVW_GOTOFF_G2 304 +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 +#define R_AARCH64_MOVW_GOTOFF_G3 306 +#define R_AARCH64_GOTREL64 307 +#define R_AARCH64_GOTREL32 308 +#define R_AARCH64_GOT_LD_PREL19 309 +#define R_AARCH64_LD64_GOTOFF_LO15 310 +#define R_AARCH64_ADR_GOT_PAGE 311 +#define R_AARCH64_LD64_GOT_LO12_NC 312 +#define R_AARCH64_LD64_GOTPAGE_LO15 313 +#define R_AARCH64_TLSGD_ADR_PREL21 512 +#define R_AARCH64_TLSGD_ADR_PAGE21 513 +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 +#define R_AARCH64_TLSGD_MOVW_G1 515 +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 +#define R_AARCH64_TLSLD_ADR_PREL21 517 +#define R_AARCH64_TLSLD_ADR_PAGE21 518 +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 +#define R_AARCH64_TLSLD_MOVW_G1 520 +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 +#define R_AARCH64_TLSLD_LD_PREL19 522 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 +#define R_AARCH64_TLSDESC_LD_PREL19 560 +#define R_AARCH64_TLSDESC_ADR_PREL21 561 +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 +#define R_AARCH64_TLSDESC_LD64_LO12 563 +#define R_AARCH64_TLSDESC_ADD_LO12 564 +#define R_AARCH64_TLSDESC_OFF_G1 565 +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 +#define R_AARCH64_TLSDESC_LDR 567 +#define R_AARCH64_TLSDESC_ADD 568 +#define R_AARCH64_TLSDESC_CALL 569 +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 +#define R_AARCH64_COPY 1024 +#define R_AARCH64_GLOB_DAT 1025 +#define R_AARCH64_JUMP_SLOT 1026 +#define R_AARCH64_RELATIVE 1027 +#define R_AARCH64_TLS_DTPMOD 1028 +#define R_AARCH64_TLS_DTPMOD64 1028 +#define R_AARCH64_TLS_DTPREL 1029 +#define R_AARCH64_TLS_DTPREL64 1029 +#define R_AARCH64_TLS_TPREL 1030 +#define R_AARCH64_TLS_TPREL64 1030 +#define R_AARCH64_TLSDESC 1031 + + +#define R_ARM_NONE 0 +#define R_ARM_PC24 1 +#define R_ARM_ABS32 2 +#define R_ARM_REL32 3 +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 +#define R_ARM_ABS12 6 +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_TLS_DESC 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 +#define R_ARM_TLS_DTPOFF32 18 +#define R_ARM_TLS_TPOFF32 19 +#define R_ARM_COPY 20 +#define R_ARM_GLOB_DAT 21 +#define R_ARM_JUMP_SLOT 22 +#define R_ARM_RELATIVE 23 +#define R_ARM_GOTOFF 24 +#define R_ARM_GOTPC 25 +#define R_ARM_GOT32 26 +#define R_ARM_PLT32 27 +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_BASE_ABS 31 +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 +#define R_ARM_MOVW_PREL_NC 45 +#define R_ARM_MOVT_PREL 46 +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 +#define R_ARM_THM_MOVW_PREL_NC 49 +#define R_ARM_THM_MOVT_PREL 50 +#define R_ARM_THM_JUMP19 51 +#define R_ARM_THM_JUMP6 52 +#define R_ARM_THM_ALU_PREL_11_0 53 +#define R_ARM_THM_PC12 54 +#define R_ARM_ABS32_NOI 55 +#define R_ARM_REL32_NOI 56 +#define R_ARM_ALU_PC_G0_NC 57 +#define R_ARM_ALU_PC_G0 58 +#define R_ARM_ALU_PC_G1_NC 59 +#define R_ARM_ALU_PC_G1 60 +#define R_ARM_ALU_PC_G2 61 +#define R_ARM_LDR_PC_G1 62 +#define R_ARM_LDR_PC_G2 63 +#define R_ARM_LDRS_PC_G0 64 +#define R_ARM_LDRS_PC_G1 65 +#define R_ARM_LDRS_PC_G2 66 +#define R_ARM_LDC_PC_G0 67 +#define R_ARM_LDC_PC_G1 68 +#define R_ARM_LDC_PC_G2 69 +#define R_ARM_ALU_SB_G0_NC 70 +#define R_ARM_ALU_SB_G0 71 +#define R_ARM_ALU_SB_G1_NC 72 +#define R_ARM_ALU_SB_G1 73 +#define R_ARM_ALU_SB_G2 74 +#define R_ARM_LDR_SB_G0 75 +#define R_ARM_LDR_SB_G1 76 +#define R_ARM_LDR_SB_G2 77 +#define R_ARM_LDRS_SB_G0 78 +#define R_ARM_LDRS_SB_G1 79 +#define R_ARM_LDRS_SB_G2 80 +#define R_ARM_LDC_SB_G0 81 +#define R_ARM_LDC_SB_G1 82 +#define R_ARM_LDC_SB_G2 83 +#define R_ARM_MOVW_BREL_NC 84 +#define R_ARM_MOVT_BREL 85 +#define R_ARM_MOVW_BREL 86 +#define R_ARM_THM_MOVW_BREL_NC 87 +#define R_ARM_THM_MOVT_BREL 88 +#define R_ARM_THM_MOVW_BREL 89 +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 +#define R_ARM_GOT_PREL 96 +#define R_ARM_GOT_BREL12 97 +#define R_ARM_GOTOFF12 98 +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 +#define R_ARM_THM_PC9 103 +#define R_ARM_TLS_GD32 104 + +#define R_ARM_TLS_LDM32 105 + +#define R_ARM_TLS_LDO32 106 + +#define R_ARM_TLS_IE32 107 + +#define R_ARM_TLS_LE32 108 +#define R_ARM_TLS_LDO12 109 +#define R_ARM_TLS_LE12 110 +#define R_ARM_TLS_IE12GP 111 +#define R_ARM_ME_TOO 128 +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 + +#define R_ARM_NUM 256 + + + + +#define EF_IA_64_MASKOS 0x0000000f +#define EF_IA_64_ABI64 0x00000010 +#define EF_IA_64_ARCH 0xff000000 + + +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) +#define PT_IA_64_UNWIND (PT_LOPROC + 1) +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + + +#define PF_IA_64_NORECOV 0x80000000 + + +#define SHT_IA_64_EXT (SHT_LOPROC + 0) +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) + + +#define SHF_IA_64_SHORT 0x10000000 +#define SHF_IA_64_NORECOV 0x20000000 + + +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + + +#define R_IA64_NONE 0x00 +#define R_IA64_IMM14 0x21 +#define R_IA64_IMM22 0x22 +#define R_IA64_IMM64 0x23 +#define R_IA64_DIR32MSB 0x24 +#define R_IA64_DIR32LSB 0x25 +#define R_IA64_DIR64MSB 0x26 +#define R_IA64_DIR64LSB 0x27 +#define R_IA64_GPREL22 0x2a +#define R_IA64_GPREL64I 0x2b +#define R_IA64_GPREL32MSB 0x2c +#define R_IA64_GPREL32LSB 0x2d +#define R_IA64_GPREL64MSB 0x2e +#define R_IA64_GPREL64LSB 0x2f +#define R_IA64_LTOFF22 0x32 +#define R_IA64_LTOFF64I 0x33 +#define R_IA64_PLTOFF22 0x3a +#define R_IA64_PLTOFF64I 0x3b +#define R_IA64_PLTOFF64MSB 0x3e +#define R_IA64_PLTOFF64LSB 0x3f +#define R_IA64_FPTR64I 0x43 +#define R_IA64_FPTR32MSB 0x44 +#define R_IA64_FPTR32LSB 0x45 +#define R_IA64_FPTR64MSB 0x46 +#define R_IA64_FPTR64LSB 0x47 +#define R_IA64_PCREL60B 0x48 +#define R_IA64_PCREL21B 0x49 +#define R_IA64_PCREL21M 0x4a +#define R_IA64_PCREL21F 0x4b +#define R_IA64_PCREL32MSB 0x4c +#define R_IA64_PCREL32LSB 0x4d +#define R_IA64_PCREL64MSB 0x4e +#define R_IA64_PCREL64LSB 0x4f +#define R_IA64_LTOFF_FPTR22 0x52 +#define R_IA64_LTOFF_FPTR64I 0x53 +#define R_IA64_LTOFF_FPTR32MSB 0x54 +#define R_IA64_LTOFF_FPTR32LSB 0x55 +#define R_IA64_LTOFF_FPTR64MSB 0x56 +#define R_IA64_LTOFF_FPTR64LSB 0x57 +#define R_IA64_SEGREL32MSB 0x5c +#define R_IA64_SEGREL32LSB 0x5d +#define R_IA64_SEGREL64MSB 0x5e +#define R_IA64_SEGREL64LSB 0x5f +#define R_IA64_SECREL32MSB 0x64 +#define R_IA64_SECREL32LSB 0x65 +#define R_IA64_SECREL64MSB 0x66 +#define R_IA64_SECREL64LSB 0x67 +#define R_IA64_REL32MSB 0x6c +#define R_IA64_REL32LSB 0x6d +#define R_IA64_REL64MSB 0x6e +#define R_IA64_REL64LSB 0x6f +#define R_IA64_LTV32MSB 0x74 +#define R_IA64_LTV32LSB 0x75 +#define R_IA64_LTV64MSB 0x76 +#define R_IA64_LTV64LSB 0x77 +#define R_IA64_PCREL21BI 0x79 +#define R_IA64_PCREL22 0x7a +#define R_IA64_PCREL64I 0x7b +#define R_IA64_IPLTMSB 0x80 +#define R_IA64_IPLTLSB 0x81 +#define R_IA64_COPY 0x84 +#define R_IA64_SUB 0x85 +#define R_IA64_LTOFF22X 0x86 +#define R_IA64_LDXMOV 0x87 +#define R_IA64_TPREL14 0x91 +#define R_IA64_TPREL22 0x92 +#define R_IA64_TPREL64I 0x93 +#define R_IA64_TPREL64MSB 0x96 +#define R_IA64_TPREL64LSB 0x97 +#define R_IA64_LTOFF_TPREL22 0x9a +#define R_IA64_DTPMOD64MSB 0xa6 +#define R_IA64_DTPMOD64LSB 0xa7 +#define R_IA64_LTOFF_DTPMOD22 0xaa +#define R_IA64_DTPREL14 0xb1 +#define R_IA64_DTPREL22 0xb2 +#define R_IA64_DTPREL64I 0xb3 +#define R_IA64_DTPREL32MSB 0xb4 +#define R_IA64_DTPREL32LSB 0xb5 +#define R_IA64_DTPREL64MSB 0xb6 +#define R_IA64_DTPREL64LSB 0xb7 +#define R_IA64_LTOFF_DTPREL22 0xba + + +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +#define R_SH_GOT20 201 +#define R_SH_GOTOFF20 202 +#define R_SH_GOTFUNCDESC 203 +#define R_SH_GOTFUNCDEST20 204 +#define R_SH_GOTOFFFUNCDESC 205 +#define R_SH_GOTOFFFUNCDEST20 206 +#define R_SH_FUNCDESC 207 +#define R_SH_FUNCDESC_VALUE 208 + +#define R_SH_NUM 256 + + + +#define R_390_NONE 0 +#define R_390_8 1 +#define R_390_12 2 +#define R_390_16 3 +#define R_390_32 4 +#define R_390_PC32 5 +#define R_390_GOT12 6 +#define R_390_GOT32 7 +#define R_390_PLT32 8 +#define R_390_COPY 9 +#define R_390_GLOB_DAT 10 +#define R_390_JMP_SLOT 11 +#define R_390_RELATIVE 12 +#define R_390_GOTOFF32 13 +#define R_390_GOTPC 14 +#define R_390_GOT16 15 +#define R_390_PC16 16 +#define R_390_PC16DBL 17 +#define R_390_PLT16DBL 18 +#define R_390_PC32DBL 19 +#define R_390_PLT32DBL 20 +#define R_390_GOTPCDBL 21 +#define R_390_64 22 +#define R_390_PC64 23 +#define R_390_GOT64 24 +#define R_390_PLT64 25 +#define R_390_GOTENT 26 +#define R_390_GOTOFF16 27 +#define R_390_GOTOFF64 28 +#define R_390_GOTPLT12 29 +#define R_390_GOTPLT16 30 +#define R_390_GOTPLT32 31 +#define R_390_GOTPLT64 32 +#define R_390_GOTPLTENT 33 +#define R_390_PLTOFF16 34 +#define R_390_PLTOFF32 35 +#define R_390_PLTOFF64 36 +#define R_390_TLS_LOAD 37 +#define R_390_TLS_GDCALL 38 + +#define R_390_TLS_LDCALL 39 + +#define R_390_TLS_GD32 40 + +#define R_390_TLS_GD64 41 + +#define R_390_TLS_GOTIE12 42 + +#define R_390_TLS_GOTIE32 43 + +#define R_390_TLS_GOTIE64 44 + +#define R_390_TLS_LDM32 45 + +#define R_390_TLS_LDM64 46 + +#define R_390_TLS_IE32 47 + +#define R_390_TLS_IE64 48 + +#define R_390_TLS_IEENT 49 + +#define R_390_TLS_LE32 50 + +#define R_390_TLS_LE64 51 + +#define R_390_TLS_LDO32 52 + +#define R_390_TLS_LDO64 53 + +#define R_390_TLS_DTPMOD 54 +#define R_390_TLS_DTPOFF 55 +#define R_390_TLS_TPOFF 56 + +#define R_390_20 57 +#define R_390_GOT20 58 +#define R_390_GOTPLT20 59 +#define R_390_TLS_GOTIE20 60 + + +#define R_390_NUM 61 + + + +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + + +#define R_X86_64_NONE 0 +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_GOT32 3 +#define R_X86_64_PLT32 4 +#define R_X86_64_COPY 5 +#define R_X86_64_GLOB_DAT 6 +#define R_X86_64_JUMP_SLOT 7 +#define R_X86_64_RELATIVE 8 +#define R_X86_64_GOTPCREL 9 + +#define R_X86_64_32 10 +#define R_X86_64_32S 11 +#define R_X86_64_16 12 +#define R_X86_64_PC16 13 +#define R_X86_64_8 14 +#define R_X86_64_PC8 15 +#define R_X86_64_DTPMOD64 16 +#define R_X86_64_DTPOFF64 17 +#define R_X86_64_TPOFF64 18 +#define R_X86_64_TLSGD 19 + +#define R_X86_64_TLSLD 20 + +#define R_X86_64_DTPOFF32 21 +#define R_X86_64_GOTTPOFF 22 + +#define R_X86_64_TPOFF32 23 +#define R_X86_64_PC64 24 +#define R_X86_64_GOTOFF64 25 +#define R_X86_64_GOTPC32 26 +#define R_X86_64_GOT64 27 +#define R_X86_64_GOTPCREL64 28 +#define R_X86_64_GOTPC64 29 +#define R_X86_64_GOTPLT64 30 +#define R_X86_64_PLTOFF64 31 +#define R_X86_64_SIZE32 32 +#define R_X86_64_SIZE64 33 + +#define R_X86_64_GOTPC32_TLSDESC 34 +#define R_X86_64_TLSDESC_CALL 35 + +#define R_X86_64_TLSDESC 36 +#define R_X86_64_IRELATIVE 37 +#define R_X86_64_RELATIVE64 38 +#define R_X86_64_GOTPCRELX 41 +#define R_X86_64_REX_GOTPCRELX 42 +#define R_X86_64_NUM 43 + + + +#define R_MN10300_NONE 0 +#define R_MN10300_32 1 +#define R_MN10300_16 2 +#define R_MN10300_8 3 +#define R_MN10300_PCREL32 4 +#define R_MN10300_PCREL16 5 +#define R_MN10300_PCREL8 6 +#define R_MN10300_GNU_VTINHERIT 7 +#define R_MN10300_GNU_VTENTRY 8 +#define R_MN10300_24 9 +#define R_MN10300_GOTPC32 10 +#define R_MN10300_GOTPC16 11 +#define R_MN10300_GOTOFF32 12 +#define R_MN10300_GOTOFF24 13 +#define R_MN10300_GOTOFF16 14 +#define R_MN10300_PLT32 15 +#define R_MN10300_PLT16 16 +#define R_MN10300_GOT32 17 +#define R_MN10300_GOT24 18 +#define R_MN10300_GOT16 19 +#define R_MN10300_COPY 20 +#define R_MN10300_GLOB_DAT 21 +#define R_MN10300_JMP_SLOT 22 +#define R_MN10300_RELATIVE 23 + +#define R_MN10300_NUM 24 + + + +#define R_M32R_NONE 0 +#define R_M32R_16 1 +#define R_M32R_32 2 +#define R_M32R_24 3 +#define R_M32R_10_PCREL 4 +#define R_M32R_18_PCREL 5 +#define R_M32R_26_PCREL 6 +#define R_M32R_HI16_ULO 7 +#define R_M32R_HI16_SLO 8 +#define R_M32R_LO16 9 +#define R_M32R_SDA16 10 +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 + +#define R_M32R_16_RELA 33 +#define R_M32R_32_RELA 34 +#define R_M32R_24_RELA 35 +#define R_M32R_10_PCREL_RELA 36 +#define R_M32R_18_PCREL_RELA 37 +#define R_M32R_26_PCREL_RELA 38 +#define R_M32R_HI16_ULO_RELA 39 +#define R_M32R_HI16_SLO_RELA 40 +#define R_M32R_LO16_RELA 41 +#define R_M32R_SDA16_RELA 42 +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 + +#define R_M32R_GOT24 48 +#define R_M32R_26_PLTREL 49 +#define R_M32R_COPY 50 +#define R_M32R_GLOB_DAT 51 +#define R_M32R_JMP_SLOT 52 +#define R_M32R_RELATIVE 53 +#define R_M32R_GOTOFF 54 +#define R_M32R_GOTPC24 55 +#define R_M32R_GOT16_HI_ULO 56 + +#define R_M32R_GOT16_HI_SLO 57 + +#define R_M32R_GOT16_LO 58 +#define R_M32R_GOTPC_HI_ULO 59 + +#define R_M32R_GOTPC_HI_SLO 60 + +#define R_M32R_GOTPC_LO 61 + +#define R_M32R_GOTOFF_HI_ULO 62 + +#define R_M32R_GOTOFF_HI_SLO 63 + +#define R_M32R_GOTOFF_LO 64 +#define R_M32R_NUM 256 + +#define R_MICROBLAZE_NONE 0 +#define R_MICROBLAZE_32 1 +#define R_MICROBLAZE_32_PCREL 2 +#define R_MICROBLAZE_64_PCREL 3 +#define R_MICROBLAZE_32_PCREL_LO 4 +#define R_MICROBLAZE_64 5 +#define R_MICROBLAZE_32_LO 6 +#define R_MICROBLAZE_SRO32 7 +#define R_MICROBLAZE_SRW32 8 +#define R_MICROBLAZE_64_NONE 9 +#define R_MICROBLAZE_32_SYM_OP_SYM 10 +#define R_MICROBLAZE_GNU_VTINHERIT 11 +#define R_MICROBLAZE_GNU_VTENTRY 12 +#define R_MICROBLAZE_GOTPC_64 13 +#define R_MICROBLAZE_GOT_64 14 +#define R_MICROBLAZE_PLT_64 15 +#define R_MICROBLAZE_REL 16 +#define R_MICROBLAZE_JUMP_SLOT 17 +#define R_MICROBLAZE_GLOB_DAT 18 +#define R_MICROBLAZE_GOTOFF_64 19 +#define R_MICROBLAZE_GOTOFF_32 20 +#define R_MICROBLAZE_COPY 21 +#define R_MICROBLAZE_TLS 22 +#define R_MICROBLAZE_TLSGD 23 +#define R_MICROBLAZE_TLSLD 24 +#define R_MICROBLAZE_TLSDTPMOD32 25 +#define R_MICROBLAZE_TLSDTPREL32 26 +#define R_MICROBLAZE_TLSDTPREL64 27 +#define R_MICROBLAZE_TLSGOTTPREL32 28 +#define R_MICROBLAZE_TLSTPREL32 29 + +#define DT_NIOS2_GP 0x70000002 + +#define R_NIOS2_NONE 0 +#define R_NIOS2_S16 1 +#define R_NIOS2_U16 2 +#define R_NIOS2_PCREL16 3 +#define R_NIOS2_CALL26 4 +#define R_NIOS2_IMM5 5 +#define R_NIOS2_CACHE_OPX 6 +#define R_NIOS2_IMM6 7 +#define R_NIOS2_IMM8 8 +#define R_NIOS2_HI16 9 +#define R_NIOS2_LO16 10 +#define R_NIOS2_HIADJ16 11 +#define R_NIOS2_BFD_RELOC_32 12 +#define R_NIOS2_BFD_RELOC_16 13 +#define R_NIOS2_BFD_RELOC_8 14 +#define R_NIOS2_GPREL 15 +#define R_NIOS2_GNU_VTINHERIT 16 +#define R_NIOS2_GNU_VTENTRY 17 +#define R_NIOS2_UJMP 18 +#define R_NIOS2_CJMP 19 +#define R_NIOS2_CALLR 20 +#define R_NIOS2_ALIGN 21 +#define R_NIOS2_GOT16 22 +#define R_NIOS2_CALL16 23 +#define R_NIOS2_GOTOFF_LO 24 +#define R_NIOS2_GOTOFF_HA 25 +#define R_NIOS2_PCREL_LO 26 +#define R_NIOS2_PCREL_HA 27 +#define R_NIOS2_TLS_GD16 28 +#define R_NIOS2_TLS_LDM16 29 +#define R_NIOS2_TLS_LDO16 30 +#define R_NIOS2_TLS_IE16 31 +#define R_NIOS2_TLS_LE16 32 +#define R_NIOS2_TLS_DTPMOD 33 +#define R_NIOS2_TLS_DTPREL 34 +#define R_NIOS2_TLS_TPREL 35 +#define R_NIOS2_COPY 36 +#define R_NIOS2_GLOB_DAT 37 +#define R_NIOS2_JUMP_SLOT 38 +#define R_NIOS2_RELATIVE 39 +#define R_NIOS2_GOTOFF 40 +#define R_NIOS2_CALL26_NOAT 41 +#define R_NIOS2_GOT_LO 42 +#define R_NIOS2_GOT_HA 43 +#define R_NIOS2_CALL_LO 44 +#define R_NIOS2_CALL_HA 45 + +#define R_OR1K_NONE 0 +#define R_OR1K_32 1 +#define R_OR1K_16 2 +#define R_OR1K_8 3 +#define R_OR1K_LO_16_IN_INSN 4 +#define R_OR1K_HI_16_IN_INSN 5 +#define R_OR1K_INSN_REL_26 6 +#define R_OR1K_GNU_VTENTRY 7 +#define R_OR1K_GNU_VTINHERIT 8 +#define R_OR1K_32_PCREL 9 +#define R_OR1K_16_PCREL 10 +#define R_OR1K_8_PCREL 11 +#define R_OR1K_GOTPC_HI16 12 +#define R_OR1K_GOTPC_LO16 13 +#define R_OR1K_GOT16 14 +#define R_OR1K_PLT26 15 +#define R_OR1K_GOTOFF_HI16 16 +#define R_OR1K_GOTOFF_LO16 17 +#define R_OR1K_COPY 18 +#define R_OR1K_GLOB_DAT 19 +#define R_OR1K_JMP_SLOT 20 +#define R_OR1K_RELATIVE 21 +#define R_OR1K_TLS_GD_HI16 22 +#define R_OR1K_TLS_GD_LO16 23 +#define R_OR1K_TLS_LDM_HI16 24 +#define R_OR1K_TLS_LDM_LO16 25 +#define R_OR1K_TLS_LDO_HI16 26 +#define R_OR1K_TLS_LDO_LO16 27 +#define R_OR1K_TLS_IE_HI16 28 +#define R_OR1K_TLS_IE_LO16 29 +#define R_OR1K_TLS_LE_HI16 30 +#define R_OR1K_TLS_LE_LO16 31 +#define R_OR1K_TLS_TPOFF 32 +#define R_OR1K_TLS_DTPOFF 33 +#define R_OR1K_TLS_DTPMOD 34 + +#define R_BPF_NONE 0 +#define R_BPF_MAP_FD 1 + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/emummc/source/nx/smc.c b/emummc/source/nx/smc.c new file mode 100644 index 000000000..921d9be69 --- /dev/null +++ b/emummc/source/nx/smc.c @@ -0,0 +1,180 @@ +/** + * @file smc.c + * @copyright libnx Authors + */ + +#include +#include +#include "smc.h" + +void smcRebootToRcm(void) +{ + SecmonArgs args; + args.X[0] = 0xC3000401; /* smcSetConfig */ + args.X[1] = SplConfigItem_NeedsReboot; /* Exosphere reboot */ + args.X[3] = 1; /* Perform reboot to RCM. */ + svcCallSecureMonitor(&args); +} + +void smcRebootToIramPayload(void) +{ + SecmonArgs args; + args.X[0] = 0xC3000401; /* smcSetConfig */ + args.X[1] = SplConfigItem_NeedsReboot; /* Exosphere reboot */ + args.X[3] = 2; /* Perform reboot to payload at 0x40010000 in IRAM. */ + svcCallSecureMonitor(&args); +} + +void smcPerformShutdown(void) +{ + SecmonArgs args; + args.X[0] = 0xC3000401; /* smcSetConfig */ + args.X[1] = SplConfigItem_NeedsShutdown; /* Exosphere shutdown */ + args.X[3] = 1; /* Perform shutdown. */ + svcCallSecureMonitor(&args); +} + +Result smcGetConfig(SplConfigItem config_item, u64 *out_config) +{ + SecmonArgs args; + args.X[0] = 0xC3000002; /* smcGetConfig */ + args.X[1] = (u64)config_item; /* config item */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] == 0) + { + if (out_config) + { + *out_config = args.X[1]; + } + } + else + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + } + return rc; +} + +Result smcCopyToIram(uintptr_t iram_addr, const void *src_addr, u32 size) +{ + SecmonArgs args; + args.X[0] = 0xF0000201; /* smcAmsIramCopy */ + args.X[1] = (u64)src_addr; /* DRAM address */ + args.X[2] = (u64)iram_addr; /* IRAM address */ + args.X[3] = size; /* Amount to copy */ + args.X[4] = 1; /* 1 = Write */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + } + return rc; +} + +Result smcCopyFromIram(void *dst_addr, uintptr_t iram_addr, u32 size) +{ + SecmonArgs args; + args.X[0] = 0xF0000201; /* smcAmsIramCopy */ + args.X[1] = (u64)dst_addr; /* DRAM address */ + args.X[2] = (u64)iram_addr; /* IRAM address */ + args.X[3] = size; /* Amount to copy */ + args.X[4] = 0; /* 0 = Read */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + } + return rc; +} + +Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask) +{ + SecmonArgs args; + args.X[0] = 0xF0000002; /* smcAmsReadWriteRegister */ + args.X[1] = phys_addr; /* MMIO address */ + args.X[2] = mask; /* mask */ + args.X[3] = value; /* value */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + } + return rc; +} + +static Result _smcWriteAddress(void *dst_addr, u64 val, u32 size) +{ + SecmonArgs args; + args.X[0] = 0xF0000003; /* smcAmsWriteAddress */ + args.X[1] = (u64)dst_addr; /* DRAM address */ + args.X[2] = val; /* value */ + args.X[3] = size; /* Amount to write */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + } + return rc; +} + +Result smcWriteAddress8(void *dst_addr, u8 val) +{ + return _smcWriteAddress(dst_addr, val, 1); +} + +Result smcWriteAddress16(void *dst_addr, u16 val) +{ + return _smcWriteAddress(dst_addr, val, 2); +} + +Result smcWriteAddress32(void *dst_addr, u32 val) +{ + return _smcWriteAddress(dst_addr, val, 4); +} + +Result smcWriteAddress64(void *dst_addr, u64 val) +{ + return _smcWriteAddress(dst_addr, val, 8); +} + +Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths) +{ + SecmonArgs args; + args.X[0] = 0xF0000404; /* smcAmsGetEmunandConfig */ + args.X[1] = mmc_id; + args.X[2] = (u64)out_paths; /* out path */ + Result rc = svcCallSecureMonitor(&args); + if (rc == 0) + { + if (args.X[0] != 0) + { + /* SPL result n = SMC result n */ + rc = (26u | ((u32)args.X[0] << 9u)); + } + if (rc == 0) + { + memcpy(out_cfg, &args.X[1], sizeof(*out_cfg)); + } + } + return rc; + +} \ No newline at end of file diff --git a/emummc/source/nx/smc.h b/emummc/source/nx/smc.h new file mode 100644 index 000000000..cb8069b28 --- /dev/null +++ b/emummc/source/nx/smc.h @@ -0,0 +1,90 @@ +/** + * @file smc.h + * @brief Wrappers for secure monitor calls. + * @copyright libnx Authors + */ +#pragma once +#include "../utils/types.h" +#include "svc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SplConfigItem_DisableProgramVerification = 1, + SplConfigItem_DramId = 2, + SplConfigItem_SecurityEngineIrqNumber = 3, + SplConfigItem_Version = 4, + SplConfigItem_HardwareType = 5, + SplConfigItem_IsRetail = 6, + SplConfigItem_IsRecoveryBoot = 7, + SplConfigItem_DeviceId = 8, + SplConfigItem_BootReason = 9, + SplConfigItem_MemoryArrange = 10, + SplConfigItem_IsDebugMode = 11, + SplConfigItem_KernelMemoryConfiguration = 12, + SplConfigItem_IsChargerHiZModeEnabled = 13, + SplConfigItem_IsKiosk = 14, + SplConfigItem_NewHardwareType = 15, + SplConfigItem_NewKeyGeneration = 16, + SplConfigItem_Package2Hash = 17, + + SplConfigItem_ExosphereVersion = 65000, + SplConfigItem_NeedsReboot = 65001, + SplConfigItem_NeedsShutdown = 65002, + SplConfigItem_ExosphereVerHash = 65003, + SplConfigItem_HasRcmBugPatch = 65004, +} SplConfigItem; + +typedef enum { + EXO_EMUMMC_TYPE_NONE = 0, + EXO_EMUMMC_TYPE_PARTITION = 1, + EXO_EMUMMC_TYPE_FILES = 2, +} exo_emummc_type_t; + +typedef enum { + EXO_EMUMMC_MMC_NAND = 0, + EXO_EMUMMC_MMC_SD = 1, + EXO_EMUMMC_MMC_GC = 2, +} exo_emummc_mmc_t; + +typedef struct { + uint32_t magic; + uint32_t type; + uint32_t id; + uint32_t fs_version; +} exo_emummc_base_config_t; + +typedef struct { + uint64_t start_sector; +} exo_emummc_partition_config_t; + +typedef struct { + exo_emummc_base_config_t base_cfg; + union { + exo_emummc_partition_config_t partition_cfg; + }; +} exo_emummc_config_t; + +Result smcGetConfig(SplConfigItem config_item, u64 *out_config); + +void smcRebootToRcm(void); +void smcRebootToIramPayload(void); +void smcPerformShutdown(void); + +Result smcCopyToIram(uintptr_t iram_addr, const void *src_addr, u32 size); +Result smcCopyFromIram(void *dst_addr, uintptr_t iram_addr, u32 size); + +Result smcReadWriteRegister(u32 phys_addr, u32 value, u32 mask); + +Result smcWriteAddress8(void *dst_addr, u8 val); +Result smcWriteAddress16(void *dst_addr, u16 val); +Result smcWriteAddress32(void *dst_addr, u32 val); +Result smcWriteAddress64(void *dst_addr, u64 val); + +Result smcGetEmummcConfig(exo_emummc_mmc_t mmc_id, exo_emummc_config_t *out_cfg, void *out_paths); + +#ifdef __cplusplus +} +#endif diff --git a/emummc/source/nx/start.s b/emummc/source/nx/start.s new file mode 100644 index 000000000..fdca838e8 --- /dev/null +++ b/emummc/source/nx/start.s @@ -0,0 +1,131 @@ +/** + * @file start.s + * @copyright libnx Authors + */ + +.macro push_all + SUB SP, SP, #0x100 + STP X29, X30, [SP, #0x0] + STP X27, X28, [SP, #0x10] + STP X25, X26, [SP, #0x20] + STP X23, X24, [SP, #0x30] + STP X21, X22, [SP, #0x40] + STP X19, X20, [SP, #0x50] + STP X17, X18, [SP, #0x60] + STP X15, X16, [SP, #0x70] + STP X13, X14, [SP, #0x80] + STP X11, X12, [SP, #0x90] + STP X9, X10, [SP, #0xA0] + STP X7, X8, [SP, #0xB0] + STP X5, X6, [SP, #0xC0] + STP X3, X4, [SP, #0xD0] + STP X1, X2, [SP, #0xE0] + STR X0, [SP, #0xF0] +.endm + +.macro pop_all + LDR X0, [SP, #0xF0] + LDP X1, X2, [SP, #0xE0] + LDP X3, X4, [SP, #0xD0] + LDP X5, X6, [SP, #0xC0] + LDP X7, X8, [SP, #0xB0] + LDP X9, X10, [SP, #0xA0] + LDP X11, X12, [SP, #0x90] + LDP X13, X14, [SP, #0x80] + LDP X15, X16, [SP, #0x70] + LDP X17, X18, [SP, #0x60] + LDP X19, X20, [SP, #0x50] + LDP X21, X22, [SP, #0x40] + LDP X23, X24, [SP, #0x30] + LDP X25, X26, [SP, #0x20] + LDP X27, X28, [SP, #0x10] + LDP X29, X30, [SP, #0x0] + ADD SP, SP, #0x100 +.endm + +.section ".crt0","ax" +.global _start +_start: + B startup +.org _start+0xc + B sdmmc_wrapper_read +.org _start+0x18 + B sdmmc_wrapper_write +.org _start+0x80 + +.section ".crt0","ax" +startup: + # Save LR + MOV X7, X30 + + # Retrieve ASLR Base + BL +4 + SUB X6, X30, #0x88 + + # Context Ptr and MainThread Handle + MOV X5, X0 + MOV X4, X1 + + # Inject start + push_all + + MOV W0, #0xFFFF8001 + ADR X1, __rodata_start + ADR X2, __data_start + SUB X2, X2, X1 + MOV X3, #1 + SVC 0x73 + + MOV W0, #0xFFFF8001 + ADR X1, __data_start + ADR X2, __end__ + SUB X2, X2, X1 + MOV X3, #3 + SVC 0x73 + + pop_all + + MOV X27, X7 + MOV X25, X5 + MOV X26, X4 + + # Clear .bss + ADRP X0, __bss_start__ + ADRP X1, __bss_end__ + ADD X0, X0, #:lo12:__bss_start__ + ADD X1, X1, #:lo12:__bss_end__ + SUB X1, X1, X0 + ADD X1, X1, #7 + BIC X1, X1, #7 + +bss_loop: + STR XZR, [X0], #8 + SUBS X1, X1, #8 + BNE bss_loop + + # Store SP + MOV X1, SP + ADRP X0, __stack_top + STR X1, [X0, #:lo12:__stack_top] + + # Process _DYNAMIC Section + MOV X0, X6 + ADRP X1, _DYNAMIC + ADD X1, X1, #:lo12:_DYNAMIC + BL __nx_dynamic + + # TODO: handle in code + MOV X0, X25 + MOV X1, X26 + MOV X2, X27 + + BL __initheap + BL __init + + MOV X0, X25 + MOV X1, X26 + MOV X30, X27 + + # FS main + ADR X16, __injected_size__ + BR X16 diff --git a/emummc/source/nx/svc.h b/emummc/source/nx/svc.h new file mode 100644 index 000000000..69819ec34 --- /dev/null +++ b/emummc/source/nx/svc.h @@ -0,0 +1,112 @@ +/** + * @file svc.h + * @brief Wrappers for kernel syscalls. + * @copyright libnx Authors + */ +#pragma once +#include "../utils/types.h" + +/// Memory information structure. +typedef struct { + u64 addr; ///< Base address. + u64 size; ///< Size. + u32 type; ///< Memory type (see lower 8 bits of \ref MemoryState). + u32 attr; ///< Memory attributes (see \ref MemoryAttribute). + u32 perm; ///< Memory permissions (see \ref Permission). + u32 device_refcount; ///< Device reference count. + u32 ipc_refcount; ///< IPC reference count. + u32 padding; ///< Padding. +} MemoryInfo; + +/// Memory permission bitmasks. +typedef enum { + Perm_None = 0, ///< No permissions. + Perm_R = BIT(0), ///< Read permission. + Perm_W = BIT(1), ///< Write permission. + Perm_X = BIT(2), ///< Execute permission. + Perm_Rw = Perm_R | Perm_W, ///< Read/write permissions. + Perm_Rx = Perm_R | Perm_X, ///< Read/execute permissions. + Perm_DontCare = BIT(28), ///< Don't care +} Permission; + +/// Secure monitor arguments. +typedef struct { + u64 X[8]; ///< Values of X0 through X7. +} SecmonArgs; + +_Static_assert(sizeof(SecmonArgs) == 0x40, "SecmonArgs definition"); + +#define DeviceName_SDMMC1A 19 +#define DeviceName_SDMMC2A 20 +#define DeviceName_SDMMC3A 21 +#define DeviceName_SDMMC4A 22 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Returns a virtual address mapped to a given IO range. + * @return Result code. + * @note Syscall number 0x55. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcQueryIoMapping(u64* virtaddr, u64 physaddr, u64 size); + +/** + * @brief Attaches a device address space to a device. + * @return Result code. + * @note Syscall number 0x57. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcAttachDeviceAddressSpace(u64 device, Handle handle); + +/** + * @brief Query information about an address. Will always fetch the lowest page-aligned mapping that contains the provided address. + * @param[out] meminfo_ptr \ref MemoryInfo structure which will be filled in. + * @param[out] pageinfo Page information which will be filled in. + * @param[in] addr Address to query. + * @return Result code. + * @note Syscall number 0x06. + */ +Result svcQueryMemory(MemoryInfo* meminfo_ptr, u32 *pageinfo, u64 addr); + +/** + * @brief Sets the memory permissions for the specified memory with the supplied process handle. + * @param[in] proc Process handle. + * @param[in] addr Address of the memory. + * @param[in] size Size of the memory. + * @param[in] perm Permissions (see \ref Permission). + * @return Result code. + * @remark This returns an error (0xD801) when \p perm is >0x5, hence -WX and RWX are not allowed. + * @note Syscall number 0x73. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +Result svcSetProcessMemoryPermission(Handle proc, u64 addr, u64 size, u32 perm); + +/** + * @brief Set the memory permissions of a (page-aligned) range of memory. + * @param[in] addr Start address of the range. + * @param[in] size Size of the range, in bytes. + * @param[in] perm Permissions (see \ref Permission). + * @return Result code. + * @remark Perm_X is not allowed. Setting write-only is not allowed either (Perm_W). + * This can be used to move back and forth between Perm_None, Perm_R and Perm_Rw. + * @note Syscall number 0x01. + */ +Result svcSetMemoryPermission(void* addr, u64 size, u32 perm); + +/** + * @brief Calls a secure monitor function (TrustZone, EL3). + * @param regs Arguments to pass to the secure monitor. + * @return Return value from the secure monitor. + * @note Syscall number 0x7F. + * @warning This is a privileged syscall. Use \ref envIsSyscallHinted to check if it is available. + */ +u64 svcCallSecureMonitor(SecmonArgs* regs); + +#ifdef __cplusplus +} +#endif + +///@} diff --git a/emummc/source/nx/svc.s b/emummc/source/nx/svc.s new file mode 100644 index 000000000..360d9c0ef --- /dev/null +++ b/emummc/source/nx/svc.s @@ -0,0 +1,64 @@ +/** + * @file svc.s + * @copyright libnx Authors + */ + +.macro SVC_BEGIN name + .section .text.\name, "ax", %progbits + .global \name + .type \name, %function + .align 2 + .cfi_startproc +\name: +.endm + +.macro SVC_END + .cfi_endproc +.endm + +SVC_BEGIN svcQueryIoMapping + STR X0, [SP, #-16]! + SVC 0x55 + LDR X2, [SP], #16 + STR X1, [X2] + RET +SVC_END + +SVC_BEGIN svcAttachDeviceAddressSpace + SVC 0x57 + RET +SVC_END + +SVC_BEGIN svcQueryMemory + STR X1, [SP, #-16]! + SVC 0x6 + LDR X2, [SP], #16 + STR W1, [X2] + RET +SVC_END + +SVC_BEGIN svcSetMemoryPermission + SVC 0x2 + RET +SVC_END + +SVC_BEGIN svcSetProcessMemoryPermission + SVC 0x73 + RET +SVC_END + +SVC_BEGIN svcCallSecureMonitor + STR X0, [SP, #-16]! + MOV X8, X0 + LDP X0, X1, [X8] + LDP X2, X3, [X8, #0x10] + LDP X4, X5, [X8, #0x20] + LDP X6, X7, [X8, #0x30] + SVC 0x7F + LDR X8, [SP], #16 + STP X0, X1, [X8] + STP X2, X3, [X8, #0x10] + STP X4, X5, [X8, #0x20] + STP X6, X7, [X8, #0x30] + RET +SVC_END \ No newline at end of file diff --git a/emummc/source/power/max77620.h b/emummc/source/power/max77620.h new file mode 100644 index 000000000..fcce30980 --- /dev/null +++ b/emummc/source/power/max77620.h @@ -0,0 +1,331 @@ +/* + * Defining registers address and its bit definitions of MAX77620 and MAX20024 + * + * Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _MFD_MAX77620_H_ +#define _MFD_MAX77620_H_ + +#define MAX77620_I2C_ADDR 0x3C + +/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */ +#define MAX77620_REG_CNFGGLBL1 0x00 +#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7) +#define MAX77620_CNFGGLBL1_MPPLD (1 << 6) +#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4)) +#define MAX77620_CNFGGLBL1_LBHYST_N (1 << 4) +#define MAX77620_CNFGGLBL1_LBDAC 0x0E +#define MAX77620_CNFGGLBL1_LBDAC_N (1 << 1) +#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0) + +#define MAX77620_REG_CNFGGLBL2 0x01 +#define MAX77620_REG_CNFGGLBL3 0x02 +#define MAX77620_WDTC_MASK 0x3 +#define MAX77620_WDTOFFC (1 << 4) +#define MAX77620_WDTSLPC (1 << 3) +#define MAX77620_WDTEN (1 << 2) +#define MAX77620_TWD_MASK 0x3 +#define MAX77620_TWD_2s 0x0 +#define MAX77620_TWD_16s 0x1 +#define MAX77620_TWD_64s 0x2 +#define MAX77620_TWD_128s 0x3 + +#define MAX77620_REG_CNFG1_32K 0x03 +#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2) + +#define MAX77620_REG_CNFGBBC 0x04 +#define MAX77620_CNFGBBC_ENABLE (1 << 0) +#define MAX77620_CNFGBBC_CURRENT_MASK 0x06 +#define MAX77620_CNFGBBC_CURRENT_SHIFT 1 +#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18 +#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3 +#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5) +#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0 +#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6 +#define MAX77620_CNFGBBC_RESISTOR_100 (0 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_1K (1 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_3K (2 << MAX77620_CNFGBBC_RESISTOR_SHIFT) +#define MAX77620_CNFGBBC_RESISTOR_6K (3 << MAX77620_CNFGBBC_RESISTOR_SHIFT) + +#define MAX77620_REG_IRQTOP 0x05 +#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7) +#define MAX77620_IRQ_TOP_SD_MASK (1 << 6) +#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5) +#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4) +#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3) +#define MAX77620_IRQ_TOP_32K_MASK (1 << 2) +#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1) + +#define MAX77620_REG_INTLBT 0x06 +#define MAX77620_REG_IRQTOPM 0x0D +#define MAX77620_IRQ_LBM_MASK (1 << 3) +#define MAX77620_IRQ_TJALRM1_MASK (1 << 2) +#define MAX77620_IRQ_TJALRM2_MASK (1 << 1) + +#define MAX77620_REG_IRQSD 0x07 +#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 +#define MAX77620_REG_IRQ_LVL2_L8 0x09 +#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A +#define MAX77620_REG_ONOFFIRQ 0x0B +#define MAX77620_REG_NVERC 0x0C + +#define MAX77620_REG_INTENLBT 0x0E +#define MAX77620_GLBLM_MASK (1 << 0) + +#define MAX77620_REG_IRQMASKSD 0x0F +#define MAX77620_REG_IRQ_MSK_L0_7 0x10 +#define MAX77620_REG_IRQ_MSK_L8 0x11 +#define MAX77620_REG_ONOFFIRQM 0x12 +#define MAX77620_REG_STATLBT 0x13 +#define MAX77620_REG_STATSD 0x14 +#define MAX77620_REG_ONOFFSTAT 0x15 + +/* SD and LDO Registers */ +#define MAX77620_REG_SD0 0x16 +#define MAX77620_REG_SD1 0x17 +#define MAX77620_REG_SD2 0x18 +#define MAX77620_REG_SD3 0x19 +#define MAX77620_REG_SD4 0x1A +#define MAX77620_SDX_VOLT_MASK 0xFF +#define MAX77620_SD0_VOLT_MASK 0x3F +#define MAX77620_SD1_VOLT_MASK 0x7F +#define MAX77620_LDO_VOLT_MASK 0x3F +#define MAX77620_REG_DVSSD0 0x1B +#define MAX77620_REG_DVSSD1 0x1C +#define MAX77620_REG_SD0_CFG 0x1D +#define MAX77620_REG_SD1_CFG 0x1E +#define MAX77620_REG_SD2_CFG 0x1F +#define MAX77620_REG_SD3_CFG 0x20 +#define MAX77620_REG_SD4_CFG 0x21 +#define MAX77620_REG_SD_CFG2 0x22 +#define MAX77620_REG_LDO0_CFG 0x23 +#define MAX77620_REG_LDO0_CFG2 0x24 +#define MAX77620_REG_LDO1_CFG 0x25 +#define MAX77620_REG_LDO1_CFG2 0x26 +#define MAX77620_REG_LDO2_CFG 0x27 +#define MAX77620_REG_LDO2_CFG2 0x28 +#define MAX77620_REG_LDO3_CFG 0x29 +#define MAX77620_REG_LDO3_CFG2 0x2A +#define MAX77620_REG_LDO4_CFG 0x2B +#define MAX77620_REG_LDO4_CFG2 0x2C +#define MAX77620_REG_LDO5_CFG 0x2D +#define MAX77620_REG_LDO5_CFG2 0x2E +#define MAX77620_REG_LDO6_CFG 0x2F +#define MAX77620_REG_LDO6_CFG2 0x30 +#define MAX77620_REG_LDO7_CFG 0x31 +#define MAX77620_REG_LDO7_CFG2 0x32 +#define MAX77620_REG_LDO8_CFG 0x33 +#define MAX77620_REG_LDO8_CFG2 0x34 +#define MAX77620_LDO_POWER_MODE_MASK 0xC0 +#define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX77620_POWER_MODE_NORMAL 3 +#define MAX77620_POWER_MODE_LPM 2 +#define MAX77620_POWER_MODE_GLPM 1 +#define MAX77620_POWER_MODE_DISABLE 0 +#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2) +#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1) +#define MAX77620_LDO_CFG2_ADE_DISABLE 0 +#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1) +#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) +#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) +#define MAX77620_LDO_CFG2_SS_SLOW 0 + +#define MAX77620_REG_LDO_CFG3 0x35 +#define MAX77620_TRACK4_MASK (1 << 5) +#define MAX77620_TRACK4_SHIFT 5 + +#define MAX77620_LDO_SLEW_RATE_MASK 0x1 + +#define MAX77620_REG_GPIO0 0x36 +#define MAX77620_REG_GPIO1 0x37 +#define MAX77620_REG_GPIO2 0x38 +#define MAX77620_REG_GPIO3 0x39 +#define MAX77620_REG_GPIO4 0x3A +#define MAX77620_REG_GPIO5 0x3B +#define MAX77620_REG_GPIO6 0x3C +#define MAX77620_REG_GPIO7 0x3D +#define MAX77620_REG_PUE_GPIO 0x3E +#define MAX77620_REG_PDE_GPIO 0x3F +#define MAX77620_REG_AME_GPIO 0x40 + +#define MAX77620_REG_ONOFFCNFG1 0x41 +#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) +#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38 +#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3 +#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2) +#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1) +#define MAX20024_ONOFFCNFG1_CLRSE 0x18 + +#define MAX77620_REG_ONOFFCNFG2 0x42 +#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7) +#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6) +#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5) +#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2) +#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0) + +/* FPS Registers */ +#define MAX77620_REG_FPS_CFG0 0x43 +#define MAX77620_REG_FPS_CFG1 0x44 +#define MAX77620_REG_FPS_CFG2 0x45 +#define MAX77620_REG_FPS_LDO0 0x46 +#define MAX77620_REG_FPS_LDO1 0x47 +#define MAX77620_REG_FPS_LDO2 0x48 +#define MAX77620_REG_FPS_LDO3 0x49 +#define MAX77620_REG_FPS_LDO4 0x4A +#define MAX77620_REG_FPS_LDO5 0x4B +#define MAX77620_REG_FPS_LDO6 0x4C +#define MAX77620_REG_FPS_LDO7 0x4D +#define MAX77620_REG_FPS_LDO8 0x4E +#define MAX77620_REG_FPS_SD0 0x4F +#define MAX77620_REG_FPS_SD1 0x50 +#define MAX77620_REG_FPS_SD2 0x51 +#define MAX77620_REG_FPS_SD3 0x52 +#define MAX77620_REG_FPS_SD4 0x53 +#define MAX77620_REG_FPS_NONE 0 +#define MAX77620_FPS_SRC_MASK 0xC0 +#define MAX77620_FPS_SRC_SHIFT 6 +#define MAX77620_FPS_PU_PERIOD_MASK 0x38 +#define MAX77620_FPS_PU_PERIOD_SHIFT 3 +#define MAX77620_FPS_PD_PERIOD_MASK 0x07 +#define MAX77620_FPS_PD_PERIOD_SHIFT 0 + +/* Minimum and maximum FPS period time (in microseconds) are + * different for MAX77620 and Max20024. + */ +#define MAX77620_FPS_COUNT 3 + +#define MAX77620_FPS_PERIOD_MIN_US 40 +#define MAX20024_FPS_PERIOD_MIN_US 20 + +#define MAX77620_FPS_PERIOD_MAX_US 2560 +#define MAX20024_FPS_PERIOD_MAX_US 5120 + +#define MAX77620_REG_FPS_GPIO1 0x54 +#define MAX77620_REG_FPS_GPIO2 0x55 +#define MAX77620_REG_FPS_GPIO3 0x56 +#define MAX77620_FPS_TIME_PERIOD_MASK 0x38 +#define MAX77620_FPS_TIME_PERIOD_SHIFT 3 +#define MAX77620_FPS_EN_SRC_MASK 0x06 +#define MAX77620_FPS_EN_SRC_SHIFT 1 +#define MAX77620_FPS_ENFPS_SW_MASK 0x01 +#define MAX77620_FPS_ENFPS_SW 0x01 + +#define MAX77620_REG_FPS_RSO 0x57 +#define MAX77620_REG_CID0 0x58 +#define MAX77620_REG_CID1 0x59 +#define MAX77620_REG_CID2 0x5A +#define MAX77620_REG_CID3 0x5B +#define MAX77620_REG_CID4 0x5C +#define MAX77620_REG_CID5 0x5D + +#define MAX77620_REG_DVSSD4 0x5E +#define MAX20024_REG_MAX_ADD 0x70 + +#define MAX77620_CID_DIDM_MASK 0xF0 +#define MAX77620_CID_DIDM_SHIFT 4 + +/* CNCG2SD */ +#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1) +#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2) + +/* Device Identification Metal */ +#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF) +/* Device Indentification OTP */ +#define MAX77620_CID5_DIDO(n) ((n) & 0xF) + +/* SD CNFG1 */ +#define MAX77620_SD_SR_MASK 0xC0 +#define MAX77620_SD_SR_SHIFT 6 +#define MAX77620_SD_POWER_MODE_MASK 0x30 +#define MAX77620_SD_POWER_MODE_SHIFT 4 +#define MAX77620_SD_CFG1_ADE_MASK (1 << 3) +#define MAX77620_SD_CFG1_ADE_DISABLE 0 +#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3) +#define MAX77620_SD_FPWM_MASK 0x04 +#define MAX77620_SD_FPWM_SHIFT 2 +#define MAX77620_SD_FSRADE_MASK 0x01 +#define MAX77620_SD_FSRADE_SHIFT 0 +#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2) +#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 +#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2) +#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1) +#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0) +#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 +#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) + +#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0) +#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN 0 +#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1) +#define MAX77620_CNFG_GPIO_DIR_OUTPUT 0 +#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3) +#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW 0 +#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4) +#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4) +#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5) +#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6) +#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6) +#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6) +#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6) +#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6) + +#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) +#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) +#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) +#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3) +#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4) +#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5) +#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6) +#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7) + +/* Interrupts */ +enum { + MAX77620_IRQ_TOP_GLBL, /* Low-Battery */ + MAX77620_IRQ_TOP_SD, /* SD power fail */ + MAX77620_IRQ_TOP_LDO, /* LDO power fail */ + MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */ + MAX77620_IRQ_TOP_RTC, /* RTC */ + MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */ + MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */ + MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */ + MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */ +}; + +/* GPIOs */ +enum { + MAX77620_GPIO0, + MAX77620_GPIO1, + MAX77620_GPIO2, + MAX77620_GPIO3, + MAX77620_GPIO4, + MAX77620_GPIO5, + MAX77620_GPIO6, + MAX77620_GPIO7, + MAX77620_GPIO_NR, +}; + +/* FPS Source */ +enum max77620_fps_src { + MAX77620_FPS_SRC_0, + MAX77620_FPS_SRC_1, + MAX77620_FPS_SRC_2, + MAX77620_FPS_SRC_NONE, + MAX77620_FPS_SRC_DEF, +}; + +enum max77620_chip_id { + MAX77620, + MAX20024, +}; + +#endif /* _MFD_MAX77620_H_ */ diff --git a/emummc/source/power/max7762x.c b/emummc/source/power/max7762x.c new file mode 100644 index 000000000..2c8cff402 --- /dev/null +++ b/emummc/source/power/max7762x.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "max7762x.h" +#include "max77620.h" +#include "../soc/i2c.h" +#include "../utils/util.h" + +#define REGULATOR_SD 0 +#define REGULATOR_LDO 1 + +typedef struct _max77620_regulator_t +{ + u8 type; + const char *name; + u8 reg_sd; + + u32 mv_step; + u32 mv_min; + u32 mv_default; + u32 mv_max; + + u8 volt_addr; + u8 cfg_addr; + + u8 volt_mask; + u8 enable_mask; + u8 enable_shift; + u8 status_mask; + + u8 fps_addr; + u8 fps_src; + u8 pd_period; + u8 pu_period; +} max77620_regulator_t; + +static const max77620_regulator_t _pmic_regulators[] = { + { REGULATOR_SD, "sd0", 0x16, 12500, 600000, 625000, 1400000, MAX77620_REG_SD0, MAX77620_REG_SD0_CFG, MAX77620_SD0_VOLT_MASK, MAX77620_SD_POWER_MODE_MASK, MAX77620_SD_POWER_MODE_SHIFT, 0x80, MAX77620_REG_FPS_SD0, 1, 7, 1 }, + { REGULATOR_SD, "sd1", 0x17, 12500, 600000, 1125000, 1125000, MAX77620_REG_SD1, MAX77620_REG_SD1_CFG, MAX77620_SD1_VOLT_MASK, MAX77620_SD_POWER_MODE_MASK, MAX77620_SD_POWER_MODE_SHIFT, 0x40, MAX77620_REG_FPS_SD1, 0, 1, 5 }, + { REGULATOR_SD, "sd2", 0x18, 12500, 600000, 1325000, 1350000, MAX77620_REG_SD2, MAX77620_REG_SD2_CFG, MAX77620_SDX_VOLT_MASK, MAX77620_SD_POWER_MODE_MASK, MAX77620_SD_POWER_MODE_SHIFT, 0x20, MAX77620_REG_FPS_SD2, 1, 5, 2 }, + { REGULATOR_SD, "sd3", 0x19, 12500, 600000, 1800000, 1800000, MAX77620_REG_SD3, MAX77620_REG_SD3_CFG, MAX77620_SDX_VOLT_MASK, MAX77620_SD_POWER_MODE_MASK, MAX77620_SD_POWER_MODE_SHIFT, 0x10, MAX77620_REG_FPS_SD3, 0, 3, 3 }, + { REGULATOR_LDO, "ldo0", 0x00, 25000, 800000, 1200000, 1200000, MAX77620_REG_LDO0_CFG, MAX77620_REG_LDO0_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO0, 3, 7, 0 }, + { REGULATOR_LDO, "ldo1", 0x00, 25000, 800000, 1050000, 1050000, MAX77620_REG_LDO1_CFG, MAX77620_REG_LDO1_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO1, 3, 7, 0 }, + { REGULATOR_LDO, "ldo2", 0x00, 50000, 800000, 1800000, 3300000, MAX77620_REG_LDO2_CFG, MAX77620_REG_LDO2_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO2, 3, 7, 0 }, + { REGULATOR_LDO, "ldo3", 0x00, 50000, 800000, 3100000, 3100000, MAX77620_REG_LDO3_CFG, MAX77620_REG_LDO3_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO3, 3, 7, 0 }, + { REGULATOR_LDO, "ldo4", 0x00, 12500, 800000, 850000, 850000, MAX77620_REG_LDO4_CFG, MAX77620_REG_LDO4_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO4, 0, 7, 1 }, + { REGULATOR_LDO, "ldo5", 0x00, 50000, 800000, 1800000, 1800000, MAX77620_REG_LDO5_CFG, MAX77620_REG_LDO5_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO5, 3, 7, 0 }, + { REGULATOR_LDO, "ldo6", 0x00, 50000, 800000, 2900000, 2900000, MAX77620_REG_LDO6_CFG, MAX77620_REG_LDO6_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO6, 3, 7, 0 }, + { REGULATOR_LDO, "ldo7", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO7_CFG, MAX77620_REG_LDO7_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO7, 1, 4, 3 }, + { REGULATOR_LDO, "ldo8", 0x00, 50000, 800000, 1050000, 1050000, MAX77620_REG_LDO8_CFG, MAX77620_REG_LDO8_CFG2, MAX77620_LDO_VOLT_MASK, MAX77620_LDO_POWER_MODE_MASK, MAX77620_LDO_POWER_MODE_SHIFT, 0x00, MAX77620_REG_FPS_LDO8, 3, 7, 0 } +}; + +int max77620_regulator_get_status(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (reg->type == REGULATOR_SD) + return (i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_STATSD) & reg->status_mask) ? 0 : 1; + return (i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, reg->cfg_addr) & 8) ? 1 : 0; +} + +int max77620_regulator_config_fps(u32 id) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, reg->fps_addr, + (reg->fps_src << MAX77620_FPS_SRC_SHIFT) | (reg->pu_period << MAX77620_FPS_PU_PERIOD_SHIFT) | (reg->pd_period)); + + return 1; +} + +int max77620_regulator_set_voltage(u32 id, u32 mv) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (mv < reg->mv_min || mv > reg->mv_max) + return 0; + + u32 mult = (mv + reg->mv_step - 1 - reg->mv_min) / reg->mv_step; + u8 val = i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, reg->volt_addr); + val = (val & ~reg->volt_mask) | (mult & reg->volt_mask); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, reg->volt_addr, val); + usleep(1000); + + return 1; +} + +int max77620_regulator_enable(u32 id, int enable) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + u32 addr = reg->type == REGULATOR_SD ? reg->cfg_addr : reg->volt_addr; + u8 val = i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, addr); + if (enable) + val = (val & ~reg->enable_mask) | ((MAX77620_POWER_MODE_NORMAL << reg->enable_shift) & reg->enable_mask); + else + val &= ~reg->enable_mask; + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, addr, val); + usleep(1000); + + return 1; +} + +int max77620_regulator_set_volt_and_flags(u32 id, u32 mv, u8 flags) +{ + if (id > REGULATOR_MAX) + return 0; + + const max77620_regulator_t *reg = &_pmic_regulators[id]; + + if (mv < reg->mv_min || mv > reg->mv_max) + return 0; + + u32 mult = (mv + reg->mv_step - 1 - reg->mv_min) / reg->mv_step; + u8 val = ((flags << reg->enable_shift) & ~reg->volt_mask) | (mult & reg->volt_mask); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, reg->volt_addr, val); + usleep(1000); + + return 1; +} + +void max77620_config_default() +{ + for (u32 i = 1; i <= REGULATOR_MAX; i++) + { + i2c_recv_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CID4); + max77620_regulator_config_fps(i); + max77620_regulator_set_voltage(i, _pmic_regulators[i].mv_default); + if (_pmic_regulators[i].fps_src != MAX77620_FPS_SRC_NONE) + max77620_regulator_enable(i, 1); + } + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD_CFG2, 4); +} + +void max77620_low_battery_monitor_config() +{ + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CNFGGLBL1, + MAX77620_CNFGGLBL1_LBDAC_EN | MAX77620_CNFGGLBL1_LBHYST_N | MAX77620_CNFGGLBL1_LBDAC_N); +} diff --git a/emummc/source/power/max7762x.h b/emummc/source/power/max7762x.h new file mode 100644 index 000000000..eefa11295 --- /dev/null +++ b/emummc/source/power/max7762x.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _MAX7762X_H_ +#define _MAX7762X_H_ + +#include "../utils/types.h" + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | +* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | +* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 | +*/ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +/*! MAX77620 partitions. */ +#define REGULATOR_SD0 0 +#define REGULATOR_SD1 1 +#define REGULATOR_SD2 2 +#define REGULATOR_SD3 3 +#define REGULATOR_LDO0 4 +#define REGULATOR_LDO1 5 +#define REGULATOR_LDO2 6 +#define REGULATOR_LDO3 7 +#define REGULATOR_LDO4 8 +#define REGULATOR_LDO5 9 +#define REGULATOR_LDO6 10 +#define REGULATOR_LDO7 11 +#define REGULATOR_LDO8 12 +#define REGULATOR_MAX 12 + +#define MAX77621_CPU_I2C_ADDR 0x1B +#define MAX77621_GPU_I2C_ADDR 0x1C + +#define MAX77621_VOUT_REG 0 +#define MAX77621_VOUT_DVC_REG 1 +#define MAX77621_CONTROL1_REG 2 +#define MAX77621_CONTROL2_REG 3 + +/* MAX77621_VOUT */ +#define MAX77621_VOUT_ENABLE (1 << 7) +#define MAX77621_VOUT_MASK 0x7F + +/* MAX77621_VOUT_DVC_DVS */ +#define MAX77621_DVS_VOUT_MASK 0x7F + +/* MAX77621_CONTROL1 */ +#define MAX77621_SNS_ENABLE (1 << 7) +#define MAX77621_FPWM_EN_M (1 << 6) +#define MAX77621_NFSR_ENABLE (1 << 5) +#define MAX77621_AD_ENABLE (1 << 4) +#define MAX77621_BIAS_ENABLE (1 << 3) +#define MAX77621_FREQSHIFT_9PER (1 << 2) + +#define MAX77621_RAMP_12mV_PER_US 0x0 +#define MAX77621_RAMP_25mV_PER_US 0x1 +#define MAX77621_RAMP_50mV_PER_US 0x2 +#define MAX77621_RAMP_200mV_PER_US 0x3 +#define MAX77621_RAMP_MASK 0x3 + +/* MAX77621_CONTROL2 */ +#define MAX77621_WDTMR_ENABLE (1 << 6) +#define MAX77621_DISCH_ENBABLE (1 << 5) +#define MAX77621_FT_ENABLE (1 << 4) +#define MAX77621_T_JUNCTION_120 (1 << 7) + +#define MAX77621_CKKADV_TRIP_DISABLE 0xC +#define MAX77621_CKKADV_TRIP_75mV_PER_US 0x0 +#define MAX77621_CKKADV_TRIP_150mV_PER_US 0x4 +#define MAX77621_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8 + +#define MAX77621_INDUCTOR_MIN_30_PER 0x0 +#define MAX77621_INDUCTOR_NOMINAL 0x1 +#define MAX77621_INDUCTOR_PLUS_30_PER 0x2 +#define MAX77621_INDUCTOR_PLUS_60_PER 0x3 + +int max77620_regulator_get_status(u32 id); +int max77620_regulator_config_fps(u32 id); +int max77620_regulator_set_voltage(u32 id, u32 mv); +int max77620_regulator_enable(u32 id, int enable); +int max77620_regulator_set_volt_and_flags(u32 id, u32 mv, u8 flags); +void max77620_config_default(); +void max77620_low_battery_monitor_config(); + +#endif diff --git a/emummc/source/soc/clock.c b/emummc/source/soc/clock.c new file mode 100644 index 000000000..ea1920865 --- /dev/null +++ b/emummc/source/soc/clock.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "../soc/clock.h" +#include "../soc/t210.h" +#include "../utils/util.h" +#include "../emmc/sdmmc.h" + +static const sclock_t _clock_i2c5 = { + CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, 0xF, 0, 4 //81.6MHz -> 400KHz +}; + +void clock_enable(const sclock_t *clk) +{ + // Put clock into reset. + CLOCK(clk->reset) = (CLOCK(clk->reset) & ~(1 << clk->index)) | (1 << clk->index); + // Disable. + CLOCK(clk->enable) &= ~(1 << clk->index); + // Configure clock source if required. + if (clk->source) + CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29); + // Enable. + CLOCK(clk->enable) = (CLOCK(clk->enable) & ~(1 << clk->index)) | (1 << clk->index); + // Take clock off reset. + CLOCK(clk->reset) &= ~(1 << clk->index); +} + +void clock_disable(const sclock_t *clk) +{ + // Put clock into reset. + CLOCK(clk->reset) = (CLOCK(clk->reset) & ~(1 << clk->index)) | (1 << clk->index); + // Disable. + CLOCK(clk->enable) &= ~(1 << clk->index); +} + +void clock_enable_i2c5() +{ + clock_enable(&_clock_i2c5); +} + +void clock_disable_i2c5() +{ + clock_disable(&_clock_i2c5); +} + +#define L_SWR_SDMMC1_RST (1 << 14) +#define L_SWR_SDMMC2_RST (1 << 9) +#define L_SWR_SDMMC4_RST (1 << 15) +#define U_SWR_SDMMC3_RST (1 << 5) + +#define L_CLK_ENB_SDMMC1 (1 << 14) +#define L_CLK_ENB_SDMMC2 (1 << 9) +#define L_CLK_ENB_SDMMC4 (1 << 15) +#define U_CLK_ENB_SDMMC3 (1 << 5) + +#define L_SET_SDMMC1_RST (1 << 14) +#define L_SET_SDMMC2_RST (1 << 9) +#define L_SET_SDMMC4_RST (1 << 15) +#define U_SET_SDMMC3_RST (1 << 5) + +#define L_CLR_SDMMC1_RST (1 << 14) +#define L_CLR_SDMMC2_RST (1 << 9) +#define L_CLR_SDMMC4_RST (1 << 15) +#define U_CLR_SDMMC3_RST (1 << 5) + +#define L_SET_CLK_ENB_SDMMC1 (1 << 14) +#define L_SET_CLK_ENB_SDMMC2 (1 << 9) +#define L_SET_CLK_ENB_SDMMC4 (1 << 15) +#define U_SET_CLK_ENB_SDMMC3 (1 << 5) + +#define L_CLR_CLK_ENB_SDMMC1 (1 << 14) +#define L_CLR_CLK_ENB_SDMMC2 (1 << 9) +#define L_CLR_CLK_ENB_SDMMC4 (1 << 15) +#define U_CLR_CLK_ENB_SDMMC3 (1 << 5) + +static int _clock_sdmmc_is_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC1_RST; + case SDMMC_2: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC2_RST; + case SDMMC_3: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_U) & U_SWR_SDMMC3_RST; + case SDMMC_4: + return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & L_SWR_SDMMC4_RST; + } + return 0; +} + +static void _clock_sdmmc_set_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC1_RST; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC2_RST; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_SET) = U_SET_SDMMC3_RST; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = L_SET_SDMMC4_RST; + break; + } +} + +static void _clock_sdmmc_clear_reset(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC1_RST; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC2_RST; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_CLR) = U_CLR_SDMMC3_RST; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = L_CLR_SDMMC4_RST; + break; + } +} + +static int _clock_sdmmc_is_enabled(u32 id) +{ + switch (id) + { + case SDMMC_1: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC1; + case SDMMC_2: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC2; + case SDMMC_3: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) & U_CLK_ENB_SDMMC3; + case SDMMC_4: + return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & L_CLK_ENB_SDMMC4; + } + return 0; +} + +static void _clock_sdmmc_set_enable(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC1; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC2; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_SET) = U_SET_CLK_ENB_SDMMC3; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = L_SET_CLK_ENB_SDMMC4; + break; + } +} + +static void _clock_sdmmc_clear_enable(u32 id) +{ + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC1; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC2; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_CLR) = U_CLR_CLK_ENB_SDMMC3; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = L_CLR_CLK_ENB_SDMMC4; + break; + } +} + +static u32 _clock_sdmmc_table[8] = { 0 }; + +#define PLLP_OUT0 0x0 + +static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val) +{ + u32 divisor = 0; + u32 source = PLLP_OUT0; + + switch (val) + { + case 25000: + *pout = 24728; + divisor = 31; + break; + case 26000: + *pout = 25500; + divisor = 30; + break; + case 40800: + *pout = 40800; + divisor = 18; + break; + case 50000: + *pout = 48000; + divisor = 15; + break; + case 52000: + *pout = 51000; + divisor = 14; + break; + case 100000: + *pout = 90667; + divisor = 7; + break; + case 200000: + *pout = 163200; + divisor = 3; + break; + case 208000: + *pout = 204000; + divisor = 2; + break; + default: + *pout = 24728; + divisor = 31; + } + + _clock_sdmmc_table[2 * id] = val; + _clock_sdmmc_table[2 * id + 1] = *pout; + + switch (id) + { + case SDMMC_1: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1) = (source << 29) | divisor; + break; + case SDMMC_2: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2) = (source << 29) | divisor; + break; + case SDMMC_3: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3) = (source << 29) | divisor; + break; + case SDMMC_4: + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4) = (source << 29) | divisor; + break; + } + + return 1; +} + +void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val) +{ + if (_clock_sdmmc_table[2 * id] == val) + { + *pout = _clock_sdmmc_table[2 * id + 1]; + } + else + { + int is_enabled = _clock_sdmmc_is_enabled(id); + if (is_enabled) + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_config_clock_source_inner(pout, id, val); + if (is_enabled) + _clock_sdmmc_set_enable(id); + _clock_sdmmc_is_reset(id); + } +} + +void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type) +{ + switch (type) + { + case 0: + *pout = 26000; + *pdivisor = 66; + break; + case 1: + *pout = 26000; + *pdivisor = 1; + break; + case 2: + *pout = 52000; + *pdivisor = 1; + break; + case 3: + case 4: + case 11: + *pout = 200000; + *pdivisor = 1; + break; + case 5: + *pout = 25000; + *pdivisor = 64; + break; + case 6: + case 8: + *pout = 25000; + *pdivisor = 1; + break; + case 7: + *pout = 50000; + *pdivisor = 1; + break; + case 10: + *pout = 100000; + *pdivisor = 1; + break; + case 13: + *pout = 40800; + *pdivisor = 1; + break; + case 14: + *pout = 200000; + *pdivisor = 2; + break; + } +} + +int clock_sdmmc_is_not_reset_and_enabled(u32 id) +{ + return !_clock_sdmmc_is_reset(id) && _clock_sdmmc_is_enabled(id); +} + +void clock_sdmmc_enable(u32 id, u32 val) +{ + u32 div = 0; + + if (_clock_sdmmc_is_enabled(id)) + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_set_reset(id); + _clock_sdmmc_config_clock_source_inner(&div, id, val); + _clock_sdmmc_set_enable(id); + _clock_sdmmc_is_reset(id); + usleep((100000 + div - 1) / div); + _clock_sdmmc_clear_reset(id); + _clock_sdmmc_is_reset(id); +} + +void clock_sdmmc_disable(u32 id) +{ + _clock_sdmmc_set_reset(id); + _clock_sdmmc_clear_enable(id); + _clock_sdmmc_is_reset(id); +} diff --git a/emummc/source/soc/clock.h b/emummc/source/soc/clock.h new file mode 100644 index 000000000..bad138bbe --- /dev/null +++ b/emummc/source/soc/clock.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _CLOCK_H_ +#define _CLOCK_H_ + +#include "../utils/types.h" + +/*! Clock registers. */ +#define CLK_RST_CONTROLLER_RST_SOURCE 0x0 +#define CLK_RST_CONTROLLER_RST_DEVICES_L 0x4 +#define CLK_RST_CONTROLLER_RST_DEVICES_H 0x8 +#define CLK_RST_CONTROLLER_RST_DEVICES_U 0xC +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L 0x10 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_H 0x14 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U 0x18 +#define CLK_RST_CONTROLLER_CCLK_BURST_POLICY 0x20 +#define CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER 0x24 +#define CLK_RST_CONTROLLER_SCLK_BURST_POLICY 0x28 +#define CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER 0x2C +#define CLK_RST_CONTROLLER_CLK_SYSTEM_RATE 0x30 +#define CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48 +#define CLK_RST_CONTROLLER_OSC_CTRL 0x50 +#define CLK_RST_CONTROLLER_PLLC_BASE 0x80 +#define CLK_RST_CONTROLLER_PLLC_MISC 0x88 +#define CLK_RST_CONTROLLER_PLLM_BASE 0x90 +#define CLK_RST_CONTROLLER_PLLM_MISC1 0x98 +#define CLK_RST_CONTROLLER_PLLM_MISC2 0x9C +#define CLK_RST_CONTROLLER_PLLP_BASE 0xA0 +#define CLK_RST_CONTROLLER_PLLD_BASE 0xD0 +#define CLK_RST_CONTROLLER_PLLX_BASE 0xE0 +#define CLK_RST_CONTROLLER_PLLX_MISC 0xE4 +#define CLK_RST_CONTROLLER_PLLE_BASE 0xE8 +#define CLK_RST_CONTROLLER_PLLE_MISC 0xEC +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA 0xF8 +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB 0xFC +#define CLK_RST_CONTROLLER_CLK_SOURCE_PWM 0x110 +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 0x124 +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 0x128 +#define CLK_RST_CONTROLLER_CLK_SOURCE_VI 0x148 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 0x150 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 0x154 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 0x164 +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA 0x178 +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB 0x17C +#define CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X 0x180 +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC 0x1A0 +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 0x1B8 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 0x1BC +#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE 0x1D4 +#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C +#define CLK_RST_CONTROLLER_CLK_SOURCE_TSEC 0x1F4 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280 +#define CLK_RST_CONTROLLER_CLK_ENB_X_SET 0x284 +#define CLK_RST_CONTROLLER_CLK_ENB_X_CLR 0x288 +#define CLK_RST_CONTROLLER_RST_DEVICES_X 0x28C +#define CLK_RST_CONTROLLER_RST_DEV_X_SET 0x290 +#define CLK_RST_CONTROLLER_RST_DEV_X_CLR 0x294 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y 0x298 +#define CLK_RST_CONTROLLER_CLK_ENB_Y_SET 0x29C +#define CLK_RST_CONTROLLER_CLK_ENB_Y_CLR 0x2A0 +#define CLK_RST_CONTROLLER_RST_DEVICES_Y 0x2A4 +#define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2A8 +#define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2AC +#define CLK_RST_CONTROLLER_RST_DEV_L_SET 0x300 +#define CLK_RST_CONTROLLER_RST_DEV_L_CLR 0x304 +#define CLK_RST_CONTROLLER_RST_DEV_H_SET 0x308 +#define CLK_RST_CONTROLLER_RST_DEV_H_CLR 0x30C +#define CLK_RST_CONTROLLER_RST_DEV_U_SET 0x310 +#define CLK_RST_CONTROLLER_RST_DEV_U_CLR 0x314 +#define CLK_RST_CONTROLLER_CLK_ENB_L_SET 0x320 +#define CLK_RST_CONTROLLER_CLK_ENB_L_CLR 0x324 +#define CLK_RST_CONTROLLER_CLK_ENB_H_SET 0x328 +#define CLK_RST_CONTROLLER_CLK_ENB_H_CLR 0x32C +#define CLK_RST_CONTROLLER_CLK_ENB_U_SET 0x330 +#define CLK_RST_CONTROLLER_CLK_ENB_U_CLR 0x334 +#define CLK_RST_CONTROLLER_RST_DEVICES_V 0x358 +#define CLK_RST_CONTROLLER_RST_DEVICES_W 0x35C +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_V 0x360 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W 0x364 +#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 0x388 +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC 0x3A0 +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD 0x3A4 +#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT 0x3B4 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 0x410 +#define CLK_RST_CONTROLLER_CLK_SOURCE_SE 0x42C +#define CLK_RST_CONTROLLER_CLK_ENB_V_SET 0x440 +#define CLK_RST_CONTROLLER_CLK_ENB_W_SET 0x448 +#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR 0x44C +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET 0x450 +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR 0x454 +#define CLK_RST_CONTROLLER_UTMIP_PLL_CFG2 0x488 +#define CLK_RST_CONTROLLER_PLLE_AUX 0x48C +#define CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S0 0x4A0 +#define CLK_RST_CONTROLLER_PLLX_MISC_3 0x518 +#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE 0x554 +#define CLK_RST_CONTROLLER_SPARE_REG0 0x55C +#define CLK_RST_CONTROLLER_PLLMB_BASE 0x5E8 +#define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP 0x620 +#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL 0x664 +#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIP_CAL 0x66C +#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM 0x694 +#define CLK_RST_CONTROLLER_CLK_SOURCE_NVENC 0x6A0 +#define CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER 0x704 + +#define CLK_NO_SOURCE 0x0 + +/*! Generic clock descriptor. */ +typedef struct _sclock_t +{ + u32 reset; + u32 enable; + u32 source; + u8 index; + u8 clk_src; + u8 clk_div; +} sclock_t; + +/*! Generic clock enable/disable. */ +void clock_enable(const sclock_t *clk); +void clock_disable(const sclock_t *clk); + +/*! Clock control for specific hardware portions. */ +void clock_enable_i2c5(); +void clock_disable_i2c5(); +void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val); +void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type); +int clock_sdmmc_is_not_reset_and_enabled(u32 id); +void clock_sdmmc_enable(u32 id, u32 val); +void clock_sdmmc_disable(u32 id); + +#endif diff --git a/emummc/source/soc/gpio.c b/emummc/source/soc/gpio.c new file mode 100644 index 000000000..053c43a07 --- /dev/null +++ b/emummc/source/soc/gpio.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "../soc/gpio.h" +#include "../soc/t210.h" + +static const u16 _gpio_cnf[31] = { + 0x000, 0x004, 0x008, 0x00C, + 0x100, 0x104, 0x108, 0x10C, + 0x200, 0x204, 0x208, 0x20C, + 0x300, 0x304, 0x308, 0x30C, + 0x400, 0x404, 0x408, 0x40C, + 0x500, 0x504, 0x508, 0x50C, + 0x600, 0x604, 0x608, 0x60C, + 0x700, 0x704, 0x708 +}; + +static const u16 _gpio_oe[31] = { + 0x010, 0x014, 0x018, 0x01C, + 0x110, 0x114, 0x118, 0x11C, + 0x210, 0x214, 0x218, 0x21C, + 0x310, 0x314, 0x318, 0x31C, + 0x410, 0x414, 0x418, 0x41C, + 0x510, 0x514, 0x518, 0x51C, + 0x610, 0x614, 0x618, 0x61C, + 0x710, 0x714, 0x718 +}; + +static const u16 _gpio_out[31] = { + 0x020, 0x024, 0x028, 0x02C, + 0x120, 0x124, 0x128, 0x12C, + 0x220, 0x224, 0x228, 0x22C, + 0x320, 0x324, 0x328, 0x32C, + 0x420, 0x424, 0x428, 0x42C, + 0x520, 0x524, 0x528, 0x52C, + 0x620, 0x624, 0x628, 0x62C, + 0x720, 0x724, 0x728 +}; + +static const u16 _gpio_in[31] = { + 0x030, 0x034, 0x038, 0x03C, + 0x130, 0x134, 0x138, 0x13C, + 0x230, 0x234, 0x238, 0x23C, + 0x330, 0x334, 0x338, 0x33C, + 0x430, 0x434, 0x438, 0x43C, + 0x530, 0x534, 0x538, 0x53C, + 0x630, 0x634, 0x638, 0x63C, + 0x730, 0x734, 0x738 +}; + +void gpio_config(u32 port, u32 pins, int mode) +{ + if (mode) + GPIO(_gpio_cnf[port]) |= pins; + else + GPIO(_gpio_cnf[port]) &= ~pins; + (void)GPIO(_gpio_cnf[port]); +} + +void gpio_output_enable(u32 port, u32 pins, int enable) +{ + if (enable) + GPIO(_gpio_oe[port]) |= pins; + else + GPIO(_gpio_oe[port]) &= ~pins; + (void)GPIO(_gpio_oe[port]); +} + +void gpio_write(u32 port, u32 pins, int high) +{ + if (high) + GPIO(_gpio_out[port]) |= pins; + else + GPIO(_gpio_out[port]) &= ~pins; + (void)GPIO(_gpio_out[port]); +} + +int gpio_read(u32 port, u32 pins) +{ + return (GPIO(_gpio_in[port]) & pins) ? 1 : 0; +} diff --git a/emummc/source/soc/gpio.h b/emummc/source/soc/gpio.h new file mode 100644 index 000000000..83170b0c3 --- /dev/null +++ b/emummc/source/soc/gpio.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include "../utils/types.h" + +#define GPIO_MODE_SPIO 0 +#define GPIO_MODE_GPIO 1 +#define GPIO_OUTPUT_DISABLE 0 +#define GPIO_OUTPUT_ENABLE 1 +#define GPIO_LOW 0 +#define GPIO_HIGH 1 + +/*! GPIO pins (0-7 for each port). */ +#define GPIO_PIN_0 (1 << 0) +#define GPIO_PIN_1 (1 << 1) +#define GPIO_PIN_2 (1 << 2) +#define GPIO_PIN_3 (1 << 3) +#define GPIO_PIN_4 (1 << 4) +#define GPIO_PIN_5 (1 << 5) +#define GPIO_PIN_6 (1 << 6) +#define GPIO_PIN_7 (1 << 7) + +/*! GPIO ports (A-EE). */ +#define GPIO_PORT_A 0 +#define GPIO_PORT_B 1 +#define GPIO_PORT_C 2 +#define GPIO_PORT_D 3 +#define GPIO_PORT_E 4 +#define GPIO_PORT_F 5 +#define GPIO_PORT_G 6 +#define GPIO_PORT_H 7 +#define GPIO_PORT_I 8 +#define GPIO_PORT_J 9 +#define GPIO_PORT_K 10 +#define GPIO_PORT_L 11 +#define GPIO_PORT_M 12 +#define GPIO_PORT_N 13 +#define GPIO_PORT_O 14 +#define GPIO_PORT_P 15 +#define GPIO_PORT_Q 16 +#define GPIO_PORT_R 17 +#define GPIO_PORT_S 18 +#define GPIO_PORT_T 19 +#define GPIO_PORT_U 20 +#define GPIO_PORT_V 21 +#define GPIO_PORT_W 22 +#define GPIO_PORT_X 23 +#define GPIO_PORT_Y 24 +#define GPIO_PORT_Z 25 +#define GPIO_PORT_AA 26 +#define GPIO_PORT_BB 27 +#define GPIO_PORT_CC 28 +#define GPIO_PORT_DD 29 +#define GPIO_PORT_EE 30 + +void gpio_config(u32 port, u32 pins, int mode); +void gpio_output_enable(u32 port, u32 pins, int enable); +void gpio_write(u32 port, u32 pins, int high); +int gpio_read(u32 port, u32 pins); + +#endif diff --git a/emummc/source/soc/i2c.c b/emummc/source/soc/i2c.c new file mode 100644 index 000000000..d8212d3d7 --- /dev/null +++ b/emummc/source/soc/i2c.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include + +#include "i2c.h" +#include "../utils/util.h" +#include "t210.h" + +static u32 i2c_addrs[] = { + 0x7000C000, 0x7000C400, 0x7000C500, + 0x7000C700, 0x7000D000, 0x7000D100 +}; + +static void _i2c_wait(vu32 *base) +{ + base[I2C_CONFIG_LOAD] = 0x25; + for (u32 i = 0; i < 20; i++) + { + usleep(1); + if (!(base[I2C_CONFIG_LOAD] & 1)) + break; + } +} + +static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size) +{ + if (size > 4) + return 0; + + u32 tmp = 0; + memcpy(&tmp, buf, size); + + vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[0], 0x2000); + base = base + (i2c_addrs[idx] - i2c_addrs[0]); + base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode). + base[I2C_CMD_DATA1] = tmp; //Set value. + base[I2C_CNFG] = (2 * size - 2) | 0x2800; //Set size and send mode. + _i2c_wait(base); //Kick transaction. + + base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; + while (base[I2C_STATUS] & 0x100) + ; + + if (base[I2C_STATUS] << 28) + return 0; + + return 1; +} + +static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x) +{ + if (size > 8) + return 0; + + vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[0], 0x2000); + base = base + (i2c_addrs[idx] - i2c_addrs[0]); + + base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode). + base[I2C_CNFG] = (size - 1) << 1 | 0x2840; // Set size and recv mode. + _i2c_wait(base); // Kick transaction. + + base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; + while (base[I2C_STATUS] & 0x100) + ; + + if (base[I2C_STATUS] << 28) + return 0; + + u32 tmp = base[I2C_CMD_DATA1]; // Get LS value. + if (size > 4) + { + memcpy(buf, &tmp, 4); + tmp = base[I2C_CMD_DATA2]; // Get MS value. + memcpy(buf + 4, &tmp, size - 4); + } + else + memcpy(buf, &tmp, size); + + return 1; +} + +void i2c_init(u32 idx) +{ + vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[0], 0x2000); + base = base + (i2c_addrs[idx] - i2c_addrs[0]); + + base[I2C_CLK_DIVISOR_REGISTER] = 0x50001; + base[I2C_BUS_CLEAR_CONFIG] = 0x90003; + _i2c_wait(base); + + for (u32 i = 0; i < 10; i++) + { + usleep(20000); + if (base[INTERRUPT_STATUS_REGISTER] & 0x800) + break; + } + + (vu32)base[I2C_BUS_CLEAR_STATUS]; + base[INTERRUPT_STATUS_REGISTER] = base[INTERRUPT_STATUS_REGISTER]; +} + +int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size) +{ + u8 tmp[4]; + + if (size > 3) + return 0; + + tmp[0] = y; + memcpy(tmp + 1, buf, size); + + return _i2c_send_pkt(idx, x, tmp, size + 1); +} + +int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y) +{ + int res = _i2c_send_pkt(idx, x, (u8 *)&y, 1); + if (res) + res = _i2c_recv_pkt(idx, buf, size, x); + return res; +} + +int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b) +{ + return i2c_send_buf_small(idx, x, y, &b, 1); +} + +u8 i2c_recv_byte(u32 idx, u32 x, u32 y) +{ + u8 tmp = 0; + i2c_recv_buf_small(&tmp, 1, idx, x, y); + return tmp; +} + diff --git a/emummc/source/soc/i2c.h b/emummc/source/soc/i2c.h new file mode 100644 index 000000000..e2b2af50c --- /dev/null +++ b/emummc/source/soc/i2c.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _I2C_H_ +#define _I2C_H_ + +#include "../utils/types.h" + +#define I2C_1 0 +#define I2C_2 1 +#define I2C_3 2 +#define I2C_4 3 +#define I2C_5 4 +#define I2C_6 5 + +#define I2C_CNFG 0x00 +#define I2C_CMD_ADDR0 0x01 +#define I2C_CMD_DATA1 0x03 +#define I2C_CMD_DATA2 0x04 +#define I2C_STATUS 0x07 +#define INTERRUPT_STATUS_REGISTER 0x1A +#define I2C_CLK_DIVISOR_REGISTER 0x1B +#define I2C_BUS_CLEAR_CONFIG 0x21 +#define I2C_BUS_CLEAR_STATUS 0x22 +#define I2C_CONFIG_LOAD 0x23 + +void i2c_init(u32 idx); +int i2c_send_buf_small(u32 idx, u32 x, u32 y, u8 *buf, u32 size); +int i2c_recv_buf_small(u8 *buf, u32 size, u32 idx, u32 x, u32 y); +int i2c_send_byte(u32 idx, u32 x, u32 y, u8 b); +u8 i2c_recv_byte(u32 idx, u32 x, u32 y); + +#endif diff --git a/emummc/source/soc/pinmux.c b/emummc/source/soc/pinmux.c new file mode 100644 index 000000000..5966be205 --- /dev/null +++ b/emummc/source/soc/pinmux.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "../soc/pinmux.h" +#include "../soc/t210.h" + +void pinmux_config_i2c(u32 idx) +{ + PINMUX_AUX(PINMUX_AUX_X_I2C_SCL(idx)) = PINMUX_INPUT_ENABLE; + PINMUX_AUX(PINMUX_AUX_X_I2C_SDA(idx)) = PINMUX_INPUT_ENABLE; +} diff --git a/emummc/source/soc/pinmux.h b/emummc/source/soc/pinmux.h new file mode 100644 index 000000000..c44b97c34 --- /dev/null +++ b/emummc/source/soc/pinmux.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2018 naehrwert + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _PINMUX_H_ +#define _PINMUX_H_ + +#include "../utils/types.h" + +/*! APB MISC registers. */ +#define APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL 0x8D4 +#define APB_MISC_GP_SDMMC3_CLK_LPBK_CONTROL 0x8D8 +#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98 +#define APB_MISC_GP_VGPIO_GPIO_MUX_SEL 0xB74 + +/*! Pinmux registers. */ +#define PINMUX_AUX_SDMMC1_CLK 0x00 +#define PINMUX_AUX_SDMMC1_CMD 0x04 +#define PINMUX_AUX_SDMMC1_DAT3 0x08 +#define PINMUX_AUX_SDMMC1_DAT2 0x0C +#define PINMUX_AUX_SDMMC1_DAT1 0x10 +#define PINMUX_AUX_SDMMC1_DAT0 0x14 +#define PINMUX_AUX_SDMMC3_CLK 0x1C +#define PINMUX_AUX_SDMMC3_CMD 0x20 +#define PINMUX_AUX_SDMMC3_DAT0 0x24 +#define PINMUX_AUX_SDMMC3_DAT1 0x28 +#define PINMUX_AUX_SDMMC3_DAT2 0x2C +#define PINMUX_AUX_SDMMC3_DAT3 0x30 +#define PINMUX_AUX_DMIC3_CLK 0xB4 +#define PINMUX_AUX_UART2_TX 0xF4 +#define PINMUX_AUX_UART3_TX 0x104 +#define PINMUX_AUX_WIFI_EN 0x1B4 +#define PINMUX_AUX_WIFI_RST 0x1B8 +#define PINMUX_AUX_NFC_EN 0x1D0 +#define PINMUX_AUX_NFC_INT 0x1D4 +#define PINMUX_AUX_LCD_BL_PWM 0x1FC +#define PINMUX_AUX_LCD_BL_EN 0x200 +#define PINMUX_AUX_LCD_RST 0x204 +#define PINMUX_AUX_GPIO_PE6 0x248 +#define PINMUX_AUX_GPIO_PH6 0x250 +#define PINMUX_AUX_GPIO_PZ1 0x280 +/*! 0:UART-A, 1:UART-B, 3:UART-C, 3:UART-D */ +#define PINMUX_AUX_UARTX_TX(x) (0xE4 + 0x10 * (x)) +#define PINMUX_AUX_UARTX_RX(x) (0xE8 + 0x10 * (x)) +#define PINMUX_AUX_UARTX_RTS(x) (0xEC + 0x10 * (x)) +#define PINMUX_AUX_UARTX_CTS(x) (0xF0 + 0x10 * (x)) +/*! 0:GEN1, 1:GEN2, 2:GEN3, 3:CAM, 4:PWR */ +#define PINMUX_AUX_X_I2C_SCL(x) (0xBC + 8 * (x)) +#define PINMUX_AUX_X_I2C_SDA(x) (0xC0 + 8 * (x)) + +#define PINMUX_FUNC_MASK (3 << 0) + +#define PINMUX_PULL_MASK (3 << 2) +#define PINMUX_PULL_NONE (0 << 2) +#define PINMUX_PULL_DOWN (1 << 2) +#define PINMUX_PULL_UP (2 << 2) + +#define PINMUX_TRISTATE (1 << 4) +#define PINMUX_PARKED (1 << 5) +#define PINMUX_INPUT_ENABLE (1 << 6) +#define PINMUX_LOCK (1 << 7) +#define PINMUX_LPDR (1 << 8) +#define PINMUX_HSM (1 << 9) + +#define PINMUX_IO_HV (1 << 10) +#define PINMUX_OPEN_DRAIN (1 << 11) +#define PINMUX_SCHMT (1 << 12) + +#define PINMUX_DRIVE_1X (0 << 13) +#define PINMUX_DRIVE_2X (1 << 13) +#define PINMUX_DRIVE_3X (2 << 13) +#define PINMUX_DRIVE_4X (3 << 13) + +void pinmux_config_i2c(u32 idx); + +#endif diff --git a/emummc/source/soc/pmc.h b/emummc/source/soc/pmc.h new file mode 100644 index 000000000..ec8eb99a8 --- /dev/null +++ b/emummc/source/soc/pmc.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 st4rk + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _PMC_H_ +#define _PMC_H_ + +/*! PMC registers. */ +#define APBDEV_PMC_CNTRL 0x0 +#define PMC_CNTRL_MAIN_RST (1 << 4) +#define APBDEV_PMC_SEC_DISABLE 0x4 +#define APBDEV_PMC_PWRGATE_TOGGLE 0x30 +#define APBDEV_PMC_PWRGATE_STATUS 0x38 +#define APBDEV_PMC_NO_IOPOWER 0x44 +#define APBDEV_PMC_SCRATCH0 0x50 +#define APBDEV_PMC_SCRATCH1 0x54 +#define APBDEV_PMC_SCRATCH20 0xA0 +#define APBDEV_PMC_PWR_DET_VAL 0xE4 +#define PMC_PWR_DET_SDMMC1_IO_EN (1 << 12) +#define APBDEV_PMC_DDR_PWR 0xE8 +#define APBDEV_PMC_CRYPTO_OP 0xF4 +#define PMC_CRYPTO_OP_SE_ENABLE 0 +#define PMC_CRYPTO_OP_SE_DISABLE 1 +#define APBDEV_PMC_SCRATCH33 0x120 +#define APBDEV_PMC_SCRATCH40 0x13C +#define APBDEV_PMC_OSC_EDPD_OVER 0x1A4 +#define APBDEV_PMC_RST_STATUS 0x1B4 +#define APBDEV_PMC_IO_DPD_REQ 0x1B8 +#define APBDEV_PMC_IO_DPD2_REQ 0x1C0 +#define APBDEV_PMC_VDDP_SEL 0x1CC +#define APBDEV_PMC_DDR_CFG 0x1D0 +#define APBDEV_PMC_SCRATCH45 0x234 +#define APBDEV_PMC_SCRATCH46 0x238 +#define APBDEV_PMC_SCRATCH49 0x244 +#define APBDEV_PMC_TSC_MULT 0x2B4 +#define APBDEV_PMC_SEC_DISABLE2 0x2C4 +#define APBDEV_PMC_WEAK_BIAS 0x2C8 +#define APBDEV_PMC_REG_SHORT 0x2CC +#define APBDEV_PMC_SEC_DISABLE3 0x2D8 +#define APBDEV_PMC_SECURE_SCRATCH21 0x334 +#define APBDEV_PMC_SECURE_SCRATCH32 0x360 +#define APBDEV_PMC_SECURE_SCRATCH49 0x3A4 +#define APBDEV_PMC_CNTRL2 0x440 +#define APBDEV_PMC_IO_DPD3_REQ 0x45C +#define APBDEV_PMC_IO_DPD4_REQ 0x464 +#define APBDEV_PMC_UTMIP_PAD_CFG1 0x4C4 +#define APBDEV_PMC_UTMIP_PAD_CFG3 0x4CC +#define APBDEV_PMC_DDR_CNTRL 0x4E4 +#define APBDEV_PMC_SEC_DISABLE4 0x5B0 +#define APBDEV_PMC_SEC_DISABLE5 0x5B4 +#define APBDEV_PMC_SEC_DISABLE6 0x5B8 +#define APBDEV_PMC_SEC_DISABLE7 0x5BC +#define APBDEV_PMC_SEC_DISABLE8 0x5C0 +#define APBDEV_PMC_SCRATCH188 0x810 +#define APBDEV_PMC_SCRATCH190 0x818 +#define APBDEV_PMC_SCRATCH200 0x840 + +#endif diff --git a/emummc/source/soc/pmc_lp0_t210.h b/emummc/source/soc/pmc_lp0_t210.h new file mode 100644 index 000000000..641d9f735 --- /dev/null +++ b/emummc/source/soc/pmc_lp0_t210.h @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef _TEGRA210_PMC_H_ +#define _TEGRA210_PMC_H_ + +#include "../utils/types.h" + +struct tegra_pmc_regs +{ + u32 cntrl; + u32 sec_disable; + u32 pmc_swrst; + u32 wake_mask; + u32 wake_lvl; + u32 wake_status; + u32 sw_wake_status; + u32 dpd_pads_oride; + u32 dpd_sample; + u32 dpd_enable; + u32 pwrgate_timer_off; + u32 clamp_status; + u32 pwrgate_toggle; + u32 remove_clamping_cmd; + u32 pwrgate_status; + u32 pwrgood_timer; + u32 blink_timer; + u32 no_iopower; + u32 pwr_det; + u32 pwr_det_latch; + u32 scratch0; + u32 scratch1; + u32 scratch2; + u32 scratch3; + u32 scratch4; + u32 scratch5; + u32 scratch6; + u32 scratch7; + u32 scratch8; + u32 scratch9; + u32 scratch10; + u32 scratch11; + u32 scratch12; + u32 scratch13; + u32 scratch14; + u32 scratch15; + u32 scratch16; + u32 scratch17; + u32 scratch18; + u32 scratch19; + u32 odmdata; + u32 scratch21; + u32 scratch22; + u32 scratch23; + u32 secure_scratch0; + u32 secure_scratch1; + u32 secure_scratch2; + u32 secure_scratch3; + u32 secure_scratch4; + u32 secure_scratch5; + u32 cpupwrgood_timer; + u32 cpupwroff_timer; + u32 pg_mask; + u32 pg_mask_1; + u32 auto_wake_lvl; + u32 auto_wake_lvl_mask; + u32 wake_delay; + u32 pwr_det_val; + u32 ddr_pwr; + u32 usb_debounce_del; + u32 usb_a0; + u32 crypto_op; + u32 pllp_wb0_override; + u32 scratch24; + u32 scratch25; + u32 scratch26; + u32 scratch27; + u32 scratch28; + u32 scratch29; + u32 scratch30; + u32 scratch31; + u32 scratch32; + u32 scratch33; + u32 scratch34; + u32 scratch35; + u32 scratch36; + u32 scratch37; + u32 scratch38; + u32 scratch39; + u32 scratch40; + u32 scratch41; + u32 scratch42; + u32 bondout_mirror[3]; + u32 sys_33v_en; + u32 bondout_mirror_access; + u32 gate; + u32 wake2_mask; + u32 wake2_lvl; + u32 wake2_status; + u32 sw_wake2_status; + u32 auto_wake2_lvl_mask; + u32 pg_mask_2; + u32 pg_mask_ce1; + u32 pg_mask_ce2; + u32 pg_mask_ce3; + u32 pwrgate_timer_ce[7]; + u32 pcx_edpd_cntrl; + u32 osc_edpd_over; + u32 clk_out_cntrl; + u32 sata_pwrgt; + u32 sensor_ctrl; + u32 rst_status; + u32 io_dpd_req; + u32 io_dpd_status; + u32 io_dpd2_req; + u32 io_dpd2_status; + u32 sel_dpd_tim; + u32 vddp_sel; + u32 ddr_cfg; + u32 e_no_vttgen; + u8 _rsv0[4]; + u32 pllm_wb0_override_freq; + u32 test_pwrgate; + u32 pwrgate_timer_mult; + u32 dis_sel_dpd; + u32 utmip_uhsic_triggers; + u32 utmip_uhsic_saved_state; + u32 utmip_pad_cfg; + u32 utmip_term_pad_cfg; + u32 utmip_uhsic_sleep_cfg; + u32 utmip_uhsic_sleepwalk_cfg; + u32 utmip_sleepwalk_p[3]; + u32 uhsic_sleepwalk_p0; + u32 utmip_uhsic_status; + u32 utmip_uhsic_fake; + u32 bondout_mirror3[5 - 3]; + u32 secure_scratch6; + u32 secure_scratch7; + u32 scratch43; + u32 scratch44; + u32 scratch45; + u32 scratch46; + u32 scratch47; + u32 scratch48; + u32 scratch49; + u32 scratch50; + u32 scratch51; + u32 scratch52; + u32 scratch53; + u32 scratch54; + u32 scratch55; + u32 scratch0_eco; + u32 por_dpd_ctrl; + u32 scratch2_eco; + u32 utmip_uhsic_line_wakeup; + u32 utmip_bias_master_cntrl; + u32 utmip_master_config; + u32 td_pwrgate_inter_part_timer; + u32 utmip_uhsic2_triggers; + u32 utmip_uhsic2_saved_state; + u32 utmip_uhsic2_sleep_cfg; + u32 utmip_uhsic2_sleepwalk_cfg; + u32 uhsic2_sleepwalk_p1; + u32 utmip_uhsic2_status; + u32 utmip_uhsic2_fake; + u32 utmip_uhsic2_line_wakeup; + u32 utmip_master2_config; + u32 utmip_uhsic_rpd_cfg; + u32 pg_mask_ce0; + u32 pg_mask3[5 - 3]; + u32 pllm_wb0_override2; + u32 tsc_mult; + u32 cpu_vsense_override; + u32 glb_amap_cfg; + u32 sticky_bits; + u32 sec_disable2; + u32 weak_bias; + u32 reg_short; + u32 pg_mask_andor; + u8 _rsv1[0x2c]; + u32 secure_scratch8; /* offset 0x300 */ + u32 secure_scratch9; + u32 secure_scratch10; + u32 secure_scratch11; + u32 secure_scratch12; + u32 secure_scratch13; + u32 secure_scratch14; + u32 secure_scratch15; + u32 secure_scratch16; + u32 secure_scratch17; + u32 secure_scratch18; + u32 secure_scratch19; + u32 secure_scratch20; + u32 secure_scratch21; + u32 secure_scratch22; + u32 secure_scratch23; + u32 secure_scratch24; + u32 secure_scratch25; + u32 secure_scratch26; + u32 secure_scratch27; + u32 secure_scratch28; + u32 secure_scratch29; + u32 secure_scratch30; + u32 secure_scratch31; + u32 secure_scratch32; + u32 secure_scratch33; + u32 secure_scratch34; + u32 secure_scratch35; + u32 secure_scratch36; + u32 secure_scratch37; + u32 secure_scratch38; + u32 secure_scratch39; + u32 secure_scratch40; + u32 secure_scratch41; + u32 secure_scratch42; + u32 secure_scratch43; + u32 secure_scratch44; + u32 secure_scratch45; + u32 secure_scratch46; + u32 secure_scratch47; + u32 secure_scratch48; + u32 secure_scratch49; + u32 secure_scratch50; + u32 secure_scratch51; + u32 secure_scratch52; + u32 secure_scratch53; + u32 secure_scratch54; + u32 secure_scratch55; + u32 secure_scratch56; + u32 secure_scratch57; + u32 secure_scratch58; + u32 secure_scratch59; + u32 secure_scratch60; + u32 secure_scratch61; + u32 secure_scratch62; + u32 secure_scratch63; + u32 secure_scratch64; + u32 secure_scratch65; + u32 secure_scratch66; + u32 secure_scratch67; + u32 secure_scratch68; + u32 secure_scratch69; + u32 secure_scratch70; + u32 secure_scratch71; + u32 secure_scratch72; + u32 secure_scratch73; + u32 secure_scratch74; + u32 secure_scratch75; + u32 secure_scratch76; + u32 secure_scratch77; + u32 secure_scratch78; + u32 secure_scratch79; + u32 _rsv0x420[8]; + u32 cntrl2; /* 0x440 */ + u32 _rsv0x444[2]; + u32 event_counter; /* 0x44C */ + u32 fuse_control; + u32 scratch1_eco; + u32 _rsv0x458[1]; + u32 io_dpd3_req; /* 0x45C */ + u32 io_dpd3_status; + u32 io_dpd4_req; + u32 io_dpd4_status; + u32 _rsv0x46C[30]; + u32 ddr_cntrl; /* 0x4E4 */ + u32 _rsv0x4E8[70]; + u32 scratch56; /* 0x600 */ + u32 scratch57; + u32 scratch58; + u32 scratch59; + u32 scratch60; + u32 scratch61; + u32 scratch62; + u32 scratch63; + u32 scratch64; + u32 scratch65; + u32 scratch66; + u32 scratch67; + u32 scratch68; + u32 scratch69; + u32 scratch70; + u32 scratch71; + u32 scratch72; + u32 scratch73; + u32 scratch74; + u32 scratch75; + u32 scratch76; + u32 scratch77; + u32 scratch78; + u32 scratch79; + u32 scratch80; + u32 scratch81; + u32 scratch82; + u32 scratch83; + u32 scratch84; + u32 scratch85; + u32 scratch86; + u32 scratch87; + u32 scratch88; + u32 scratch89; + u32 scratch90; + u32 scratch91; + u32 scratch92; + u32 scratch93; + u32 scratch94; + u32 scratch95; + u32 scratch96; + u32 scratch97; + u32 scratch98; + u32 scratch99; + u32 scratch100; + u32 scratch101; + u32 scratch102; + u32 scratch103; + u32 scratch104; + u32 scratch105; + u32 scratch106; + u32 scratch107; + u32 scratch108; + u32 scratch109; + u32 scratch110; + u32 scratch111; + u32 scratch112; + u32 scratch113; + u32 scratch114; + u32 scratch115; + u32 scratch116; + u32 scratch117; + u32 scratch118; + u32 scratch119; + u32 scratch120; /* 0x700 */ + u32 scratch121; + u32 scratch122; + u32 scratch123; + u32 scratch124; + u32 scratch125; + u32 scratch126; + u32 scratch127; + u32 scratch128; + u32 scratch129; + u32 scratch130; + u32 scratch131; + u32 scratch132; + u32 scratch133; + u32 scratch134; + u32 scratch135; + u32 scratch136; + u32 scratch137; + u32 scratch138; + u32 scratch139; + u32 scratch140; + u32 scratch141; + u32 scratch142; + u32 scratch143; + u32 scratch144; + u32 scratch145; + u32 scratch146; + u32 scratch147; + u32 scratch148; + u32 scratch149; + u32 scratch150; + u32 scratch151; + u32 scratch152; + u32 scratch153; + u32 scratch154; + u32 scratch155; + u32 scratch156; + u32 scratch157; + u32 scratch158; + u32 scratch159; + u32 scratch160; + u32 scratch161; + u32 scratch162; + u32 scratch163; + u32 scratch164; + u32 scratch165; + u32 scratch166; + u32 scratch167; + u32 scratch168; + u32 scratch169; + u32 scratch170; + u32 scratch171; + u32 scratch172; + u32 scratch173; + u32 scratch174; + u32 scratch175; + u32 scratch176; + u32 scratch177; + u32 scratch178; + u32 scratch179; + u32 scratch180; + u32 scratch181; + u32 scratch182; + u32 scratch183; + u32 scratch184; + u32 scratch185; + u32 scratch186; + u32 scratch187; + u32 scratch188; + u32 scratch189; + u32 scratch190; + u32 scratch191; + u32 scratch192; + u32 scratch193; + u32 scratch194; + u32 scratch195; + u32 scratch196; + u32 scratch197; + u32 scratch198; + u32 scratch199; + u32 scratch200; + u32 scratch201; + u32 scratch202; + u32 scratch203; + u32 scratch204; + u32 scratch205; + u32 scratch206; + u32 scratch207; + u32 scratch208; + u32 scratch209; + u32 scratch210; + u32 scratch211; + u32 scratch212; + u32 scratch213; + u32 scratch214; + u32 scratch215; + u32 scratch216; + u32 scratch217; + u32 scratch218; + u32 scratch219; + u32 scratch220; + u32 scratch221; + u32 scratch222; + u32 scratch223; + u32 scratch224; + u32 scratch225; + u32 scratch226; + u32 scratch227; + u32 scratch228; + u32 scratch229; + u32 scratch230; + u32 scratch231; + u32 scratch232; + u32 scratch233; + u32 scratch234; + u32 scratch235; + u32 scratch236; + u32 scratch237; + u32 scratch238; + u32 scratch239; + u32 scratch240; + u32 scratch241; + u32 scratch242; + u32 scratch243; + u32 scratch244; + u32 scratch245; + u32 scratch246; + u32 scratch247; + u32 scratch248; + u32 scratch249; + u32 scratch250; + u32 scratch251; + u32 scratch252; + u32 scratch253; + u32 scratch254; + u32 scratch255; + u32 scratch256; + u32 scratch257; + u32 scratch258; + u32 scratch259; + u32 scratch260; + u32 scratch261; + u32 scratch262; + u32 scratch263; + u32 scratch264; + u32 scratch265; + u32 scratch266; + u32 scratch267; + u32 scratch268; + u32 scratch269; + u32 scratch270; + u32 scratch271; + u32 scratch272; + u32 scratch273; + u32 scratch274; + u32 scratch275; + u32 scratch276; + u32 scratch277; + u32 scratch278; + u32 scratch279; + u32 scratch280; + u32 scratch281; + u32 scratch282; + u32 scratch283; + u32 scratch284; + u32 scratch285; + u32 scratch286; + u32 scratch287; + u32 scratch288; + u32 scratch289; + u32 scratch290; + u32 scratch291; + u32 scratch292; + u32 scratch293; + u32 scratch294; + u32 scratch295; + u32 scratch296; + u32 scratch297; + u32 scratch298; + u32 scratch299; /* 0x9CC */ + u32 _rsv0x9D0[50]; + u32 secure_scratch80; /* 0xa98 */ + u32 secure_scratch81; + u32 secure_scratch82; + u32 secure_scratch83; + u32 secure_scratch84; + u32 secure_scratch85; + u32 secure_scratch86; + u32 secure_scratch87; + u32 secure_scratch88; + u32 secure_scratch89; + u32 secure_scratch90; + u32 secure_scratch91; + u32 secure_scratch92; + u32 secure_scratch93; + u32 secure_scratch94; + u32 secure_scratch95; + u32 secure_scratch96; + u32 secure_scratch97; + u32 secure_scratch98; + u32 secure_scratch99; + u32 secure_scratch100; + u32 secure_scratch101; + u32 secure_scratch102; + u32 secure_scratch103; + u32 secure_scratch104; + u32 secure_scratch105; + u32 secure_scratch106; + u32 secure_scratch107; + u32 secure_scratch108; + u32 secure_scratch109; + u32 secure_scratch110; + u32 secure_scratch111; + u32 secure_scratch112; + u32 secure_scratch113; + u32 secure_scratch114; + u32 secure_scratch115; + u32 secure_scratch116; + u32 secure_scratch117; + u32 secure_scratch118; + u32 secure_scratch119; +}; + +#endif /* _TEGRA210_PMC_H_ */ diff --git a/emummc/source/soc/t210.h b/emummc/source/soc/t210.h new file mode 100644 index 000000000..0ce1d5d39 --- /dev/null +++ b/emummc/source/soc/t210.h @@ -0,0 +1,121 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _T210_H_ +#define _T210_H_ + +#include "../utils/types.h" + +intptr_t QueryIoMapping(u64 addr, u64 size); + +#define TMR_BASE 0x60005000 +#define CLOCK_BASE 0x60006000 +#define GPIO_BASE 0x6000D000 +#define GPIO_1_BASE (GPIO_BASE) +#define GPIO_2_BASE (GPIO_BASE + 0x100) +#define GPIO_3_BASE (GPIO_BASE + 0x200) +#define GPIO_4_BASE (GPIO_BASE + 0x300) +#define GPIO_5_BASE (GPIO_BASE + 0x400) +#define GPIO_6_BASE (GPIO_BASE + 0x500) +#define GPIO_7_BASE (GPIO_BASE + 0x600) +#define GPIO_8_BASE (GPIO_BASE + 0x700) +#define APB_MISC_BASE 0x70000000 +#define PINMUX_AUX_BASE 0x70003000 +#define PWM_BASE 0x7000A000 +#define RTC_BASE 0x7000E000 +#define PMC_BASE 0x7000E400 + +#define _REG(base, off) *(vu32 *)(QueryIoMapping((u64)base, 0) + (off)) +#define _REG_IO(base, off, size) *(vu32 *)(QueryIoMapping((u64)base, size) + (off)) + +#define RTC(off) _REG_IO(RTC_BASE, off, 0x4000) +#define TMR(off) _REG_IO(TMR_BASE, off, 0x3FF) +#define CLOCK(off) _REG_IO(CLOCK_BASE, off, 0x1000) + +#define PMC(off) _REG_IO(PMC_BASE, off, 0x1000) // ?????????? + +#define APB_MISC(off) _REG_IO(APB_MISC_BASE, off, 0x4000) +#define PINMUX_AUX(off) _REG_IO(APB_MISC_BASE, off + (PINMUX_AUX_BASE - APB_MISC_BASE), 0x4000) + +#define GPIO(off) _REG_IO(GPIO_BASE, off, 0x1000) +#define GPIO_1(off) _REG_IO(GPIO_BASE, off + (GPIO_1_BASE - GPIO_BASE), 0x1000) +#define GPIO_2(off) _REG_IO(GPIO_BASE, off + (GPIO_2_BASE - GPIO_BASE), 0x1000) +#define GPIO_3(off) _REG_IO(GPIO_BASE, off + (GPIO_3_BASE - GPIO_BASE), 0x1000) +#define GPIO_4(off) _REG_IO(GPIO_BASE, off + (GPIO_4_BASE - GPIO_BASE), 0x1000) +#define GPIO_5(off) _REG_IO(GPIO_BASE, off + (GPIO_5_BASE - GPIO_BASE), 0x1000) +#define GPIO_6(off) _REG_IO(GPIO_BASE, off + (GPIO_6_BASE - GPIO_BASE), 0x1000) +#define GPIO_7(off) _REG_IO(GPIO_BASE, off + (GPIO_7_BASE - GPIO_BASE), 0x1000) +#define GPIO_8(off) _REG_IO(GPIO_BASE, off + (GPIO_8_BASE - GPIO_BASE), 0x1000) + +#define HOST1X(off) _REG(HOST1X_BASE, off) +#define BPMP_CACHE_CTRL(off) _REG(BPMP_CACHE_BASE, off) +#define DISPLAY_A(off) _REG(DISPLAY_A_BASE, off) +#define DSI(off) _REG(DSI_BASE, off) +#define VIC(off) _REG(VIC_BASE, off) +#define TSEC(off) _REG(TSEC_BASE, off) +#define SOR1(off) _REG(SOR1_BASE, off) +#define FLOW_CTLR(off) _REG(FLOW_CTLR_BASE, off) +#define SYSREG(off) _REG(SYSREG_BASE, off) +#define SB(off) _REG(SB_BASE, off) +#define EXCP_VEC(off) _REG(EXCP_VEC_BASE, off) +#define PWM(off) _REG(PWM_BASE, off) +#define SYSCTR0(off) _REG(SYSCTR0_BASE, off) +#define FUSE(off) _REG(FUSE_BASE, off) +#define KFUSE(off) _REG(KFUSE_BASE, off) +#define SE(off) _REG(SE_BASE, off) +#define MC(off) _REG(MC_BASE, off) +#define EMC(off) _REG(EMC_BASE, off) +#define MIPI_CAL(off) _REG(MIPI_CAL_BASE, off) +#define I2S(off) _REG(I2S_BASE, off) +#define CL_DVFS(off) _REG(CL_DVFS_BASE, off) +#define TEST_REG(off) _REG(0x0, off) + +/*! Misc registers. */ +#define APB_MISC_PP_STRAPPING_OPT_A 0x08 +#define APB_MISC_PP_PINMUX_GLOBAL 0x40 +#define APB_MISC_GP_LCD_BL_PWM_CFGPADCTRL 0xA34 +#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98 +#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL 0xAB4 +#define APB_MISC_GP_WIFI_EN_CFGPADCTRL 0xB64 +#define APB_MISC_GP_WIFI_RST_CFGPADCTRL 0xB68 + +/*! System registers. */ +#define AHB_ARBITRATION_XBAR_CTRL 0xE0 +#define AHB_AHB_SPARE_REG 0x110 + +/*! RTC registers. */ +#define APBDEV_RTC_SECONDS 0x8 +#define APBDEV_RTC_SHADOW_SECONDS 0xC +#define APBDEV_RTC_MILLI_SECONDS 0x10 + +/*! TMR registers. */ +#define TIMERUS_CNTR_1US (0x10 + 0x0) +#define TIMERUS_USEC_CFG (0x10 + 0x4) +#define TIMER_TMR9_TMR_PTV 0x80 +#define TIMER_EN (1 << 31) +#define TIMER_PER_EN (1 << 30) +#define TIMER_WDT4_CONFIG (0x100 + 0x80) +#define TIMER_SRC(TMR) (TMR & 0xF) +#define TIMER_PER(PER) ((PER & 0xFF) << 4) +#define TIMER_SYSRESET_EN (1 << 14) +#define TIMER_PMCRESET_EN (1 << 15) +#define TIMER_WDT4_COMMAND (0x108 + 0x80) +#define TIMER_START_CNT (1 << 0) +#define TIMER_CNT_DISABLE (1 << 1) +#define TIMER_WDT4_UNLOCK_PATTERN (0x10C + 0x80) +#define TIMER_MAGIC_PTRN 0xC45A + +#endif diff --git a/emummc/source/utils/fatal.c b/emummc/source/utils/fatal.c new file mode 100644 index 000000000..e76937b9f --- /dev/null +++ b/emummc/source/utils/fatal.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include "fatal.h" + +void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason) +{ + // Reboot to RCM + smcRebootToRcm(); + + while(true) + ; // Should never be reached +} diff --git a/emummc/source/utils/fatal.h b/emummc/source/utils/fatal.h new file mode 100644 index 000000000..ccbcd6fd8 --- /dev/null +++ b/emummc/source/utils/fatal.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#pragma once +#include "../nx/smc.h" + +enum FatalReason { + Fatal_InitMMC = 0, + Fatal_InitSD, + Fatal_InvalidAccessor, + Fatal_ReadNoAccessor, + Fatal_WriteNoAccessor, + Fatal_IoMapping, + Fatal_UnknownVersion, + Fatal_BadResult, + Fatal_GetConfig, + Fatal_Max +}; + +void __attribute__((noreturn)) fatal_abort(enum FatalReason abortReason); diff --git a/emummc/source/utils/types.h b/emummc/source/utils/types.h new file mode 100644 index 000000000..b39359e8f --- /dev/null +++ b/emummc/source/utils/types.h @@ -0,0 +1,97 @@ +/* +* Copyright (c) 2018 naehrwert +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include + +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define OFFSET_OF(t, m) ((u32)&((t *)NULL)->m) +#define CONTAINER_OF(mp, t, mn) ((t *)((u32)mp - OFFSET_OF(t, mn))) + +/// Creates a bitmask from a bit number. +#ifndef BIT +#define BIT(n) (1U<<(n)) +#endif + +typedef signed char s8; +typedef short s16; +typedef short SHORT; +typedef int s32; +typedef int INT; +typedef long LONG; +typedef long long int s64; +typedef unsigned char u8; +typedef unsigned char BYTE; +typedef unsigned short u16; +typedef unsigned short WORD; +typedef unsigned short WCHAR; +typedef unsigned int u32; +typedef unsigned int UINT; +typedef unsigned long DWORD; +typedef unsigned long long QWORD; +typedef unsigned long long int u64; +typedef volatile unsigned char vu8; +typedef volatile unsigned short vu16; +typedef volatile unsigned int vu32; + +typedef u32 Handle; ///< Kernel object handle. +typedef u32 Result; ///< Function error code result type. + +#ifndef __cplusplus +typedef int bool; +#define true 1 +#define false 0 +#endif /* __cplusplus */ + +#define BOOT_CFG_AUTOBOOT_EN (1 << 0) +#define BOOT_CFG_FROM_LAUNCH (1 << 1) +#define BOOT_CFG_SEPT_RUN (1 << 7) + +#define EXTRA_CFG_KEYS (1 << 0) +#define EXTRA_CFG_PAYLOAD (1 << 1) +#define EXTRA_CFG_MODULE (1 << 2) + +typedef struct __attribute__((__packed__)) _boot_cfg_t +{ + u8 boot_cfg; + u8 autoboot; + u8 autoboot_list; + u8 extra_cfg; + u8 rsvd[128]; +} boot_cfg_t; + +typedef struct __attribute__((__packed__)) _ipl_ver_meta_t +{ + u32 magic; + u32 version; + u16 rsvd0; + u16 rsvd1; +} ipl_ver_meta_t; + +typedef struct __attribute__((__packed__)) _reloc_meta_t +{ + u32 start; + u32 stack; + u32 end; + u32 ep; +} reloc_meta_t; + +#endif diff --git a/emummc/source/utils/util.c b/emummc/source/utils/util.c new file mode 100644 index 000000000..ee95032b0 --- /dev/null +++ b/emummc/source/utils/util.c @@ -0,0 +1,110 @@ +/* +* Copyright (c) 2018 naehrwert +* Copyright (C) 2018 CTCaer +* Copyright (C) 2019 M4xw +* Copyright (c) 2019 Atmosphere-NX +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope 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 . +*/ + +#include "util.h" +#include "fatal.h" +#include "types.h" +#include "../nx/counter.h" +#include "../nx/svc.h" +#include "../soc/t210.h" + +typedef struct _io_mapping_t +{ + u64 phys; + u64 virt; + u64 size; +} io_mapping_t; +static io_mapping_t io_mapping_list[10] = {0}; // Max 10 Mappings +#define IO_MAPPING_COUNT (sizeof(io_mapping_list) / sizeof(io_mapping_t)) + +static inline uintptr_t _GetIoMapping(u64 io_addr, u64 io_size) +{ + u64 vaddr; + u64 aligned_addr = (io_addr & ~0xFFFul); + u64 aligned_size = io_size + (io_addr - aligned_addr); + if (svcQueryIoMapping(&vaddr, aligned_addr, aligned_size) != 0) { + fatal_abort(Fatal_IoMapping); + } + return (uintptr_t)(vaddr + (io_addr - aligned_addr)); +} + +intptr_t QueryIoMapping(u64 addr, u64 size) +{ + for (int i = 0; i < IO_MAPPING_COUNT; i++) + { + if (io_mapping_list[i].phys == addr && io_mapping_list[i].size == size) + { + return io_mapping_list[i].virt; + } + } + + u64 ioMap = _GetIoMapping(addr, size); + + for (int i = 0; i < IO_MAPPING_COUNT; i++) + { + if (io_mapping_list[i].phys == 0 && io_mapping_list[i].virt == 0 && io_mapping_list[i].size == 0) // First empty + { + io_mapping_list[i].virt = ioMap; + io_mapping_list[i].phys = addr; + io_mapping_list[i].size = size; + break; + } + } + + return (intptr_t)ioMap; +} + +u64 get_tmr_s() +{ + return armTicksToNs(armGetSystemTick()) / 1e+9; +} + +u64 get_tmr_ms() +{ + return armTicksToNs(armGetSystemTick()) / 1000000; +} + +u64 get_tmr_us() +{ + return armTicksToNs(armGetSystemTick()) / 1000; +} + +// TODO: Figure if Sleep or Busy loop +void msleep(u64 milliseconds) +{ + u64 now = get_tmr_ms(); + while (get_tmr_ms() - now < milliseconds) + ; + //svcSleepThread(1000000 * milliseconds); +} + +// TODO: Figure if Sleep or Busy loop +void usleep(u64 microseconds) +{ + u64 now = get_tmr_us(); + while (get_tmr_us() - now < microseconds) + ; + //svcSleepThread(1000 * microseconds); +} + +void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops) +{ + for (u32 i = 0; i < num_ops; i++) + base[ops[i].off] = ops[i].val; +} diff --git a/emummc/source/utils/util.h b/emummc/source/utils/util.h new file mode 100644 index 000000000..0571d01c5 --- /dev/null +++ b/emummc/source/utils/util.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (C) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include "types.h" + +intptr_t QueryIoMapping(u64 addr, u64 size); +#define byte_swap_32(num) ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \ + ((num >> 8 )& 0xff00) | ((num << 24) & 0xff000000) + +typedef struct _cfg_op_t +{ + u32 off; + u32 val; +} cfg_op_t; + +u64 get_tmr_us(); +u64 get_tmr_ms(); +u64 get_tmr_s(); +void usleep(u64 ticks); +void msleep(u64 milliseconds); +void exec_cfg(u32 *base, const cfg_op_t *ops, u32 num_ops); + +#endif diff --git a/emummc/tools/fs_ida_nintendo_folder_xref_formatter.au3 b/emummc/tools/fs_ida_nintendo_folder_xref_formatter.au3 new file mode 100644 index 000000000..6e28f869b --- /dev/null +++ b/emummc/tools/fs_ida_nintendo_folder_xref_formatter.au3 @@ -0,0 +1,36 @@ +ParseClipboard() + +Func FormatLineData($sLineData) + Local $lineData = StringReplace($sLineData, @TAB, " ") + Local $isADRP = false + $lineData = StringReplace($lineData, "Up o sub_71000" , "0x") + $isADRP = StringInStr($lineData, "ADRP") + + Local $targetRegister = StringSplit(StringSplit($lineData, "X")[2], ",")[1] + $lineData = StringSplit($lineData, " ")[1] + $lineDataAddr = StringSplit($lineData, "+")[1] + $lineDataAddition = StringSplit($lineData, "+")[2] + + $lineDataAddr = StringReplace($lineDataAddr, "0x" , "") + $lineDataAddition = StringReplace($lineDataAddition, "0x" , "") + + If $isADRP Then + $lineData = "0x" & Hex(Dec($lineDataAddr) + Dec($lineDataAddition)) + Else + Return "" + EndIf + + Return "{.opcode_reg = " & $targetRegister & ", .adrp_offset = " & $lineData & "}, \" & @LF +EndFunc + +Func ParseClipboard() + Local $sData = ClipGet() + Local $oData = "" + Local $sLineData = StringSplit(StringReplace($sData, @CRLF, @LF), @LF) + For $i = 2 to UBound($sLineData) - 2 + Local $lineData = FormatLineData($sLineData[$i]) + ;ConsoleWrite($lineData) + $oData = $oData & $lineData + Next + ClipPut($oData) +EndFunc diff --git a/emummc/tools/kip1converter.py b/emummc/tools/kip1converter.py new file mode 100644 index 000000000..471e1e4af --- /dev/null +++ b/emummc/tools/kip1converter.py @@ -0,0 +1,54 @@ +# Modified kip1 conversion script, originally by jakibaki +# Used for dev purposes, will be replaced in the future + +from struct import pack, unpack +from sys import argv + +f = open(argv[1], "rb") + +header_start = f.read(0x20) + +section_names = [".text", ".rodata", ".data", ".bss"] + +sections = [] +for i in range(6): + section_bytes = f.read(0x10) + section = {} + + if i < len(section_names): + section["Name"] = section_names[i] + + section["OutOffset"], section["DecompressedSize"], section["CompressedSize"], section["Attribute"] = unpack( + "IIII", section_bytes) + sections.append(section) + print(section) + +assert (sections[3]["OutOffset"] + sections[3]["DecompressedSize"]) % 0x1000 == 0 + +kernel_caps = [] +for i in range(0x20): + val, = unpack("I", f.read(4)) + kernel_caps.append(val) + +f.seek(0x100) + +for i in range(3): + section = sections[i] + section["Buffer"] = f.read(section["DecompressedSize"]) +print(f.read()) + +f.close() + +f = open(argv[2], "wb") + +for i in range(3): + section = sections[i] + f.seek(section["OutOffset"]) + f.write(section["Buffer"]) + +f.seek(sections[3]["OutOffset"]) +f.write(b'\0' * sections[3]["DecompressedSize"]) + +caps = open("emummc.caps", "wb") +for i in range(0x20): + caps.write(pack("I", kernel_caps[i]))