sys-clk: deprecate
This commit is contained in:
2
Source/sys-clk-OC/sysmodule/.gitignore
vendored
2
Source/sys-clk-OC/sysmodule/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/out
|
||||
/build
|
||||
@@ -1,164 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := sys-clk-OC
|
||||
BUILD := build
|
||||
OUTDIR := out
|
||||
RESOURCES := res
|
||||
SOURCES := src src/nx/ipc ../common/src
|
||||
DATA := data
|
||||
INCLUDES := ../common/include
|
||||
EXEFS_SRC := exefs_src
|
||||
LIBNAMES := minIni nxExt
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# version control constants
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET_VERSION := 1.8.3
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
DEFINES := -DDISABLE_IPC -DTARGET="\"$(TARGET)\"" -DTARGET_VERSION="\"$(TARGET_VERSION)\""
|
||||
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O3 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++20
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lnx $(foreach lib,$(LIBNAMES),-l$(lib))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(foreach lib,$(LIBNAMES),$(TOPDIR)/lib/$(lib))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
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)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
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)
|
||||
|
||||
export APP_JSON := $(TOPDIR)/perms.json
|
||||
|
||||
.PHONY: $(BUILD) clean clean-libs clean-build all libs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
libs:
|
||||
@$(foreach lib,$(LIBNAMES),$(MAKE) --no-print-directory -C $(TOPDIR)/lib/$(lib) && ) true
|
||||
|
||||
$(LIBNAMES):
|
||||
@echo $@
|
||||
|
||||
$(BUILD): libs
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean-libs:
|
||||
@echo clean libs $(LIBNAMES) ...
|
||||
@$(foreach lib,$(LIBNAMES),$(MAKE) -C $(TOPDIR)/lib/$(lib) clean && ) true
|
||||
|
||||
clean-build:
|
||||
@echo clean build ...
|
||||
@rm -fr $(BUILD) $(TARGET).kip $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf $(OUTDIR)
|
||||
|
||||
clean: clean-libs clean-build
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all $(LIBFILES)
|
||||
|
||||
LIBFILES := $(foreach lib,$(LIBNAMES),$(TOPDIR)/lib/$(lib)/lib/lib$(lib).a)
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
all: $(OUTPUT).nsp
|
||||
|
||||
$(OUTPUT).nsp: $(OUTPUT).nso $(OUTPUT).npdm
|
||||
|
||||
$(OUTPUT).elf: $(OFILES) $(LIBFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -1,13 +0,0 @@
|
||||
# Editor files
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# Objects
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Lib
|
||||
lib
|
||||
release
|
||||
debug
|
||||
@@ -1,12 +0,0 @@
|
||||
; 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/compuphase/minIni
|
||||
branch = master
|
||||
commit = 8ce144c3c287fa4e59f8ed4c405cd8b7e29f189b
|
||||
parent = f32c0b1bca87a6d7e55fb341f8db9ef33b13819f
|
||||
method = merge
|
||||
cmdver = 0.4.0
|
||||
@@ -1,189 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
|
||||
EXCEPTION TO THE APACHE 2.0 LICENSE
|
||||
|
||||
As a special exception to the Apache License 2.0 (and referring to the
|
||||
definitions in Section 1 of this license), you may link, statically or
|
||||
dynamically, the "Work" to other modules to produce an executable file
|
||||
containing portions of the "Work", and distribute that executable file
|
||||
in "Object" form under the terms of your choice, without any of the
|
||||
additional requirements listed in Section 4 of the Apache License 2.0.
|
||||
This exception applies only to redistributions in "Object" form (not
|
||||
"Source" form) and only if no modifications have been made to the "Work".
|
||||
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
SOURCES := dev
|
||||
DATA := data
|
||||
INCLUDES := dev
|
||||
SRC_H_FILES :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
|
||||
|
||||
CFLAGS := -g -Wall -Werror \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
$(ARCH) \
|
||||
$(BUILD_CFLAGS)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := minIni.c
|
||||
CPPFILES :=
|
||||
SFILES :=
|
||||
BINFILES :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
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 := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
.PHONY: clean all lib/lib$(TARGET).a
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: lib/lib$(TARGET).a
|
||||
|
||||
lib:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
release:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
debug:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
lib/lib$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
|
||||
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
|
||||
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
|
||||
DEPSDIR=$(CURDIR)/release \
|
||||
--no-print-directory -C release \
|
||||
-f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr release debug lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT) : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -1,12 +0,0 @@
|
||||
minIni is a programmer's library to read and write "INI" files in embedded
|
||||
systems. The library takes little resources and can be configured for various
|
||||
kinds of file I/O libraries.
|
||||
|
||||
The method for portable INI file management in minIni is, in part based, on the
|
||||
article "Multiplatform .INI Files" by Joseph J. Graf in the March 1994 issue of
|
||||
Dr. Dobb's Journal.
|
||||
|
||||
The C++ class in minIni.h was contributed by Steven Van Ingelgem.
|
||||
|
||||
The option to compile minIni as a read-only library was contributed by Luca
|
||||
Bassanello.
|
||||
@@ -1,170 +0,0 @@
|
||||
# minIni
|
||||
minIni is a portable and configurable library for reading and writing ".INI" files. At 830 lines of commented source
|
||||
code
|
||||
(version 1.2), minIni truly is a "mini" INI file parser, especially considering its features.
|
||||
|
||||
The library does not require the file I/O functions from the standard C/C++ library, but instead lets you configure
|
||||
the file I/O interface to use via macros. minIni uses limited stack space and does not use dynamic memory (malloc and
|
||||
friends) at all.
|
||||
|
||||
Some minor variations on standard INI files are supported too, notably minIni supports INI files that lack sections.
|
||||
|
||||
|
||||
# Acknowledgement
|
||||
|
||||
minIni is derived from an earlier INI file parser (which I wrote) for desktop systems.
|
||||
|
||||
In turn, that earlier parser was a re-write of the code from the article "Multiplatform .INI Files" by Joseph J. Graf
|
||||
in the March 1994 issue of Dr. Dobb's Journal. In other words, minIni has its roots in the work of Joseph Graf (even
|
||||
though the code has been almost completely re-written).
|
||||
|
||||
|
||||
# Features
|
||||
|
||||
minIni is a programmer's library to read and write "INI" files in embedded systems. minIni takes little resources,
|
||||
can be configured for various kinds of file I/O libraries and provides functionality for reading, writing and
|
||||
deleting keys from an INI file.
|
||||
|
||||
Although the main feature of minIni is that it is small and minimal, it has a few other features:
|
||||
|
||||
* minIni supports reading keys that are outside a section, and it thereby supports configuration files that do not use sections (but that are otherwise compatible with INI files).
|
||||
* You may use a colon to separate key and value; the colon is equivalent to the equal sign. That is, the strings "Name: Value" and "Name=Value" have the same meaning.
|
||||
* The hash character ("#") is an alternative for the semicolon to start a comment. Trailing comments (i.e. behind a key/value pair on a line) are allowed.
|
||||
* Leading and trailing white space around key names and values is ignored.
|
||||
* When writing a value that contains a comment character (";" or "#"), that value will automatically be put between double quotes; when reading the value, these quotes are removed. When a double-quote itself appears in the setting, these characters are escaped.
|
||||
* Section and key enumeration are supported.
|
||||
* You can optionally set the line termination (for text files) that minIni will use. (This is a compile-time setting, not a run-time setting.)
|
||||
* Since writing speed is much lower than reading speed in Flash memory (SD/MMC cards, USB memory sticks), minIni minimizes "file writes" at the expense of double "file reads".
|
||||
* The memory footprint is deterministic. There is no dynamic memory allocation.
|
||||
|
||||
## INI file reading paradigms
|
||||
|
||||
There are two approaches to reading settings from an INI file. One way is to call a function, such as
|
||||
GetProfileString() for every section and key that you need. This is especially convenient if there is a large
|
||||
INI file, but you only need a few settings from that file at any time —especially if the INI file can also
|
||||
change while your program runs. This is the approach that the Microsoft Windows API uses.
|
||||
|
||||
The above procedure is quite inefficient, however, when you need to retrieve quite a few settings in a row from
|
||||
the INI file —especially if the INI file is not cached in memory (which it isn't, in minIni). A different approach
|
||||
to getting settings from an INI file is to call a "parsing" function and let that function call the application
|
||||
back with the section and key names plus the associated data. XML parsing libraries often use this approach; see
|
||||
for example the Expat library.
|
||||
|
||||
minIni supports both approaches. For reading a single setting, use functions like ini_gets(). For the callback
|
||||
approach, implement a callback and call ini_browse(). See the minIni manual for details.
|
||||
|
||||
|
||||
# INI file syntax
|
||||
|
||||
INI files are best known from Microsoft Windows, but they are also used with applications that run on other
|
||||
platforms (although their file extension is sometimes ".cfg" instead of ".ini").
|
||||
|
||||
INI files have a simple syntax with name/value pairs in a plain text file. The name must be unique (per section)
|
||||
and the value must fit on a single line. INI files are commonly separated into sections —in minIni, this is
|
||||
optional. A section is a name between square brackets, like "[Network]" in the example below.
|
||||
|
||||
```
|
||||
[Network]
|
||||
hostname=My Computer
|
||||
address=dhcp
|
||||
dns = 192.168.1.1
|
||||
```
|
||||
|
||||
In the API and in this documentation, the "name" for a setting is denoted as the key for the setting. The key
|
||||
and the value are separated by an equal sign ("="). minIni supports the colon (":") as an alternative to the
|
||||
equal sign for the key/value delimiter.
|
||||
|
||||
Leading a trailing spaces around values or key names are removed. If you need to include leading and/or trailing
|
||||
spaces in a value, put the value between double quotes. The ini_gets() function (from the minIni library, see the
|
||||
minIni manual) strips off the double quotes from the returned value. Function ini_puts() adds double quotes if
|
||||
the value to write contains trailing white space (or special characters).
|
||||
|
||||
minIni ignores spaces around the "=" or ":" delimiters, but it does not ignore spaces between the brackets in a
|
||||
section name. In other words, it is best not to put spaces behind the opening bracket "[" or before the closing
|
||||
bracket "]" of a section name.
|
||||
|
||||
Comments in the INI must start with a semicolon (";") or a hash character ("#"), and run to the end of the line.
|
||||
A comment can be a line of its own, or it may follow a key/value pair (the "#" character and trailing comments
|
||||
are extensions of minIni).
|
||||
|
||||
For more details on the format, please see http://en.wikipedia.org/wiki/INI_file.
|
||||
|
||||
|
||||
# Adapting minIni to a file system
|
||||
|
||||
The minIni library must be configured for a platform with the help of a so- called "glue file". This glue file
|
||||
contains macros (and possibly functions) that map file reading and writing functions used by the minIni library
|
||||
to those provided by the operating system. The glue file must be called "minGlue.h".
|
||||
|
||||
To get you started, the minIni distribution comes with the following example glue files:
|
||||
|
||||
* a glue file that maps to the standard C/C++ library (specifically the file I/O functions from the "stdio" package),
|
||||
* a glue file for Microchip's "Memory Disk Drive File System Library" (see http://www.microchip.com/),
|
||||
* a glue file for the FAT library provided with the CCS PIC compiler (see http://www.ccsinfo.com/)
|
||||
* a glue file for the EFS Library (EFSL, http://www.efsl.be/),
|
||||
* and a glue file for the FatFs and Petit-FatFs libraries (http://elm-chan.org/fsw/ff/00index_e.html).
|
||||
|
||||
The minIni library does not rely on the availability of a standard C library, because embedded operating systems
|
||||
may have limited support for file I/O. Even on full operating systems, separating the file I/O from the INI format
|
||||
parsing carries advantages, because it allows you to cache the INI file and thereby enhance performance.
|
||||
|
||||
The glue file must specify the type that identifies a file, whether it is a handle or a pointer. For the standard
|
||||
C/C++ file I/O library, this would be:
|
||||
|
||||
```C
|
||||
#define INI_FILETYPE FILE*
|
||||
```
|
||||
|
||||
If you are not using the standard C/C++ file I/O library, chances are that you need a different handle or
|
||||
"structure" to identify the storage than the ubiquitous "FILE*" type. For example, the glue file for the FatFs
|
||||
library uses the following declaration:
|
||||
|
||||
```C
|
||||
#define INI_FILETYPE FIL
|
||||
```
|
||||
|
||||
The minIni functions declare variables of this INI_FILETYPE type and pass these variables to sub-functions
|
||||
(including the glue interface functions) by reference.
|
||||
|
||||
For "write support", another type that must be defined is for variables that hold the "current position" in a
|
||||
file. For the standard C/C++ I/O library, this is "fpos_t".
|
||||
|
||||
Another item that needs to be configured is the buffer size. The functions in the minIni library allocate this
|
||||
buffer on the stack, so the buffer size is directly related to the stack usage. In addition, the buffer size
|
||||
determines the maximum line length that is supported in the INI file and the maximum path name length for the
|
||||
temporary file. For example, minGlue.h could contain the definition:
|
||||
|
||||
```C
|
||||
#define INI_BUFFERSIZE 512
|
||||
```
|
||||
|
||||
The above macro limits the line length of the INI files supported by minIni to 512 characters.
|
||||
|
||||
The temporary file is only used when writing to INI files. The minIni routines copy/change the INI file to a
|
||||
temporary file and then rename that temporary file to the original file. This approach uses the least amount of
|
||||
memory. The path name of the temporary file is the same as the input file, but with the last character set to a
|
||||
tilde ("~").
|
||||
|
||||
Below is an example of a glue file (this is the one that maps to the C/C++ "stdio" library).
|
||||
|
||||
```C
|
||||
#include <stdio.h>
|
||||
|
||||
#define INI_FILETYPE FILE*
|
||||
#define ini_openread(filename,file) ((*(file) = fopen((filename),"r")) != NULL)
|
||||
#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"w")) != NULL)
|
||||
#define ini_close(file) (fclose(*(file)) == 0)
|
||||
#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL)
|
||||
#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0)
|
||||
#define ini_rename(source,dest) (rename((source), (dest)) == 0)
|
||||
#define ini_remove(filename) (remove(filename) == 0)
|
||||
|
||||
#define INI_FILEPOS fpos_t
|
||||
#define ini_tell(file,pos) (fgetpos(*(file), (pos)) == 0)
|
||||
#define ini_seek(file,pos) (fsetpos(*(file), (pos)) == 0)
|
||||
```
|
||||
|
||||
As you can see, a glue file is mostly a set of macros that wraps one function definition around another.
|
||||
|
||||
The glue file may contain more settings, for support of rational numbers, to explicitly set the line termination
|
||||
character(s), or to disable write support (for example). See the manual that comes with the archive for the details.
|
||||
@@ -1,37 +0,0 @@
|
||||
/* Glue functions for the minIni library, based on the FatFs and Petit-FatFs
|
||||
* libraries, see http://elm-chan.org/fsw/ff/00index_e.html
|
||||
*
|
||||
* By CompuPhase, 2008-2012
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*
|
||||
* (The FatFs and Petit-FatFs libraries are copyright by ChaN and licensed at
|
||||
* its own terms.)
|
||||
*/
|
||||
|
||||
#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */
|
||||
|
||||
/* You must set _USE_STRFUNC to 1 or 2 in the include file ff.h (or tff.h)
|
||||
* to enable the "string functions" fgets() and fputs().
|
||||
*/
|
||||
#include "ff.h" /* include tff.h for Tiny-FatFs */
|
||||
|
||||
#define INI_FILETYPE FIL
|
||||
#define ini_openread(filename,file) (f_open((file), (filename), FA_READ+FA_OPEN_EXISTING) == FR_OK)
|
||||
#define ini_openwrite(filename,file) (f_open((file), (filename), FA_WRITE+FA_CREATE_ALWAYS) == FR_OK)
|
||||
#define ini_close(file) (f_close(file) == FR_OK)
|
||||
#define ini_read(buffer,size,file) f_gets((buffer), (size),(file))
|
||||
#define ini_write(buffer,file) f_puts((buffer), (file))
|
||||
#define ini_remove(filename) (f_unlink(filename) == FR_OK)
|
||||
|
||||
#define INI_FILEPOS DWORD
|
||||
#define ini_tell(file,pos) (*(pos) = f_tell((file)))
|
||||
#define ini_seek(file,pos) (f_lseek((file), *(pos)) == FR_OK)
|
||||
|
||||
static int ini_rename(TCHAR *source, const TCHAR *dest)
|
||||
{
|
||||
/* Function f_rename() does not allow drive letters in the destination file */
|
||||
char *drive = strchr(dest, ':');
|
||||
drive = (drive == NULL) ? dest : drive + 1;
|
||||
return (f_rename(source, drive) == FR_OK);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* minIni glue functions for FAT library by CCS, Inc. (as provided with their
|
||||
* PIC MCU compiler)
|
||||
*
|
||||
* By CompuPhase, 2011-2012
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*
|
||||
* (The FAT library is copyright (c) 2007 Custom Computer Services, and
|
||||
* licensed at its own terms.)
|
||||
*/
|
||||
|
||||
#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */
|
||||
|
||||
#ifndef FAT_PIC_C
|
||||
#error FAT library must be included before this module
|
||||
#endif
|
||||
#define const /* keyword not supported by CCS */
|
||||
|
||||
#define INI_FILETYPE FILE
|
||||
#define ini_openread(filename,file) (fatopen((filename), "r", (file)) == GOODEC)
|
||||
#define ini_openwrite(filename,file) (fatopen((filename), "w", (file)) == GOODEC)
|
||||
#define ini_close(file) (fatclose((file)) == 0)
|
||||
#define ini_read(buffer,size,file) (fatgets((buffer), (size), (file)) != NULL)
|
||||
#define ini_write(buffer,file) (fatputs((buffer), (file)) == GOODEC)
|
||||
#define ini_remove(filename) (rm_file((filename)) == 0)
|
||||
|
||||
#define INI_FILEPOS fatpos_t
|
||||
#define ini_tell(file,pos) (fatgetpos((file), (pos)) == 0)
|
||||
#define ini_seek(file,pos) (fatsetpos((file), (pos)) == 0)
|
||||
|
||||
#ifndef INI_READONLY
|
||||
/* CCS FAT library lacks a rename function, so instead we copy the file to the
|
||||
* new name and delete the old file
|
||||
*/
|
||||
static int ini_rename(char *source, char *dest)
|
||||
{
|
||||
FILE fr, fw;
|
||||
int n;
|
||||
|
||||
if (fatopen(source, "r", &fr) != GOODEC)
|
||||
return 0;
|
||||
if (rm_file(dest) != 0)
|
||||
return 0;
|
||||
if (fatopen(dest, "w", &fw) != GOODEC)
|
||||
return 0;
|
||||
|
||||
/* With some "insider knowledge", we can save some memory: the "source"
|
||||
* parameter holds a filename that was built from the "dest" parameter. It
|
||||
* was built in a local buffer with the size INI_BUFFERSIZE. We can reuse
|
||||
* this buffer for copying the file.
|
||||
*/
|
||||
while (n=fatread(source, 1, INI_BUFFERSIZE, &fr))
|
||||
fatwrite(source, 1, n, &fw);
|
||||
|
||||
fatclose(&fr);
|
||||
fatclose(&fw);
|
||||
|
||||
/* Now we need to delete the source file. However, we have garbled the buffer
|
||||
* that held the filename of the source. So we need to build it again.
|
||||
*/
|
||||
ini_tempname(source, dest, INI_BUFFERSIZE);
|
||||
return rm_file(source) == 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
/* Glue functions for the minIni library, based on the EFS Library, see
|
||||
* http://www.efsl.be/
|
||||
*
|
||||
* By CompuPhase, 2008-2012
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*
|
||||
* (EFSL is copyright 2005-2006 Lennart Ysboodt and Michael De Nil, and
|
||||
* licensed under the GPL with an exception clause for static linking.)
|
||||
*/
|
||||
|
||||
#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */
|
||||
#define INI_LINETERM "\r\n" /* set line termination explicitly */
|
||||
|
||||
#include "efs.h"
|
||||
extern EmbeddedFileSystem g_efs;
|
||||
|
||||
#define INI_FILETYPE EmbeddedFile
|
||||
#define ini_openread(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'r') == 0)
|
||||
#define ini_openwrite(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'w') == 0)
|
||||
#define ini_close(file) file_fclose(file)
|
||||
#define ini_read(buffer,size,file) (file_read((file), (size), (buffer)) > 0)
|
||||
#define ini_write(buffer,file) (file_write((file), strlen(buffer), (char*)(buffer)) > 0)
|
||||
#define ini_remove(filename) rmfile(&g_efs.myFs, (char*)(filename))
|
||||
|
||||
#define INI_FILEPOS euint32
|
||||
#define ini_tell(file,pos) (*(pos) = (file)->FilePtr))
|
||||
#define ini_seek(file,pos) file_setpos((file), (*pos))
|
||||
|
||||
#if ! defined INI_READONLY
|
||||
/* EFSL lacks a rename function, so instead we copy the file to the new name
|
||||
* and delete the old file
|
||||
*/
|
||||
static int ini_rename(char *source, const char *dest)
|
||||
{
|
||||
EmbeddedFile fr, fw;
|
||||
int n;
|
||||
|
||||
if (file_fopen(&fr, &g_efs.myFs, source, 'r') != 0)
|
||||
return 0;
|
||||
if (rmfile(&g_efs.myFs, (char*)dest) != 0)
|
||||
return 0;
|
||||
if (file_fopen(&fw, &g_efs.myFs, (char*)dest, 'w') != 0)
|
||||
return 0;
|
||||
|
||||
/* With some "insider knowledge", we can save some memory: the "source"
|
||||
* parameter holds a filename that was built from the "dest" parameter. It
|
||||
* was built in buffer and this buffer has the size INI_BUFFERSIZE. We can
|
||||
* reuse this buffer for copying the file.
|
||||
*/
|
||||
while (n=file_read(&fr, INI_BUFFERSIZE, source))
|
||||
file_write(&fw, n, source);
|
||||
|
||||
file_fclose(&fr);
|
||||
file_fclose(&fw);
|
||||
|
||||
/* Now we need to delete the source file. However, we have garbled the buffer
|
||||
* that held the filename of the source. So we need to build it again.
|
||||
*/
|
||||
ini_tempname(source, dest, INI_BUFFERSIZE);
|
||||
return rmfile(&g_efs.myFs, source) == 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/* Glue functions for the minIni library, based on the "FAT Filing System"
|
||||
* library by embedded-code.com
|
||||
*
|
||||
* By CompuPhase, 2008-2012
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*
|
||||
* (The "FAT Filing System" library itself is copyright embedded-code.com, and
|
||||
* licensed at its own terms.)
|
||||
*/
|
||||
|
||||
#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */
|
||||
#include <mem-ffs.h>
|
||||
|
||||
#define INI_FILETYPE FFS_FILE*
|
||||
#define ini_openread(filename,file) ((*(file) = ffs_fopen((filename),"r")) != NULL)
|
||||
#define ini_openwrite(filename,file) ((*(file) = ffs_fopen((filename),"w")) != NULL)
|
||||
#define ini_close(file) (ffs_fclose(*(file)) == 0)
|
||||
#define ini_read(buffer,size,file) (ffs_fgets((buffer),(size),*(file)) != NULL)
|
||||
#define ini_write(buffer,file) (ffs_fputs((buffer),*(file)) >= 0)
|
||||
#define ini_rename(source,dest) (ffs_rename((source), (dest)) == 0)
|
||||
#define ini_remove(filename) (ffs_remove(filename) == 0)
|
||||
|
||||
#define INI_FILEPOS long
|
||||
#define ini_tell(file,pos) (ffs_fgetpos(*(file), (pos)) == 0)
|
||||
#define ini_seek(file,pos) (ffs_fsetpos(*(file), (pos)) == 0)
|
||||
@@ -1,58 +0,0 @@
|
||||
/* minIni glue functions for Microchip's "Memory Disk Drive" file system
|
||||
* library, as presented in Microchip application note AN1045.
|
||||
*
|
||||
* By CompuPhase, 2011-2014
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*
|
||||
* (The "Microchip Memory Disk Drive File System" is copyright (c) Microchip
|
||||
* Technology Incorporated, and licensed at its own terms.)
|
||||
*/
|
||||
|
||||
#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */
|
||||
|
||||
#include "MDD File System\fsio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define INI_FILETYPE FSFILE*
|
||||
#define ini_openread(filename,file) ((*(file) = FSfopen((filename),FS_READ)) != NULL)
|
||||
#define ini_openwrite(filename,file) ((*(file) = FSfopen((filename),FS_WRITE)) != NULL)
|
||||
#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),FS_READPLUS)) != NULL)
|
||||
#define ini_close(file) (FSfclose(*(file)) == 0)
|
||||
#define ini_write(buffer,file) (FSfwrite((buffer), 1, strlen(buffer), (*file)) > 0)
|
||||
#define ini_remove(filename) (FSremove((filename)) == 0)
|
||||
|
||||
#define INI_FILEPOS long int
|
||||
#define ini_tell(file,pos) (*(pos) = FSftell(*(file)))
|
||||
#define ini_seek(file,pos) (FSfseek(*(file), *(pos), SEEK_SET) == 0)
|
||||
|
||||
/* Since the Memory Disk Drive file system library reads only blocks of files,
|
||||
* the function to read a text line does so by "over-reading" a block of the
|
||||
* of the maximum size and truncating it behind the end-of-line.
|
||||
*/
|
||||
static int ini_read(char *buffer, int size, INI_FILETYPE *file)
|
||||
{
|
||||
size_t numread = size;
|
||||
char *eol;
|
||||
|
||||
if ((numread = FSfread(buffer, 1, size, *file)) == 0)
|
||||
return 0; /* at EOF */
|
||||
if ((eol = strchr(buffer, '\n')) == NULL)
|
||||
eol = strchr(buffer, '\r');
|
||||
if (eol != NULL) {
|
||||
/* terminate the buffer */
|
||||
*++eol = '\0';
|
||||
/* "unread" the data that was read too much */
|
||||
FSfseek(*file, - (int)(numread - (size_t)(eol - buffer)), SEEK_CUR);
|
||||
} /* if */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef INI_READONLY
|
||||
static int ini_rename(const char *source, const char *dest)
|
||||
{
|
||||
FSFILE* ftmp = FSfopen((source), FS_READ);
|
||||
FSrename((dest), ftmp);
|
||||
return FSfclose(ftmp) == 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,31 +0,0 @@
|
||||
/* Glue functions for the minIni library, based on the C/C++ stdio library
|
||||
*
|
||||
* Or better said: this file contains macros that maps the function interface
|
||||
* used by minIni to the standard C/C++ file I/O functions.
|
||||
*
|
||||
* By CompuPhase, 2008-2014
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*/
|
||||
|
||||
/* map required file I/O types and functions to the standard C library */
|
||||
#include <stdio.h>
|
||||
|
||||
#define INI_FILETYPE FILE*
|
||||
#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL)
|
||||
#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL)
|
||||
#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL)
|
||||
#define ini_close(file) (fclose(*(file)) == 0)
|
||||
#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL)
|
||||
#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0)
|
||||
#define ini_rename(source,dest) (rename((source), (dest)) == 0)
|
||||
#define ini_remove(filename) (remove(filename) == 0)
|
||||
|
||||
#define INI_FILEPOS long int
|
||||
#define ini_tell(file,pos) (*(pos) = ftell(*(file)))
|
||||
#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0)
|
||||
|
||||
/* for floating-point support, define additional types and functions */
|
||||
#define INI_REAL float
|
||||
#define ini_ftoa(string,value) sprintf((string),"%f",(value))
|
||||
#define ini_atof(string) (INI_REAL)strtod((string),NULL)
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Glue functions for the minIni library, based on the C/C++ stdio library
|
||||
*
|
||||
* Or better said: this file contains macros that maps the function interface
|
||||
* used by minIni to the standard C/C++ file I/O functions.
|
||||
*
|
||||
* By CompuPhase, 2008-2014
|
||||
* This "glue file" is in the public domain. It is distributed without
|
||||
* warranties or conditions of any kind, either express or implied.
|
||||
*/
|
||||
|
||||
/* map required file I/O types and functions to the standard C library */
|
||||
#include <stdio.h>
|
||||
|
||||
#define INI_FILETYPE FILE*
|
||||
#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL)
|
||||
#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL)
|
||||
#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL)
|
||||
#define ini_close(file) (fclose(*(file)) == 0)
|
||||
#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL)
|
||||
#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0)
|
||||
#define ini_rename(source,dest) (rename((source), (dest)) == 0)
|
||||
#define ini_remove(filename) (remove(filename) == 0)
|
||||
|
||||
#define INI_FILEPOS long int
|
||||
#define ini_tell(file,pos) (*(pos) = ftell(*(file)))
|
||||
#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0)
|
||||
|
||||
/* for floating-point support, define additional types and functions */
|
||||
//#define INI_REAL float
|
||||
//#define ini_ftoa(string,value) sprintf((string),"%f",(value))
|
||||
//#define ini_atof(string) (INI_REAL)strtod((string),NULL)
|
||||
|
||||
#define INI_ANSIONLY
|
||||
#define INI_LINETERM "\n"
|
||||
#define PORTABLE_STRNICMP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,158 +0,0 @@
|
||||
/* minIni - Multi-Platform INI file parser, suitable for embedded systems
|
||||
*
|
||||
* Copyright (c) CompuPhase, 2008-2017
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Version: $Id: minIni.h 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $
|
||||
*/
|
||||
#ifndef MININI_H
|
||||
#define MININI_H
|
||||
|
||||
#include "minGlue.h"
|
||||
|
||||
#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY
|
||||
#include <tchar.h>
|
||||
#define mTCHAR TCHAR
|
||||
#else
|
||||
/* force TCHAR to be "char", but only for minIni */
|
||||
#define mTCHAR char
|
||||
#endif
|
||||
|
||||
#if !defined INI_BUFFERSIZE
|
||||
#define INI_BUFFERSIZE 512
|
||||
#endif
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename);
|
||||
long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename);
|
||||
int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename);
|
||||
|
||||
#if defined INI_REAL
|
||||
INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename);
|
||||
#endif
|
||||
|
||||
#if !defined INI_READONLY
|
||||
int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename);
|
||||
int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename);
|
||||
int ini_putsection(const mTCHAR *Section, const mTCHAR **Keys, const mTCHAR **Values, const mTCHAR *Filename);
|
||||
#if defined INI_REAL
|
||||
int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename);
|
||||
#endif
|
||||
#endif /* INI_READONLY */
|
||||
|
||||
#if !defined INI_NOBROWSE
|
||||
typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData);
|
||||
int ini_browse(INI_CALLBACK Callback, void *UserData, const mTCHAR *Filename);
|
||||
#endif /* INI_NOBROWSE */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined __cplusplus
|
||||
|
||||
#if defined __WXWINDOWS__
|
||||
#include "wxMinIni.h"
|
||||
#else
|
||||
#include <string>
|
||||
|
||||
/* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */
|
||||
class minIni
|
||||
{
|
||||
public:
|
||||
minIni(const std::string& filename) : iniFilename(filename)
|
||||
{ }
|
||||
|
||||
bool getbool(const std::string& Section, const std::string& Key, bool DefValue=false) const
|
||||
{ return ini_getbool(Section.c_str(), Key.c_str(), int(DefValue), iniFilename.c_str()) != 0; }
|
||||
|
||||
long getl(const std::string& Section, const std::string& Key, long DefValue=0) const
|
||||
{ return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); }
|
||||
|
||||
int geti(const std::string& Section, const std::string& Key, int DefValue=0) const
|
||||
{ return static_cast<int>(this->getl(Section, Key, long(DefValue))); }
|
||||
|
||||
std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string getsection(int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string getkey(const std::string& Section, int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getkey(Section.c_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#if defined INI_REAL
|
||||
INI_REAL getf(const std::string& Section, const std::string& Key, INI_REAL DefValue=0) const
|
||||
{ return ini_getf(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); }
|
||||
#endif
|
||||
|
||||
#if ! defined INI_READONLY
|
||||
bool put(const std::string& Section, const std::string& Key, long Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, int Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, bool Value)
|
||||
{ return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, const std::string& Value)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; }
|
||||
|
||||
bool put(const std::string& Section, const std::string& Key, const char* Value)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
|
||||
#if defined INI_REAL
|
||||
bool put(const std::string& Section, const std::string& Key, INI_REAL Value)
|
||||
{ return ini_putf(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
bool del(const std::string& Section, const std::string& Key)
|
||||
{ return ini_puts(Section.c_str(), Key.c_str(), 0, iniFilename.c_str()) != 0; }
|
||||
|
||||
bool del(const std::string& Section)
|
||||
{ return ini_puts(Section.c_str(), 0, 0, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
#if !defined INI_NOBROWSE
|
||||
bool browse(INI_CALLBACK Callback, void *UserData) const
|
||||
{ return ini_browse(Callback, UserData, iniFilename.c_str()) != 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
std::string iniFilename;
|
||||
};
|
||||
|
||||
#endif /* __WXWINDOWS__ */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* MININI_H */
|
||||
@@ -1,117 +0,0 @@
|
||||
/* Simple test program
|
||||
*
|
||||
* gcc -o test test.c minIni.c
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "minIni.h"
|
||||
|
||||
#define sizearray(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
const char inifile[] = "test.ini";
|
||||
const char inifile2[] = "testplain.ini";
|
||||
|
||||
int Callback(const char *section, const char *key, const char *value, void *userdata)
|
||||
{
|
||||
(void)userdata; /* this parameter is not used in this example */
|
||||
printf(" [%s]\t%s=%s\n", section, key, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char str[100];
|
||||
long n;
|
||||
int s, k;
|
||||
char section[50];
|
||||
|
||||
/* string reading */
|
||||
n = ini_gets("first", "string", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==4 && strcmp(str,"noot")==0);
|
||||
n = ini_gets("second", "string", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==4 && strcmp(str,"mies")==0);
|
||||
n = ini_gets("first", "undefined", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==5 && strcmp(str,"dummy")==0);
|
||||
/* ----- */
|
||||
n = ini_gets("", "string", "dummy", str, sizearray(str), inifile2);
|
||||
assert(n==4 && strcmp(str,"noot")==0);
|
||||
n = ini_gets(NULL, "string", "dummy", str, sizearray(str), inifile2);
|
||||
assert(n==4 && strcmp(str,"noot")==0);
|
||||
/* ----- */
|
||||
printf("1. String reading tests passed\n");
|
||||
|
||||
/* value reading */
|
||||
n = ini_getl("first", "val", -1, inifile);
|
||||
assert(n==1);
|
||||
n = ini_getl("second", "val", -1, inifile);
|
||||
assert(n==2);
|
||||
n = ini_getl("first", "undefined", -1, inifile);
|
||||
assert(n==-1);
|
||||
/* ----- */
|
||||
n = ini_getl(NULL, "val", -1, inifile2);
|
||||
assert(n==1);
|
||||
/* ----- */
|
||||
printf("2. Value reading tests passed\n");
|
||||
|
||||
/* string writing */
|
||||
n = ini_puts("first", "alt", "flagged as \"correct\"", inifile);
|
||||
assert(n==1);
|
||||
n = ini_gets("first", "alt", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==20 && strcmp(str,"flagged as \"correct\"")==0);
|
||||
/* ----- */
|
||||
n = ini_puts("second", "alt", "correct", inifile);
|
||||
assert(n==1);
|
||||
n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==7 && strcmp(str,"correct")==0);
|
||||
/* ----- */
|
||||
n = ini_puts("third", "test", "correct", inifile);
|
||||
assert(n==1);
|
||||
n = ini_gets("third", "test", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==7 && strcmp(str,"correct")==0);
|
||||
/* ----- */
|
||||
n = ini_puts("second", "alt", "overwrite", inifile);
|
||||
assert(n==1);
|
||||
n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==9 && strcmp(str,"overwrite")==0);
|
||||
/* ----- */
|
||||
n = ini_puts("second", "alt", "123456789", inifile);
|
||||
assert(n==1);
|
||||
n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile);
|
||||
assert(n==9 && strcmp(str,"123456789")==0);
|
||||
/* ----- */
|
||||
n = ini_puts(NULL, "alt", "correct", inifile2);
|
||||
assert(n==1);
|
||||
n = ini_gets(NULL, "alt", "dummy", str, sizearray(str), inifile2);
|
||||
assert(n==7 && strcmp(str,"correct")==0);
|
||||
/* ----- */
|
||||
printf("3. String writing tests passed\n");
|
||||
|
||||
/* section/key enumeration */
|
||||
printf("4. Section/key enumeration, file structure follows\n");
|
||||
for (s = 0; ini_getsection(s, section, sizearray(section), inifile) > 0; s++) {
|
||||
printf(" [%s]\n", section);
|
||||
for (k = 0; ini_getkey(section, k, str, sizearray(str), inifile) > 0; k++) {
|
||||
printf("\t%s\n", str);
|
||||
} /* for */
|
||||
} /* for */
|
||||
|
||||
/* browsing through the file */
|
||||
printf("5. browse through all settings, file field list follows\n");
|
||||
ini_browse(Callback, NULL, inifile);
|
||||
|
||||
/* string deletion */
|
||||
n = ini_puts("first", "alt", NULL, inifile);
|
||||
assert(n==1);
|
||||
n = ini_puts("second", "alt", NULL, inifile);
|
||||
assert(n==1);
|
||||
n = ini_puts("third", NULL, NULL, inifile);
|
||||
assert(n==1);
|
||||
/* ----- */
|
||||
n = ini_puts(NULL, "alt", NULL, inifile2);
|
||||
assert(n==1);
|
||||
printf("6. String deletion tests passed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[First]
|
||||
String=noot # trailing commment
|
||||
Val=1
|
||||
|
||||
[Second]
|
||||
Val = 2
|
||||
#comment=3
|
||||
String = mies
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
gcc -o minIni.o -c minIni.c
|
||||
g++ -o test2.o -c test2.cc
|
||||
g++ -o test2 test2.o minIni.o
|
||||
./test2
|
||||
*/
|
||||
|
||||
|
||||
#include <assert>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std ;
|
||||
|
||||
#include "minIni.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
minIni ini("test.ini");
|
||||
string s;
|
||||
|
||||
/* string reading */
|
||||
s = ini.gets( "first", "string" , "aap" );
|
||||
assert(s == "noot");
|
||||
s = ini.gets( "second", "string" , "aap" );
|
||||
assert(s == "mies");
|
||||
s = ini.gets( "first", "dummy" , "aap" );
|
||||
assert(s == "aap");
|
||||
cout << "1. String reading tests passed" << endl ;
|
||||
|
||||
|
||||
/* value reading */
|
||||
long n;
|
||||
n = ini.getl("first", "val", -1 );
|
||||
assert(n==1);
|
||||
n = ini.getl("second", "val", -1);
|
||||
assert(n==2);
|
||||
n = ini.getl("first", "dummy", -1);
|
||||
assert(n==-1);
|
||||
cout << "2. Value reading tests passed" << endl ;
|
||||
|
||||
|
||||
/* string writing */
|
||||
bool b;
|
||||
b = ini.put("first", "alt", "flagged as \"correct\"");
|
||||
assert(b);
|
||||
s = ini.gets("first", "alt", "aap");
|
||||
assert(s=="flagged as \"correct\"");
|
||||
|
||||
b = ini.put("second", "alt", "correct");
|
||||
assert(b);
|
||||
s = ini.gets("second", "alt", "aap");
|
||||
assert(s=="correct");
|
||||
|
||||
b = ini.put("third", "alt", "correct");
|
||||
assert(b);
|
||||
s = ini.gets("third", "alt", "aap" );
|
||||
assert(s=="correct");
|
||||
cout << "3. String writing tests passed" << endl;
|
||||
|
||||
/* section/key enumeration */
|
||||
cout << "4. section/key enumeration; file contents follows" << endl;
|
||||
string section;
|
||||
for (int is = 0; section = ini.getsection(is), section.length() > 0; is++) {
|
||||
cout << " [" << section.c_str() << "]" << endl;
|
||||
for (int ik = 0; s = ini.getkey(section, ik), s.length() > 0; ik++) {
|
||||
cout << "\t" << s.c_str() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* string deletion */
|
||||
b = ini.del("first", "alt");
|
||||
assert(b);
|
||||
b = ini.del("second", "alt");
|
||||
assert(b);
|
||||
b = ini.del("third");
|
||||
assert(b);
|
||||
cout << "5. string deletion passed " << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
String=noot # trailing commment
|
||||
#comment=3
|
||||
Val=1
|
||||
@@ -1,101 +0,0 @@
|
||||
/* minIni - Multi-Platform INI file parser, wxWidgets interface
|
||||
*
|
||||
* Copyright (c) CompuPhase, 2008-2012
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy
|
||||
* of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Version: $Id: wxMinIni.h 44 2012-01-04 15:52:56Z thiadmer.riemersma@gmail.com $
|
||||
*/
|
||||
#ifndef WXMININI_H
|
||||
#define WXMININI_H
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "minIni.h"
|
||||
|
||||
class minIni
|
||||
{
|
||||
public:
|
||||
minIni(const wxString& filename) : iniFilename(filename)
|
||||
{ }
|
||||
|
||||
bool getbool(const wxString& Section, const wxString& Key, bool DefValue=false) const
|
||||
{ return ini_getbool(Section.utf8_str(), Key.utf8_str(), int(DefValue), iniFilename.utf8_str()) != 0; }
|
||||
|
||||
long getl(const wxString& Section, const wxString& Key, long DefValue=0) const
|
||||
{ return ini_getl(Section.utf8_str(), Key.utf8_str(), DefValue, iniFilename.utf8_str()); }
|
||||
|
||||
int geti(const wxString& Section, const wxString& Key, int DefValue=0) const
|
||||
{ return static_cast<int>(ini_getl(Section.utf8_str(), Key.utf8_str(), (long)DefValue, iniFilename.utf8_str())); }
|
||||
|
||||
wxString gets(const wxString& Section, const wxString& Key, const wxString& DefValue=wxT("")) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_gets(Section.utf8_str(), Key.utf8_str(), DefValue.utf8_str(), buffer, INI_BUFFERSIZE, iniFilename.utf8_str());
|
||||
wxString result = wxString::FromUTF8(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
wxString getsection(int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.utf8_str());
|
||||
wxString result = wxString::FromUTF8(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
wxString getkey(const wxString& Section, int idx) const
|
||||
{
|
||||
char buffer[INI_BUFFERSIZE];
|
||||
ini_getkey(Section.utf8_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.utf8_str());
|
||||
wxString result = wxString::FromUTF8(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined INI_REAL
|
||||
INI_REAL getf(const wxString& Section, wxString& Key, INI_REAL DefValue=0) const
|
||||
{ return ini_getf(Section.utf8_str(), Key.utf8_str(), DefValue, iniFilename.utf8_str()); }
|
||||
#endif
|
||||
|
||||
#if ! defined INI_READONLY
|
||||
bool put(const wxString& Section, const wxString& Key, long Value) const
|
||||
{ return ini_putl(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; }
|
||||
|
||||
bool put(const wxString& Section, const wxString& Key, int Value) const
|
||||
{ return ini_putl(Section.utf8_str(), Key.utf8_str(), (long)Value, iniFilename.utf8_str()) != 0; }
|
||||
|
||||
bool put(const wxString& Section, const wxString& Key, bool Value) const
|
||||
{ return ini_putl(Section.utf8_str(), Key.utf8_str(), (long)Value, iniFilename.utf8_str()) != 0; }
|
||||
|
||||
bool put(const wxString& Section, const wxString& Key, const wxString& Value) const
|
||||
{ return ini_puts(Section.utf8_str(), Key.utf8_str(), Value.utf8_str(), iniFilename.utf8_str()) != 0; }
|
||||
|
||||
bool put(const wxString& Section, const wxString& Key, const char* Value) const
|
||||
{ return ini_puts(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; }
|
||||
|
||||
#if defined INI_REAL
|
||||
bool put(const wxString& Section, const wxString& Key, INI_REAL Value) const
|
||||
{ return ini_putf(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; }
|
||||
#endif
|
||||
|
||||
bool del(const wxString& Section, const wxString& Key) const
|
||||
{ return ini_puts(Section.utf8_str(), Key.utf8_str(), 0, iniFilename.utf8_str()) != 0; }
|
||||
|
||||
bool del(const wxString& Section) const
|
||||
{ return ini_puts(Section.utf8_str(), 0, 0, iniFilename.utf8_str()) != 0; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
wxString iniFilename;
|
||||
};
|
||||
|
||||
#endif /* WXMININI_H */
|
||||
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "../dev/minIni.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
13
Source/sys-clk-OC/sysmodule/lib/nxExt/.gitignore
vendored
13
Source/sys-clk-OC/sysmodule/lib/nxExt/.gitignore
vendored
@@ -1,13 +0,0 @@
|
||||
# Editor files
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# Objects
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Lib
|
||||
lib
|
||||
release
|
||||
debug
|
||||
@@ -1,132 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
SOURCES := src
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
|
||||
|
||||
CFLAGS := -g -Wall -Werror \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
$(ARCH) \
|
||||
$(BUILD_CFLAGS)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
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)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
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 := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
.PHONY: clean all lib/lib$(TARGET).a lib/lib$(TARGET)d.a
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: lib/lib$(TARGET).a lib/lib$(TARGET)d.a
|
||||
|
||||
lib:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
release:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
debug:
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
lib/lib$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
|
||||
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
|
||||
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
|
||||
DEPSDIR=$(CURDIR)/release \
|
||||
--no-print-directory -C release \
|
||||
-f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr release debug lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT) : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nxExt/apm_ext.h"
|
||||
#include "nxExt/ipc_server.h"
|
||||
#include "nxExt/cpp/lockable_mutex.h"
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
Result apmExtInitialize(void);
|
||||
void apmExtExit(void);
|
||||
|
||||
// Silently fail
|
||||
Result apmExtSysRequestPerformanceMode(u32 mode);
|
||||
Result apmExtSysSetCpuBoostMode(u32 mode);
|
||||
|
||||
Result apmExtGetPerformanceMode(u32 *out_mode);
|
||||
Result apmExtGetCurrentPerformanceConfiguration(u32 *out_conf);
|
||||
|
||||
inline bool apmExtIsCPUBoosted(u32 conf_id) { // CPU boosted to 1785 MHz
|
||||
return (conf_id == 0x92220009 || conf_id == 0x9222000A);
|
||||
};
|
||||
inline bool apmExtIsBoostMode(u32 conf_id) { // GPU throttled to 76.8 MHz
|
||||
return (conf_id >= 0x92220009 && conf_id <= 0x9222000C);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <mutex>
|
||||
#include <switch.h>
|
||||
|
||||
class LockableMutex
|
||||
{
|
||||
public:
|
||||
LockableMutex()
|
||||
{
|
||||
mutexInit(&this->m);
|
||||
}
|
||||
|
||||
virtual ~LockableMutex() {}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
mutexLock(&this->m);
|
||||
}
|
||||
|
||||
bool TryLock()
|
||||
{
|
||||
return mutexTryLock(&this->m);
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
mutexUnlock(&this->m);
|
||||
}
|
||||
|
||||
// snake_case aliases in order to implement Lockable
|
||||
|
||||
void lock()
|
||||
{
|
||||
this->Lock();
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return this->TryLock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
this->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex m;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#define IPC_SERVER_EXT_RESPONSE_MAX_DATA_SIZE (0x100 - 0x10 - sizeof(IpcServerRawHeader))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 magic;
|
||||
union
|
||||
{
|
||||
u64 cmdId;
|
||||
u64 result;
|
||||
};
|
||||
} IpcServerRawHeader;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SmServiceName srvName;
|
||||
Handle handles[MAX_WAIT_OBJECTS];
|
||||
u32 max;
|
||||
u32 count;
|
||||
} IpcServer;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u64 cmdId;
|
||||
void* ptr;
|
||||
size_t size;
|
||||
} IpcServerRequestData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HipcParsedRequest hipc;
|
||||
IpcServerRequestData data;
|
||||
} IpcServerRequest;
|
||||
|
||||
typedef Result (*IpcServerRequestHandler)(void* userdata, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize);
|
||||
|
||||
Result ipcServerInit(IpcServer* server, const char* name, u32 max_sessions);
|
||||
Result ipcServerExit(IpcServer* server);
|
||||
Result ipcServerProcess(IpcServer* server, IpcServerRequestHandler handler, void* userdata);
|
||||
Result ipcServerParseCommand(const IpcServerRequest* r, size_t *out_datasize, void** out_data, u64* out_cmd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "nxExt/apm_ext.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
static Service g_apmSrv;
|
||||
static Service g_apmSysSrv;
|
||||
static atomic_size_t g_refCnt;
|
||||
|
||||
Result apmExtInitialize(void)
|
||||
{
|
||||
g_refCnt++;
|
||||
|
||||
if (serviceIsActive(&g_apmSrv))
|
||||
return 0;
|
||||
|
||||
Result rc = smGetService(&g_apmSrv, "apm");
|
||||
|
||||
if(R_SUCCEEDED(rc))
|
||||
rc = smGetService(&g_apmSysSrv, "apm:sys");
|
||||
|
||||
if (R_FAILED(rc))
|
||||
apmExtExit();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void apmExtExit(void)
|
||||
{
|
||||
if (--g_refCnt == 0)
|
||||
{
|
||||
serviceClose(&g_apmSrv);
|
||||
serviceClose(&g_apmSysSrv);
|
||||
}
|
||||
}
|
||||
|
||||
Result apmExtSysSetCpuBoostMode(u32 mode)
|
||||
{
|
||||
return serviceDispatchIn(&g_apmSysSrv, 6, mode);
|
||||
}
|
||||
|
||||
Result apmExtGetPerformanceMode(u32 *out_mode)
|
||||
{
|
||||
return serviceDispatchOut(&g_apmSrv, 1, *out_mode);
|
||||
}
|
||||
|
||||
Result apmExtSysRequestPerformanceMode(u32 mode)
|
||||
{
|
||||
return serviceDispatchIn(&g_apmSysSrv, 0, mode);
|
||||
}
|
||||
|
||||
Result apmExtGetCurrentPerformanceConfiguration(u32 *out_conf)
|
||||
{
|
||||
return serviceDispatchOut(&g_apmSysSrv, 7, *out_conf);
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "nxExt/ipc_server.h"
|
||||
#include <string.h>
|
||||
|
||||
Result ipcServerInit(IpcServer* server, const char* name, u32 max_sessions)
|
||||
{
|
||||
if(max_sessions < 1 || max_sessions > (MAX_WAIT_OBJECTS - 1))
|
||||
{
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
}
|
||||
|
||||
server->srvName = smEncodeName(name);
|
||||
server->max = max_sessions + 1;
|
||||
server->count = 0;
|
||||
|
||||
Result rc = smRegisterService(&server->handles[0], server->srvName, false, max_sessions);
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
server->count = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result ipcServerExit(IpcServer* server)
|
||||
{
|
||||
for(u32 i = 0; i < server->count; i++)
|
||||
{
|
||||
svcCloseHandle(server->handles[i]);
|
||||
}
|
||||
server->count = 0;
|
||||
return smUnregisterService(server->srvName);
|
||||
}
|
||||
|
||||
static Result _ipcServerAddSession(IpcServer* server, Handle session)
|
||||
{
|
||||
if(server->count >= server->max)
|
||||
{
|
||||
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
|
||||
}
|
||||
|
||||
server->handles[server->count] = session;
|
||||
server->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result _ipcServerDeleteSession(IpcServer* server, u32 index)
|
||||
{
|
||||
if(!index || index >= server->count)
|
||||
{
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
}
|
||||
|
||||
svcCloseHandle(server->handles[index]);
|
||||
|
||||
for(u32 j = index; j < (server->count - 1); j++)
|
||||
{
|
||||
server->handles[j] = server->handles[j + 1];
|
||||
}
|
||||
server->count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Result _ipcServerParseRequest(IpcServerRequest* r)
|
||||
{
|
||||
u8* base = armGetTls();
|
||||
|
||||
r->hipc = hipcParseRequest(base);
|
||||
r->data.cmdId = 0;
|
||||
r->data.size = 0;
|
||||
r->data.ptr = NULL;
|
||||
|
||||
if(r->hipc.meta.type == CmifCommandType_Request)
|
||||
{
|
||||
IpcServerRawHeader* header = cmifGetAlignedDataStart(r->hipc.data.data_words, base);
|
||||
size_t dataSize = r->hipc.meta.num_data_words * 4;
|
||||
|
||||
if(!header || dataSize < sizeof(IpcServerRawHeader) || header->magic != CMIF_IN_HEADER_MAGIC)
|
||||
{
|
||||
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
||||
}
|
||||
|
||||
r->data.cmdId = header->cmdId;
|
||||
if(dataSize > sizeof(IpcServerRawHeader))
|
||||
{
|
||||
r->data.size = dataSize - sizeof(IpcServerRawHeader);
|
||||
r->data.ptr = ((u8*)header) + sizeof(IpcServerRawHeader);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _ipcServerPrepareResponse(Result rc, void* data, size_t dataSize)
|
||||
{
|
||||
u8* base = armGetTls();
|
||||
HipcRequest hipc = hipcMakeRequestInline(base,
|
||||
.type = CmifCommandType_Request,
|
||||
.num_data_words = (sizeof(IpcServerRawHeader) + dataSize + 0x10) / 4,
|
||||
);
|
||||
|
||||
IpcServerRawHeader* rawHeader = cmifGetAlignedDataStart(hipc.data_words, base);
|
||||
rawHeader->magic = CMIF_OUT_HEADER_MAGIC;
|
||||
rawHeader->result = rc;
|
||||
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
memcpy(((u8*)rawHeader) + sizeof(IpcServerRawHeader), data, dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
static Result _ipcServerProcessNewSession(IpcServer* server)
|
||||
{
|
||||
Handle session;
|
||||
Result rc = svcAcceptSession(&session, server->handles[0]);
|
||||
if(R_SUCCEEDED(rc) && R_FAILED(rc = _ipcServerAddSession(server, session)))
|
||||
{
|
||||
svcCloseHandle(session);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _ipcServerProcessSession(IpcServer* server, IpcServerRequestHandler handler, void* userdata, u32 handleIndex)
|
||||
{
|
||||
s32 unusedIndex;
|
||||
IpcServerRequest r;
|
||||
size_t dataSize = 0;
|
||||
u8 data[IPC_SERVER_EXT_RESPONSE_MAX_DATA_SIZE];
|
||||
bool close = false;
|
||||
|
||||
Result rc = svcReplyAndReceive(&unusedIndex, &server->handles[handleIndex], 1, 0, UINT64_MAX);
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = _ipcServerParseRequest(&r);
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
switch(r.hipc.meta.type)
|
||||
{
|
||||
case CmifCommandType_Request:
|
||||
_ipcServerPrepareResponse(
|
||||
handler(userdata, &r, data, &dataSize),
|
||||
data,
|
||||
dataSize
|
||||
);
|
||||
break;
|
||||
case CmifCommandType_Close:
|
||||
_ipcServerPrepareResponse(0, NULL, 0);
|
||||
close = true;
|
||||
break;
|
||||
default:
|
||||
_ipcServerPrepareResponse(MAKERESULT(11, 403), NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
rc = svcReplyAndReceive(&unusedIndex, &server->handles[handleIndex], 0, server->handles[handleIndex], 0);
|
||||
if(rc == KERNELRESULT(TimedOut))
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(R_FAILED(rc) || close)
|
||||
{
|
||||
_ipcServerDeleteSession(server, handleIndex);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result ipcServerProcess(IpcServer* server, IpcServerRequestHandler handler, void* userdata)
|
||||
{
|
||||
s32 handleIndex = -1;
|
||||
Result rc = svcWaitSynchronization(&handleIndex, server->handles, server->count, UINT64_MAX);
|
||||
|
||||
if(R_SUCCEEDED(rc) && (handleIndex < 0 || handleIndex >= server->count))
|
||||
{
|
||||
rc = MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||
}
|
||||
|
||||
if(R_SUCCEEDED(rc))
|
||||
{
|
||||
if(handleIndex)
|
||||
{
|
||||
rc = _ipcServerProcessSession(server, handler, userdata, handleIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = _ipcServerProcessNewSession(server);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"name": "sysclkOC",
|
||||
"title_id": "0x00FF0000636C6BFF",
|
||||
"title_id_range_min": "0x00FF0000636C6BFF",
|
||||
"title_id_range_max": "0x00FF0000636C6BFF",
|
||||
"main_thread_stack_size": "0x00004000",
|
||||
"main_thread_priority": 49,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 3,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": [
|
||||
"*"
|
||||
],
|
||||
"service_host": [
|
||||
"sysclkOC"
|
||||
],
|
||||
"kernel_capabilities": [
|
||||
{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 0,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"svcSetThreadActivity": "0x32",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcCreateInterruptEvent": "0x53",
|
||||
"svcReadWriteRegister": "0x4E",
|
||||
"svcQueryIoMapping": "0x55",
|
||||
"svcCreateDeviceAddressSpace": "0x56",
|
||||
"svcAttachDeviceAddressSpace": "0x57",
|
||||
"svcDetachDeviceAddressSpace": "0x58",
|
||||
"svcMapDeviceAddressSpaceAligned": "0x5a",
|
||||
"svcUnmapDeviceAddressSpace": "0x5c",
|
||||
"svcGetSystemInfo": "0x6f",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0060"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <nxExt.h>
|
||||
#include "errors.h"
|
||||
#include "clock_manager.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include "process_management.h"
|
||||
#include <cstring>
|
||||
|
||||
ClockManager* ClockManager::instance = NULL;
|
||||
|
||||
ClockManager* ClockManager::GetInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ClockManager::Exit()
|
||||
{
|
||||
if(instance)
|
||||
{
|
||||
delete instance;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::Initialize()
|
||||
{
|
||||
if(!instance)
|
||||
{
|
||||
instance = new ClockManager();
|
||||
}
|
||||
}
|
||||
|
||||
ClockManager::ClockManager()
|
||||
{
|
||||
this->config = Config::CreateDefault();
|
||||
this->context = new SysClkContext;
|
||||
this->context->applicationId = 0;
|
||||
this->context->profile = SysClkProfile_Handheld;
|
||||
this->context->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->context->freqs[i] = 0;
|
||||
this->context->overrideFreqs[i] = 0;
|
||||
}
|
||||
this->context->perfConfId = 0;
|
||||
this->running = false;
|
||||
this->lastTempLogNs = 0;
|
||||
this->lastCsvWriteNs = 0;
|
||||
|
||||
this->oc = new SysClkOcExtra;
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
this->oc->batteryChargingDisabledOverride = false;
|
||||
this->oc->realProfile = SysClkProfile_Handheld;
|
||||
|
||||
this->rnxSync = new ReverseNXSync;
|
||||
this->governor = new Governor();
|
||||
}
|
||||
|
||||
ClockManager::~ClockManager()
|
||||
{
|
||||
delete this->governor;
|
||||
delete this->rnxSync;
|
||||
delete this->oc;
|
||||
delete this->context;
|
||||
delete this->config;
|
||||
}
|
||||
|
||||
void ClockManager::SetRunning(bool running)
|
||||
{
|
||||
this->running = running;
|
||||
}
|
||||
|
||||
bool ClockManager::Running()
|
||||
{
|
||||
return this->running;
|
||||
}
|
||||
|
||||
uint32_t ClockManager::GetHz(SysClkModule module)
|
||||
{
|
||||
/* Temp override setting */
|
||||
uint32_t hz = this->context->overrideFreqs[module];
|
||||
|
||||
/* Per-Game setting */
|
||||
if (!hz)
|
||||
hz = this->config->GetAutoClockHz(this->context->applicationId, module, this->context->profile);
|
||||
|
||||
/* Global profile */
|
||||
if (!hz)
|
||||
hz = this->config->GetAutoClockHz(SYSCLK_GLOBAL_PROFILE_TID, module, this->context->profile);
|
||||
|
||||
/* Return pre-set hz */
|
||||
ReverseNXMode mode;
|
||||
if (!hz && (mode = this->rnxSync->GetMode()))
|
||||
{
|
||||
switch (module)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
hz = 1020'000'000;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
hz = (mode == ReverseNX_Docked ||
|
||||
this->oc->realProfile == SysClkProfile_Docked) ?
|
||||
768'000'000 : 460'800'000;
|
||||
break;
|
||||
case SysClkModule_MEM:
|
||||
hz = MEM_CLOCK_DOCK;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hz)
|
||||
{
|
||||
/* Considering realProfile frequency limit */
|
||||
hz = Clocks::GetNearestHz(module, this->oc->realProfile, hz);
|
||||
}
|
||||
|
||||
/* Handle CPU Auto Boost, no user-defined hz required */
|
||||
if (module == SysClkModule_CPU)
|
||||
{
|
||||
if (this->oc->systemCoreBoostCPU && hz < Clocks::boostCpuFreq)
|
||||
return Clocks::boostCpuFreq;
|
||||
if (!hz)
|
||||
/* Trigger RefreshContext() and Tick(), resetting default CPU frequency */
|
||||
return 1020'000'000;
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
void ClockManager::Tick()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
|
||||
if (this->RefreshContext() && this->context->enabled)
|
||||
{
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
uint32_t hz = GetHz((SysClkModule)module);
|
||||
|
||||
if (module == SysClkModule_CPU) {
|
||||
this->governor->SetMinHz(*Clocks::freqRange[module].first, SysClkModule_CPU);
|
||||
if (Clocks::GetIsMariko() && hz > (uint32_t)1020'000'000) {
|
||||
this->governor->SetMinHz(1020'000'000, SysClkModule_CPU);
|
||||
}
|
||||
}
|
||||
|
||||
this->governor->SetMaxHz(hz, (SysClkModule)module);
|
||||
|
||||
if (hz && hz != this->context->freqs[module] && !this->governor->IsHandledByGovernor((SysClkModule)module))
|
||||
{
|
||||
// Skip setting CPU or GPU clocks in CpuBoostMode if CPU <= boostCPUFreq or GPU >= 76.8MHz
|
||||
bool skipBoost = apmExtIsBoostMode(this->context->perfConfId) &&
|
||||
((module == SysClkModule_CPU && hz <= Clocks::boostCpuFreq) || module == SysClkModule_GPU);
|
||||
|
||||
if (!skipBoost) {
|
||||
FileUtils::LogLine("[mgr] %s clock set : %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
Clocks::SetHz((SysClkModule)module, hz);
|
||||
uint32_t hz_now = Clocks::GetCurrentHz((SysClkModule)module);
|
||||
if (hz != hz_now)
|
||||
FileUtils::LogLine("[mgr] Cannot set %s clock to %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
this->context->freqs[module] = hz_now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::WaitForNextTick()
|
||||
{
|
||||
/* Self-check system core (#3) usage via idleticks at intervals (Not enabled at higher CPU freq or without charger) */
|
||||
uint64_t tickWaitTimeMs = this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs);
|
||||
|
||||
if (this->governor->IsHandledByGovernor(SysClkModule_CPU)) {
|
||||
svcSleepThread(tickWaitTimeMs * 1000'000ULL);
|
||||
return;
|
||||
}
|
||||
|
||||
bool boostOK =
|
||||
this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost) &&
|
||||
this->context->enabled &&
|
||||
this->oc->realProfile != SysClkProfile_Handheld &&
|
||||
this->context->freqs[SysClkModule_CPU] <= Clocks::boostCpuFreq;
|
||||
|
||||
if (boostOK) {
|
||||
uint32_t core3Util = CpuCoreUtil(3, tickWaitTimeMs * 1000'000ULL).Get();
|
||||
bool lastBoost = this->oc->systemCoreBoostCPU;
|
||||
this->oc->systemCoreBoostCPU = (core3Util >= BOOST_THRESHOLD);
|
||||
|
||||
if (lastBoost && !this->oc->systemCoreBoostCPU)
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
|
||||
if (!lastBoost && this->oc->systemCoreBoostCPU)
|
||||
Clocks::SetHz(SysClkModule_CPU, Clocks::boostCpuFreq);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->oc->systemCoreBoostCPU) {
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
}
|
||||
svcSleepThread(tickWaitTimeMs * 1000'000ULL);
|
||||
}
|
||||
|
||||
bool ClockManager::RefreshContext()
|
||||
{
|
||||
PsmExt::ChargingHandler(this->GetInstance());
|
||||
|
||||
bool hasChanged = false;
|
||||
SysClkProfile realProfile = Clocks::GetCurrentProfile();
|
||||
if (realProfile != this->oc->realProfile)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Profile change: %s", Clocks::GetProfileName(realProfile, true));
|
||||
this->oc->realProfile = realProfile;
|
||||
// Signal that power state has been changed, reset the override
|
||||
this->SetBatteryChargingDisabledOverride(false);
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
hasChanged = this->config->Refresh();
|
||||
if (hasChanged) {
|
||||
this->rnxSync->ToggleSync(this->GetConfig()->GetConfigValue(SysClkConfigValue_SyncReverseNXMode));
|
||||
bool allowUnsafe = this->GetConfig()->GetConfigValue(SysClkConfigValue_AllowUnsafeFrequencies);
|
||||
Clocks::SetAllowUnsafe(allowUnsafe);
|
||||
|
||||
this->governor->SetAutoCPUBoost(this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost));
|
||||
this->governor->SetCPUBoostHz(Clocks::GetNearestHz(SysClkModule_CPU, this->oc->realProfile, Clocks::boostCpuFreq));
|
||||
}
|
||||
|
||||
bool enabled = this->GetConfig()->Enabled();
|
||||
if(enabled != this->context->enabled)
|
||||
{
|
||||
this->context->enabled = enabled;
|
||||
FileUtils::LogLine("[mgr] " TARGET " status: %s", enabled ? "enabled" : "disabled");
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId);
|
||||
this->context->applicationId = applicationId;
|
||||
hasChanged = true;
|
||||
|
||||
/* Clear ReverseNX state */
|
||||
this->rnxSync->Reset(applicationId);
|
||||
}
|
||||
|
||||
SysClkOcGovernorConfig governorConfig = SysClkOcGovernorConfig_AllDisabled;
|
||||
if (this->GetConfig()->GetConfigValue(SysClkConfigValue_GovernorExperimental)) {
|
||||
auto governorHandheldOnly = this->GetConfig()->GetConfigValue(SysClkConfigValue_GovernorHandheldOnly);
|
||||
governorConfig = SysClkOcGovernorConfig_Default;
|
||||
SysClkOcGovernorConfig governorConfigTitle = this->GetConfig()->GetTitleGovernorConfig(applicationId);
|
||||
if (governorConfig != governorConfigTitle)
|
||||
governorConfig = governorConfigTitle;
|
||||
// Set governor config to disabled if Handheld Only is true
|
||||
if (governorHandheldOnly && (realProfile != SysClkProfile_Handheld))
|
||||
governorConfig = SysClkOcGovernorConfig_AllDisabled;
|
||||
}
|
||||
this->governor->SetConfig(governorConfig);
|
||||
|
||||
/* Update PerformanceConfigurationId */
|
||||
{
|
||||
uint32_t confId = 0;
|
||||
Result rc = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
if (this->context->perfConfId != confId)
|
||||
{
|
||||
this->context->perfConfId = confId;
|
||||
this->governor->SetPerfConf(confId);
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SysClkProfile current = this->context->profile;
|
||||
SysClkProfile expected = this->rnxSync->GetProfile(this->oc->realProfile);
|
||||
this->context->profile = expected;
|
||||
if (current != expected)
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
// let ptm module handle boost clocks rather than resetting
|
||||
if (hasChanged && !apmExtIsBoostMode(this->context->perfConfId)) {
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
|
||||
std::uint32_t hz = 0;
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
hz = Clocks::GetCurrentHz((SysClkModule)module);
|
||||
|
||||
if (hz != 0 && hz != this->context->freqs[module])
|
||||
{
|
||||
this->context->freqs[module] = hz;
|
||||
if (!this->governor->IsHandledByGovernor((SysClkModule)module)) {
|
||||
FileUtils::LogLine("[mgr] %s clock change: %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
hz = this->GetConfig()->GetOverrideHz((SysClkModule)module);
|
||||
if (hz != this->context->overrideFreqs[module])
|
||||
{
|
||||
if(hz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override change: %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override disabled", Clocks::GetModuleName((SysClkModule)module, true));
|
||||
Clocks::ResetToStock(module);
|
||||
}
|
||||
this->context->overrideFreqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// temperatures do not and should not force a refresh, hasChanged untouched
|
||||
std::uint32_t millis = 0;
|
||||
std::uint64_t ns = armTicksToNs(armGetSystemTick());
|
||||
std::uint64_t tempLogInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_TempLogIntervalMs) * 1000000ULL;
|
||||
bool shouldLogTemp = tempLogInterval && ((ns - this->lastTempLogNs) > tempLogInterval);
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
millis = Clocks::GetTemperatureMilli((SysClkThermalSensor)sensor);
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s temp: %u.%u °C", Clocks::GetThermalSensorName((SysClkThermalSensor)sensor, true), millis/1000, (millis - millis/1000*1000) / 100);
|
||||
}
|
||||
this->context->temps[sensor] = millis;
|
||||
}
|
||||
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
this->lastTempLogNs = ns;
|
||||
}
|
||||
|
||||
std::uint64_t csvWriteInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_CsvWriteIntervalMs) * 1000000ULL;
|
||||
|
||||
if(csvWriteInterval && ((ns - this->lastCsvWriteNs) > csvWriteInterval))
|
||||
{
|
||||
FileUtils::WriteContextToCsv(this->context);
|
||||
this->lastCsvWriteNs = ns;
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
void ClockManager::SetRNXRTMode(ReverseNXMode mode) {
|
||||
this->rnxSync->SetRTMode(mode);
|
||||
}
|
||||
|
||||
SysClkContext ClockManager::GetCurrentContext()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
return *this->context;
|
||||
}
|
||||
|
||||
Config* ClockManager::GetConfig()
|
||||
{
|
||||
return this->config;
|
||||
}
|
||||
|
||||
bool ClockManager::GetBatteryChargingDisabledOverride() {
|
||||
return this->oc->batteryChargingDisabledOverride;
|
||||
}
|
||||
|
||||
Result ClockManager::SetBatteryChargingDisabledOverride(bool toggle_true) {
|
||||
this->oc->batteryChargingDisabledOverride = toggle_true;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <sysclk.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "clocks.h"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
#include "oc_extra.h"
|
||||
|
||||
// Forward declaration
|
||||
class ReverseNXSync;
|
||||
class Governor;
|
||||
|
||||
class ClockManager
|
||||
{
|
||||
public:
|
||||
static ClockManager* GetInstance();
|
||||
static void Initialize();
|
||||
static void Exit();
|
||||
|
||||
void SetRunning(bool running);
|
||||
bool Running();
|
||||
void Tick();
|
||||
void WaitForNextTick();
|
||||
void SetRNXRTMode(ReverseNXMode mode);
|
||||
SysClkContext GetCurrentContext();
|
||||
Config* GetConfig();
|
||||
bool GetBatteryChargingDisabledOverride();
|
||||
Result SetBatteryChargingDisabledOverride(bool toggle_true);
|
||||
|
||||
protected:
|
||||
ClockManager();
|
||||
virtual ~ClockManager();
|
||||
|
||||
bool RefreshContext();
|
||||
uint32_t GetHz(SysClkModule);
|
||||
|
||||
static ClockManager *instance;
|
||||
std::atomic_bool running;
|
||||
LockableMutex contextMutex;
|
||||
Config *config;
|
||||
SysClkContext *context;
|
||||
std::uint64_t lastTempLogNs;
|
||||
std::uint64_t lastCsvWriteNs;
|
||||
|
||||
SysClkOcExtra *oc;
|
||||
ReverseNXSync *rnxSync;
|
||||
Governor *governor;
|
||||
};
|
||||
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <nxExt.h>
|
||||
#include "clocks.h"
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
Result Clocks::GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min, uint32_t** max)
|
||||
{
|
||||
switch (module) {
|
||||
case SysClkModule_CPU:
|
||||
case SysClkModule_GPU:
|
||||
case SysClkModule_MEM:
|
||||
*min = freqRange[module].min;
|
||||
*max = freqRange[module].max[profile];
|
||||
break;
|
||||
default:
|
||||
ERROR_THROW("No such PcvModule: %u", module);
|
||||
}
|
||||
|
||||
if (!*min || !*max || *max < *min || size_t(*max - *min + 1) > sizeof(SysClkFrequencyTable))
|
||||
return SYSCLK_ERROR(InternalFrequencyTableError);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Clocks::UpdateFreqRange() {
|
||||
freqRange[SysClkModule_MEM].InitDefault(SysClkModule_MEM);
|
||||
if (isMariko) {
|
||||
freqRange[SysClkModule_MEM].first = freqRange[SysClkModule_MEM].min = freqRange[SysClkModule_MEM].FindFreq(1600'000'000);
|
||||
}
|
||||
|
||||
freqRange[SysClkModule_CPU].InitDefault(SysClkModule_CPU);
|
||||
uint32_t* cpu_max_freq = freqRange[SysClkModule_CPU].last;
|
||||
uint32_t CPU_SAFE_MAX = isMariko ? 1963'500'000 : 1785'000'000;
|
||||
uint32_t CPU_UNSAFE_MAX[SysClkProfile_EnumMax];
|
||||
for (auto &m : CPU_UNSAFE_MAX) {
|
||||
m = *cpu_max_freq;
|
||||
}
|
||||
if (!isMariko) {
|
||||
CPU_UNSAFE_MAX[SysClkProfile_Handheld] = 1785'000'000;
|
||||
}
|
||||
|
||||
freqRange[SysClkModule_GPU].InitDefault(SysClkModule_GPU);
|
||||
uint32_t* gpu_max_freq = freqRange[SysClkModule_GPU].last;
|
||||
uint32_t GPU_SAFE_MAX[SysClkProfile_EnumMax];
|
||||
if (isMariko) {
|
||||
for (auto &m : GPU_SAFE_MAX) {
|
||||
m = 998'400'000;
|
||||
}
|
||||
} else {
|
||||
GPU_SAFE_MAX[SysClkProfile_Handheld] = \
|
||||
GPU_SAFE_MAX[SysClkProfile_HandheldCharging] = 460'800'000;
|
||||
GPU_SAFE_MAX[SysClkProfile_HandheldChargingUSB] = 768'000'000;
|
||||
GPU_SAFE_MAX[SysClkProfile_HandheldChargingOfficial] = \
|
||||
GPU_SAFE_MAX[SysClkProfile_Docked] = 921'600'000;
|
||||
};
|
||||
uint32_t GPU_UNSAFE_MAX[SysClkProfile_EnumMax];
|
||||
for (auto &m : GPU_UNSAFE_MAX) {
|
||||
m = *gpu_max_freq;
|
||||
}
|
||||
if (isMariko) {
|
||||
GPU_UNSAFE_MAX[SysClkProfile_Handheld] = 998'400'000;
|
||||
} else {
|
||||
memcpy(GPU_UNSAFE_MAX, GPU_SAFE_MAX, sizeof(GPU_UNSAFE_MAX));
|
||||
GPU_UNSAFE_MAX[SysClkProfile_HandheldChargingOfficial] = \
|
||||
GPU_UNSAFE_MAX[SysClkProfile_Docked] = *gpu_max_freq;
|
||||
}
|
||||
|
||||
const bool use_unsafe = allowUnsafe;
|
||||
for (int i = 0; i < int(SysClkProfile_EnumMax); i++) {
|
||||
freqRange[SysClkModule_CPU].max[i] = std::min(cpu_max_freq,
|
||||
freqRange[SysClkModule_CPU].FindFreq(use_unsafe ? CPU_UNSAFE_MAX[i] : CPU_SAFE_MAX, SysClkProfile(i)));
|
||||
freqRange[SysClkModule_GPU].max[i] = std::min(gpu_max_freq,
|
||||
freqRange[SysClkModule_GPU].FindFreq(use_unsafe ? GPU_UNSAFE_MAX[i] : GPU_SAFE_MAX[i], SysClkProfile(i)));
|
||||
}
|
||||
}
|
||||
|
||||
Result Clocks::GetTable(SysClkModule module, SysClkProfile profile, SysClkFrequencyTable* out_table) {
|
||||
uint32_t* min = NULL;
|
||||
uint32_t* max = NULL;
|
||||
if (Result res = GetRange(module, profile, &min, &max)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
memset(out_table, 0, sizeof(SysClkFrequencyTable));
|
||||
uint32_t* p = min;
|
||||
size_t idx = 0;
|
||||
while(p <= max)
|
||||
out_table->freq[idx++] = *p++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Clocks::SetAllowUnsafe(bool allow) {
|
||||
if (allowUnsafe != allow) {
|
||||
allowUnsafe = allow;
|
||||
UpdateFreqRange();
|
||||
}
|
||||
};
|
||||
|
||||
void Clocks::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
u64 hardware_type = 0;
|
||||
rc = splInitialize();
|
||||
ASSERT_RESULT_OK(rc, "splInitialize");
|
||||
rc = splGetConfig(SplConfigItem_HardwareType, &hardware_type);
|
||||
ASSERT_RESULT_OK(rc, "splGetConfig");
|
||||
splExit();
|
||||
|
||||
switch (hardware_type) {
|
||||
case 0: // Icosa
|
||||
case 1: // Copper
|
||||
isMariko = false;
|
||||
break;
|
||||
case 2: // Hoag
|
||||
case 3: // Iowa
|
||||
case 4: // Calcio
|
||||
case 5: // Aula
|
||||
isMariko = true;
|
||||
break;
|
||||
default:
|
||||
ERROR_THROW("Unknown hardware type: 0x%X!", hardware_type);
|
||||
return;
|
||||
}
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
rc = clkrstInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pcvInitialize");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pcvInitialize");
|
||||
}
|
||||
|
||||
rc = apmExtInitialize();
|
||||
ASSERT_RESULT_OK(rc, "apmExtInitialize");
|
||||
|
||||
rc = psmInitialize();
|
||||
ASSERT_RESULT_OK(rc, "psmInitialize");
|
||||
|
||||
rc = tsInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tsInitialize");
|
||||
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
rc = tcInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tcInitialize");
|
||||
}
|
||||
|
||||
FileUtils::ParseLoaderKip();
|
||||
|
||||
UpdateFreqRange();
|
||||
}
|
||||
|
||||
void Clocks::Exit()
|
||||
{
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
pcvExit();
|
||||
}
|
||||
else
|
||||
{
|
||||
clkrstExit();
|
||||
}
|
||||
|
||||
apmExtExit();
|
||||
psmExit();
|
||||
tsExit();
|
||||
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
tcExit();
|
||||
}
|
||||
}
|
||||
|
||||
const char* Clocks::GetModuleName(SysClkModule module, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatModule(module, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkModule: %u", module);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* Clocks::GetProfileName(SysClkProfile profile, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatProfile(profile, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkProfile: %u", profile);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* Clocks::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatThermalSensor(sensor, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkThermalSensor: %u", sensor);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PcvModule Clocks::GetPcvModule(SysClkModule sysclkModule)
|
||||
{
|
||||
switch(sysclkModule)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
return PcvModule_CpuBus;
|
||||
case SysClkModule_GPU:
|
||||
return PcvModule_GPU;
|
||||
case SysClkModule_MEM:
|
||||
return PcvModule_EMC;
|
||||
default:
|
||||
ERROR_THROW("No such SysClkModule: %u", sysclkModule);
|
||||
}
|
||||
|
||||
return (PcvModule)0;
|
||||
}
|
||||
|
||||
PcvModuleId Clocks::GetPcvModuleId(SysClkModule sysclkModule)
|
||||
{
|
||||
PcvModuleId pcvModuleId;
|
||||
Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(sysclkModule));
|
||||
ASSERT_RESULT_OK(rc, "pcvGetModuleId");
|
||||
|
||||
return pcvModuleId;
|
||||
}
|
||||
|
||||
SysClkApmConfiguration* Clocks::GetEmbeddedApmConfig(uint32_t confId)
|
||||
{
|
||||
SysClkApmConfiguration* apmConfiguration = NULL;
|
||||
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++)
|
||||
{
|
||||
if(sysclk_g_apm_configurations[i].id == confId)
|
||||
{
|
||||
apmConfiguration = &sysclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!apmConfiguration)
|
||||
{
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
return apmConfiguration;
|
||||
}
|
||||
|
||||
uint32_t Clocks::GetStockClock(SysClkApmConfiguration* apm, SysClkModule module)
|
||||
{
|
||||
switch (module) {
|
||||
case SysClkModule_CPU:
|
||||
return apm->cpu_hz;
|
||||
case SysClkModule_GPU:
|
||||
return apm->gpu_hz;
|
||||
case SysClkModule_MEM:
|
||||
return GetIsMariko() ? MEM_CLOCK_MARIKO_MIN : apm->mem_hz;
|
||||
default:
|
||||
ERROR_THROW("Unknown SysClkModule: %x", module);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::ResetToStock(unsigned int module)
|
||||
{
|
||||
if(hosversionAtLeast(9,0,0))
|
||||
{
|
||||
std::uint32_t confId = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
SysClkApmConfiguration* apmConfiguration = GetEmbeddedApmConfig(confId);
|
||||
|
||||
if (module == SysClkModule_EnumMax || module == SysClkModule_CPU)
|
||||
{
|
||||
Clocks::SetHz(SysClkModule_CPU, GetStockClock(apmConfiguration, SysClkModule_CPU));
|
||||
}
|
||||
if (module == SysClkModule_EnumMax || module == SysClkModule_GPU)
|
||||
{
|
||||
Clocks::SetHz(SysClkModule_GPU, GetStockClock(apmConfiguration, SysClkModule_GPU));
|
||||
}
|
||||
if (module == SysClkModule_EnumMax || module == SysClkModule_MEM)
|
||||
{
|
||||
Clocks::SetHz(SysClkModule_MEM, GetStockClock(apmConfiguration, SysClkModule_MEM));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint32_t mode = 0;
|
||||
rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
rc = apmExtSysRequestPerformanceMode(mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
|
||||
}
|
||||
}
|
||||
|
||||
SysClkProfile Clocks::GetCurrentProfile()
|
||||
{
|
||||
std::uint32_t mode = 0;
|
||||
Result rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
if(mode)
|
||||
{
|
||||
return SysClkProfile_Docked;
|
||||
}
|
||||
|
||||
PsmChargerType chargerType;
|
||||
|
||||
rc = psmGetChargerType(&chargerType);
|
||||
ASSERT_RESULT_OK(rc, "psmGetChargerType");
|
||||
|
||||
switch(chargerType)
|
||||
{
|
||||
case PsmChargerType_EnoughPower:
|
||||
return SysClkProfile_HandheldChargingOfficial;
|
||||
case PsmChargerType_LowPower:
|
||||
case PsmChargerType_NotSupported:
|
||||
return SysClkProfile_HandheldChargingUSB;
|
||||
default:
|
||||
return SysClkProfile_Handheld;
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::SetHz(SysClkModule module, std::uint32_t hz)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
ClkrstSession session = {0};
|
||||
|
||||
rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
rc = clkrstSetClockRate(&session, hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvSetClockRate(Clocks::GetPcvModule(module), hz);
|
||||
ASSERT_RESULT_OK(rc, "pcvSetClockRate");
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetCurrentHz(SysClkModule module)
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint32_t hz = 0;
|
||||
|
||||
if(hosversionAtLeast(8,0,0))
|
||||
{
|
||||
ClkrstSession session = {0};
|
||||
|
||||
rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
|
||||
rc = clkrstGetClockRate(&session, &hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstGetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = pcvGetClockRate(Clocks::GetPcvModule(module), &hz);
|
||||
ASSERT_RESULT_OK(rc, "pcvGetClockRate");
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz)
|
||||
{
|
||||
uint32_t *min = nullptr, *max = nullptr;
|
||||
if (GetRange(module, profile, &min, &max))
|
||||
ERROR_THROW("table lookup failed for SysClkModule: %u", module);
|
||||
|
||||
return *GetNearestHzPtr(min, max, inHz);
|
||||
}
|
||||
|
||||
std::int32_t Clocks::GetTsTemperatureMilli(TsLocation location)
|
||||
{
|
||||
Result rc;
|
||||
std::int32_t millis = 0;
|
||||
|
||||
if(hosversionAtLeast(14,0,0))
|
||||
{
|
||||
rc = tsGetTemperature(location, &millis);
|
||||
ASSERT_RESULT_OK(rc, "tsGetTemperature(%u)", location);
|
||||
millis *= 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = tsGetTemperatureMilliC(location, &millis);
|
||||
ASSERT_RESULT_OK(rc, "tsGetTemperatureMilliC(%u)", location);
|
||||
}
|
||||
|
||||
return millis;
|
||||
}
|
||||
|
||||
std::uint32_t Clocks::GetTemperatureMilli(SysClkThermalSensor sensor)
|
||||
{
|
||||
std::int32_t millis = 0;
|
||||
|
||||
if(sensor == SysClkThermalSensor_SOC)
|
||||
{
|
||||
millis = GetTsTemperatureMilli(TsLocation_External);
|
||||
}
|
||||
else if(sensor == SysClkThermalSensor_PCB)
|
||||
{
|
||||
millis = GetTsTemperatureMilli(TsLocation_Internal);
|
||||
}
|
||||
else if(sensor == SysClkThermalSensor_Skin)
|
||||
{
|
||||
if(hosversionAtLeast(5,0,0))
|
||||
{
|
||||
Result rc = tcGetSkinTemperatureMilliC(&millis);
|
||||
ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_THROW("No such SysClkThermalSensor: %u", sensor);
|
||||
}
|
||||
|
||||
return std::max(0, millis);
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <switch.h>
|
||||
#include <sysclk.h>
|
||||
|
||||
#define MAX_MEM_CLOCK 1862'400'000
|
||||
#define MEM_CLOCK_MARIKO_MIN 1600'000'000
|
||||
#define MEM_CLOCK_DOCK 1600'000'000
|
||||
#define BOOST_THRESHOLD 95'0
|
||||
|
||||
class Clocks
|
||||
{
|
||||
public:
|
||||
static inline uint32_t boostCpuFreq = 1785000000;
|
||||
static inline uint32_t maxMemFreq = 0;
|
||||
|
||||
static inline uint32_t* GetNearestHzPtr(uint32_t* hzListStart, uint32_t* hzListEnd, uint32_t freq) {
|
||||
uint32_t* p = hzListStart;
|
||||
while (p < hzListEnd) {
|
||||
if (freq <= *p)
|
||||
return p;
|
||||
p++;
|
||||
}
|
||||
return hzListEnd;
|
||||
}
|
||||
|
||||
static inline SysClkFrequencyTable freqTable[SysClkModule_EnumMax] = {
|
||||
{}, // CPU
|
||||
{}, // GPU
|
||||
{ // MEM
|
||||
665600000,
|
||||
800000000,
|
||||
1065600000,
|
||||
1331200000,
|
||||
1600000000,
|
||||
},
|
||||
};
|
||||
|
||||
typedef struct FreqRange {
|
||||
uint32_t* first;
|
||||
uint32_t* last;
|
||||
uint32_t* min;
|
||||
uint32_t* max[SysClkProfile_EnumMax];
|
||||
|
||||
void InitDefault(SysClkModule module) {
|
||||
SysClkFrequencyTable* table_head = &freqTable[module];
|
||||
uint32_t* p = this->first = this->min = &table_head->freq[0];
|
||||
|
||||
// Get pointer to last value
|
||||
for (int i = 0; i < FREQ_TABLE_MAX_ENTRY_COUNT; i++) {
|
||||
if (!*(++p)) {
|
||||
--p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->last = p;
|
||||
for (auto& m: this->max) {
|
||||
m = p;
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t* FindFreq(uint32_t freq, SysClkProfile profile = SysClkProfile_Docked) {
|
||||
return GetNearestHzPtr(this->min, this->max[profile], freq);
|
||||
};
|
||||
} FreqRange;
|
||||
|
||||
static inline FreqRange freqRange[SysClkModule_EnumMax];
|
||||
static void UpdateFreqRange();
|
||||
|
||||
static Result GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min, uint32_t** max);
|
||||
static Result GetTable(SysClkModule module, SysClkProfile profile, SysClkFrequencyTable* out_table);
|
||||
static void SetAllowUnsafe(bool allow);
|
||||
static bool GetIsMariko() { return isMariko; };
|
||||
static void Exit();
|
||||
static void Initialize();
|
||||
static SysClkApmConfiguration* GetEmbeddedApmConfig(uint32_t confId);
|
||||
static uint32_t GetStockClock(SysClkApmConfiguration* apm, SysClkModule module);
|
||||
static void ResetToStock(unsigned int module = SysClkModule_EnumMax);
|
||||
static SysClkProfile GetCurrentProfile();
|
||||
static std::uint32_t GetCurrentHz(SysClkModule module);
|
||||
static void SetHz(SysClkModule module, std::uint32_t hz);
|
||||
static const char* GetProfileName(SysClkProfile profile, bool pretty);
|
||||
static const char* GetModuleName(SysClkModule module, bool pretty);
|
||||
static const char* GetThermalSensorName(SysClkThermalSensor sensor, bool pretty);
|
||||
static std::uint32_t GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz);
|
||||
static std::uint32_t GetTemperatureMilli(SysClkThermalSensor sensor);
|
||||
|
||||
protected:
|
||||
static inline bool allowUnsafe;
|
||||
static inline bool isMariko;
|
||||
static std::int32_t GetTsTemperatureMilli(TsLocation location);
|
||||
static PcvModule GetPcvModule(SysClkModule sysclkModule);
|
||||
static PcvModuleId GetPcvModuleId(SysClkModule sysclkModule);
|
||||
static std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);
|
||||
};
|
||||
@@ -1,538 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "errors.h"
|
||||
#include "clocks.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
Config::Config(std::string path)
|
||||
{
|
||||
this->path = path;
|
||||
this->loaded = false;
|
||||
this->profileMhzMap = std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>();
|
||||
this->profileCountMap = std::map<std::uint64_t, std::uint8_t>();
|
||||
this->profileGovernorMap = std::map<std::uint64_t, SysClkOcGovernorConfig>();
|
||||
this->mtime = 0;
|
||||
this->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->overrideFreqs[i] = 0;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
this->Close();
|
||||
}
|
||||
|
||||
Config *Config::CreateDefault()
|
||||
{
|
||||
//if (R_FAILED(FileUtils::mkdir_p(FILE_CONFIG_DIR)))
|
||||
// ERROR_THROW("Cannot create " FILE_CONFIG_DIR);
|
||||
|
||||
return new Config(FILE_CONFIG_DIR "/config.ini");
|
||||
}
|
||||
|
||||
void Config::Load()
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Reading %s", this->path.c_str());
|
||||
|
||||
this->Close();
|
||||
this->mtime = this->CheckModificationTime();
|
||||
if(!this->mtime)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error finding file");
|
||||
}
|
||||
else if (!ini_browse(&BrowseIniFunc, this, this->path.c_str()))
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Error loading file");
|
||||
}
|
||||
|
||||
// Erista: Disable Mariko only features
|
||||
// if (!Clocks::GetIsMariko()) { }
|
||||
|
||||
this->loaded = true;
|
||||
}
|
||||
|
||||
void Config::Close()
|
||||
{
|
||||
this->loaded = false;
|
||||
this->profileMhzMap.clear();
|
||||
this->profileCountMap.clear();
|
||||
this->profileGovernorMap.clear();
|
||||
|
||||
for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++)
|
||||
{
|
||||
this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::Refresh()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
if (!this->loaded || this->mtime != this->CheckModificationTime())
|
||||
{
|
||||
this->Load();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Config::HasProfilesLoaded()
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
return this->loaded;
|
||||
}
|
||||
|
||||
time_t Config::CheckModificationTime()
|
||||
{
|
||||
time_t mtime = 0;
|
||||
struct stat st;
|
||||
if (stat(this->path.c_str(), &st) == 0)
|
||||
{
|
||||
mtime = st.st_mtime;
|
||||
}
|
||||
|
||||
return mtime;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockMhz(std::uint64_t tid, SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
if (this->loaded)
|
||||
{
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t>::const_iterator it = this->profileMhzMap.find(std::make_tuple(tid, profile, module));
|
||||
if (it != this->profileMhzMap.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles)
|
||||
{
|
||||
std::uint32_t mhz = 0;
|
||||
|
||||
if (this->loaded)
|
||||
{
|
||||
for(auto profile: profiles)
|
||||
{
|
||||
mhz = FindClockMhz(tid, module, profile);
|
||||
|
||||
if(mhz)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::max((std::uint32_t)0, mhz * 1000000);
|
||||
}
|
||||
|
||||
std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
switch(profile)
|
||||
{
|
||||
case SysClkProfile_Handheld:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld});
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld});
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld});
|
||||
case SysClkProfile_Docked:
|
||||
return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked});
|
||||
default:
|
||||
ERROR_THROW("Unhandled SysClkProfile: %u", profile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SysClkOcGovernorConfig Config::GetTitleGovernorConfig(std::uint64_t tid)
|
||||
{
|
||||
if (this->loaded)
|
||||
{
|
||||
std::map<uint64_t, SysClkOcGovernorConfig>::const_iterator it = this->profileGovernorMap.find(tid);
|
||||
if (it != this->profileGovernorMap.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
return SysClkOcGovernorConfig_Default;
|
||||
}
|
||||
|
||||
void Config::GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
out_profiles->mhzMap[profile][module] = FindClockMhz(tid, (SysClkModule)module, (SysClkProfile)profile);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<uint64_t, SysClkOcGovernorConfig>::const_iterator it = this->profileGovernorMap.find(tid);
|
||||
SysClkOcGovernorConfig governor = SysClkOcGovernorConfig_Default;
|
||||
// Found
|
||||
if (it != this->profileGovernorMap.end())
|
||||
governor = it->second;
|
||||
out_profiles->governorConfig = governor;
|
||||
}
|
||||
|
||||
bool Config::SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
// String pointer array passed to ini
|
||||
char* iniKeys[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) + 1 + 1];
|
||||
char* iniValues[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) + 1 + 1];
|
||||
|
||||
// Char arrays to build strings
|
||||
char keysStr[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) * 0x40];
|
||||
char valuesStr[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) * 0x10];
|
||||
char section[17] = {0};
|
||||
|
||||
// Iteration pointers
|
||||
char** ik = &iniKeys[0];
|
||||
char** iv = &iniValues[0];
|
||||
char* sk = &keysStr[0];
|
||||
char* sv = &valuesStr[0];
|
||||
std::uint32_t* mhz = &profiles->mhz[0];
|
||||
|
||||
snprintf(section, sizeof(section), "%016lX", tid);
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
numProfiles++;
|
||||
|
||||
// Put key and value as string
|
||||
snprintf(sk, 0x40, "%s_%s", Clocks::GetProfileName((SysClkProfile)profile, false), Clocks::GetModuleName((SysClkModule)module, false));
|
||||
snprintf(sv, 0x10, "%d", *mhz);
|
||||
|
||||
// Add them to the ini key/value str arrays
|
||||
*ik = sk;
|
||||
*iv = sv;
|
||||
ik++;
|
||||
iv++;
|
||||
|
||||
// We used those chars, get to the next ones
|
||||
sk += 0x40;
|
||||
sv += 0x10;
|
||||
}
|
||||
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
|
||||
if (profiles->governorConfig != SysClkOcGovernorConfig_Default) {
|
||||
snprintf(sk, 0x40, "%s", CONFIG_KEY_TITLE_GOVERNOR_CONFIG);
|
||||
snprintf(sv, 0x10, "%d", profiles->governorConfig);
|
||||
*ik++ = sk;
|
||||
*iv++ = sv;
|
||||
}
|
||||
|
||||
*ik = NULL;
|
||||
*iv = NULL;
|
||||
|
||||
if(!ini_putsection(section, (const char**)iniKeys, (const char**)iniValues, this->path.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only actually apply changes in memory after a succesful save
|
||||
if(immediate)
|
||||
{
|
||||
mhz = &profiles->mhz[0];
|
||||
this->profileCountMap[tid] = numProfiles;
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
if(*mhz)
|
||||
{
|
||||
this->profileMhzMap[std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)] = *mhz;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->profileMhzMap.erase(std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module));
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
|
||||
if (profiles->governorConfig == SysClkOcGovernorConfig_Default)
|
||||
this->profileGovernorMap.erase(tid);
|
||||
else
|
||||
this->profileGovernorMap[tid] = profiles->governorConfig;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint8_t Config::GetProfileCount(std::uint64_t tid)
|
||||
{
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = this->profileCountMap.find(tid);
|
||||
if (it == this->profileCountMap.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int Config::BrowseIniFunc(const char* section, const char* key, const char* value, void *userdata)
|
||||
{
|
||||
Config* config = (Config*)userdata;
|
||||
std::uint64_t input;
|
||||
if(!strcmp(section, CONFIG_VAL_SECTION))
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!strcmp(key, sysclkFormatConfigValue((SysClkConfigValue)kval, false)))
|
||||
{
|
||||
input = strtoul(value, NULL, 0);
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, input))
|
||||
{
|
||||
input = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
FileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
config->configValues[kval] = input;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t tid = strtoul(section, NULL, 16);
|
||||
|
||||
if(!tid || strlen(section) != 16)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(key, CONFIG_KEY_TITLE_GOVERNOR_CONFIG)) {
|
||||
input = strtoul(value, NULL, 0);
|
||||
if ((input & SysClkOcGovernorConfig_Mask) != input) {
|
||||
input = SysClkOcGovernorConfig_Default;
|
||||
FileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
config->profileGovernorMap[tid] = (SysClkOcGovernorConfig)input;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SysClkProfile parsedProfile = SysClkProfile_EnumMax;
|
||||
SysClkModule parsedModule = SysClkModule_EnumMax;
|
||||
|
||||
for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++)
|
||||
{
|
||||
const char* profileCode = Clocks::GetProfileName((SysClkProfile)profile, false);
|
||||
size_t profileCodeLen = strlen(profileCode);
|
||||
|
||||
if(!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_')
|
||||
{
|
||||
const char* subkey = key + profileCodeLen + 1;
|
||||
|
||||
for(unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
const char* moduleCode = Clocks::GetModuleName((SysClkModule)module, false);
|
||||
size_t moduleCodeLen = strlen(moduleCode);
|
||||
if(!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0')
|
||||
{
|
||||
parsedProfile = (SysClkProfile)profile;
|
||||
parsedModule = (SysClkModule)module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(parsedModule == SysClkModule_EnumMax || parsedProfile == SysClkProfile_EnumMax)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint32_t mhz = strtoul(value, NULL, 10);
|
||||
if(!mhz)
|
||||
{
|
||||
FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Mem freq > 1600'000'000 will be regarded as Clocks::maxMemFreq for consistency
|
||||
if (parsedModule == SysClkModule_MEM && mhz > 1600) {
|
||||
mhz = Clocks::maxMemFreq / 1000'000;
|
||||
}
|
||||
|
||||
config->profileMhzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz;
|
||||
std::map<std::uint64_t, std::uint8_t>::iterator it = config->profileCountMap.find(tid);
|
||||
if (it == config->profileCountMap.end())
|
||||
{
|
||||
config->profileCountMap[tid] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Config::SetEnabled(bool enabled)
|
||||
{
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
bool Config::Enabled()
|
||||
{
|
||||
return this->enabled;
|
||||
}
|
||||
|
||||
void Config::SetOverrideHz(SysClkModule module, std::uint32_t hz)
|
||||
{
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, module))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkModule: %u", module);
|
||||
}
|
||||
this->overrideFreqs[module] = hz;
|
||||
}
|
||||
|
||||
std::uint32_t Config::GetOverrideHz(SysClkModule module)
|
||||
{
|
||||
std::scoped_lock lock{this->overrideMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, module))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkModule: %u", module);
|
||||
}
|
||||
|
||||
return this->overrideFreqs[module];
|
||||
}
|
||||
|
||||
std::uint64_t Config::GetConfigValue(SysClkConfigValue kval)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
if(!SYSCLK_ENUM_VALID(SysClkConfigValue, kval))
|
||||
{
|
||||
ERROR_THROW("Unhandled SysClkConfigValue: %u", kval);
|
||||
}
|
||||
|
||||
return this->configValues[kval];
|
||||
}
|
||||
|
||||
const char* Config::GetConfigValueName(SysClkConfigValue kval, bool pretty)
|
||||
{
|
||||
const char* result = sysclkFormatConfigValue(kval, pretty);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
ERROR_THROW("No such SysClkConfigValue: %u", kval);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Config::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
out_configValues->values[kval] = this->configValues[kval];
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetConfigValues(SysClkConfigValueList* configValues, bool immediate)
|
||||
{
|
||||
std::scoped_lock lock{this->configMutex};
|
||||
|
||||
// String pointer array passed to ini
|
||||
const char* iniKeys[SysClkConfigValue_EnumMax + 1];
|
||||
char* iniValues[SysClkConfigValue_EnumMax + 1];
|
||||
|
||||
// char arrays to build strings
|
||||
char valuesStr[SysClkConfigValue_EnumMax * 0x20];
|
||||
|
||||
// Iteration pointers
|
||||
char* sv = &valuesStr[0];
|
||||
const char** ik = &iniKeys[0];
|
||||
char** iv = &iniValues[0];
|
||||
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(!sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]) || configValues->values[kval] == sysclkDefaultConfigValue((SysClkConfigValue)kval))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Put key and value as string
|
||||
// And add them to the ini key/value str arrays
|
||||
snprintf(sv, 0x20, "%ld", configValues->values[kval]);
|
||||
*ik = sysclkFormatConfigValue((SysClkConfigValue)kval, false);
|
||||
*iv = sv;
|
||||
|
||||
// We used those chars, get to the next ones
|
||||
sv += 0x20;
|
||||
ik++;
|
||||
iv++;
|
||||
}
|
||||
|
||||
*ik = NULL;
|
||||
*iv = NULL;
|
||||
|
||||
if(!ini_putsection(CONFIG_VAL_SECTION, (const char**)iniKeys, (const char**)iniValues, this->path.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only actually apply changes in memory after a succesful save
|
||||
if(immediate)
|
||||
{
|
||||
for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++)
|
||||
{
|
||||
if(sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]))
|
||||
{
|
||||
this->configValues[kval] = configValues->values[kval];
|
||||
}
|
||||
else
|
||||
{
|
||||
this->configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <initializer_list>
|
||||
#include <switch.h>
|
||||
#include <minIni.h>
|
||||
#include <nxExt.h>
|
||||
#include "clocks.h"
|
||||
|
||||
#define CONFIG_VAL_SECTION "values"
|
||||
|
||||
#define CONFIG_KEY_TITLE_GOVERNOR_CONFIG "governor_config"
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(std::string path);
|
||||
virtual ~Config();
|
||||
|
||||
static Config *CreateDefault();
|
||||
|
||||
bool Refresh();
|
||||
|
||||
bool HasProfilesLoaded();
|
||||
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid);
|
||||
void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles);
|
||||
bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate);
|
||||
std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
|
||||
SysClkOcGovernorConfig GetTitleGovernorConfig(std::uint64_t tid);
|
||||
|
||||
void SetEnabled(bool enabled);
|
||||
bool Enabled();
|
||||
void SetOverrideHz(SysClkModule module, std::uint32_t hz);
|
||||
std::uint32_t GetOverrideHz(SysClkModule module);
|
||||
|
||||
std::uint64_t GetConfigValue(SysClkConfigValue val);
|
||||
const char* GetConfigValueName(SysClkConfigValue val, bool pretty);
|
||||
void GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
bool SetConfigValues(SysClkConfigValueList* configValues, bool immediate);
|
||||
protected:
|
||||
void Load();
|
||||
void Close();
|
||||
|
||||
time_t CheckModificationTime();
|
||||
std::uint32_t FindClockMhz(std::uint64_t tid, SysClkModule module, SysClkProfile profile);
|
||||
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list<SysClkProfile> profiles);
|
||||
static int BrowseIniFunc(const char* section, const char* key, const char* value, void *userdata);
|
||||
|
||||
std::map<std::tuple<std::uint64_t, SysClkProfile, SysClkModule>, std::uint32_t> profileMhzMap;
|
||||
std::map<std::uint64_t, std::uint8_t> profileCountMap;
|
||||
std::map<std::uint64_t, SysClkOcGovernorConfig> profileGovernorMap;
|
||||
bool loaded;
|
||||
std::string path;
|
||||
time_t mtime;
|
||||
LockableMutex configMutex;
|
||||
LockableMutex overrideMutex;
|
||||
std::atomic_bool enabled;
|
||||
std::uint32_t overrideFreqs[SysClkModule_EnumMax];
|
||||
std::uint64_t configValues[SysClkConfigValue_EnumMax];
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "errors.h"
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
|
||||
void Errors::ThrowException(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const char *msg = Errors::FormatMessage(format, args);
|
||||
va_end(args);
|
||||
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
const char *Errors::FormatMessage(const char *format, va_list args)
|
||||
{
|
||||
size_t len = vsnprintf(NULL, 0, format, args) * sizeof(char);
|
||||
char *buf = (char *)malloc(len + 1);
|
||||
if (buf == NULL)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
vsnprintf(buf, len + 1, format, args);
|
||||
|
||||
return buf;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ERROR_THROW(format, ...) Errors::ThrowException(format "\n in %s:%u", ##__VA_ARGS__, __FILE__, __LINE__)
|
||||
#else
|
||||
#define ERROR_THROW(format, ...) Errors::ThrowException(format, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define ERROR_RESULT_THROW(rc, format, ...) ERROR_THROW(format "\n RC: [0x%x] %04d-%04d", ##__VA_ARGS__, rc, R_MODULE(rc), R_DESCRIPTION(rc))
|
||||
#define ASSERT_RESULT_OK(rc, format, ...) \
|
||||
if (R_FAILED(rc)) \
|
||||
{ \
|
||||
ERROR_RESULT_THROW(rc, "ASSERT_RESULT_OK: " format, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
class Errors
|
||||
{
|
||||
public:
|
||||
static void ThrowException(const char *format, ...);
|
||||
|
||||
protected:
|
||||
static const char *FormatMessage(const char *format, va_list args);
|
||||
};
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include <dirent.h>
|
||||
#include <nxExt.h>
|
||||
#include "errors.h"
|
||||
|
||||
static LockableMutex g_log_mutex;
|
||||
static LockableMutex g_csv_mutex;
|
||||
static std::atomic_bool g_has_initialized = false;
|
||||
static bool g_log_enabled = false;
|
||||
static std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
extern "C" void __libnx_init_time(void);
|
||||
|
||||
static void _FileUtils_InitializeThreadFunc(void *args)
|
||||
{
|
||||
FileUtils::Initialize();
|
||||
}
|
||||
|
||||
bool FileUtils::IsInitialized()
|
||||
{
|
||||
return g_has_initialized;
|
||||
}
|
||||
|
||||
void FileUtils::LogLine(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_has_initialized)
|
||||
{
|
||||
g_log_mutex.Lock();
|
||||
|
||||
FileUtils::RefreshFlags(false);
|
||||
|
||||
if(g_log_enabled)
|
||||
{
|
||||
FILE *file = fopen(FILE_LOG_FILE_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
struct tm* nowTm = localtime(&now.tv_sec);
|
||||
|
||||
fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ", nowTm->tm_year+1900, nowTm->tm_mon+1, nowTm->tm_mday, nowTm->tm_hour, nowTm->tm_min, nowTm->tm_sec, now.tv_nsec / 1000000UL);
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
g_log_mutex.Unlock();
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void FileUtils::WriteContextToCsv(const SysClkContext* context)
|
||||
{
|
||||
std::scoped_lock lock{g_csv_mutex};
|
||||
|
||||
FILE *file = fopen(FILE_CONTEXT_CSV_PATH, "a");
|
||||
|
||||
if (file)
|
||||
{
|
||||
// Print header
|
||||
if(!ftell(file))
|
||||
{
|
||||
fprintf(file, "timestamp,profile,app_tid");
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%s_hz", sysclkFormatModule((SysClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%s_milliC", sysclkFormatThermalSensor((SysClkThermalSensor)sensor, false));
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, sysclkFormatProfile(context->profile, false), context->applicationId);
|
||||
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
fprintf(file, ",%d", context->freqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
fprintf(file, ",%d", context->temps[sensor]);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void FileUtils::RefreshFlags(bool force)
|
||||
{
|
||||
std::uint64_t now = armTicksToNs(armGetSystemTick());
|
||||
if(!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file = fopen(FILE_LOG_FLAG_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_log_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_log_enabled = false;
|
||||
}
|
||||
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
void FileUtils::InitializeAsync()
|
||||
{
|
||||
Thread initThread = {0};
|
||||
threadCreate(&initThread, _FileUtils_InitializeThreadFunc, NULL, NULL, 0x2000, 0x15, 0);
|
||||
threadStart(&initThread);
|
||||
}
|
||||
|
||||
Result FileUtils::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = timeInitialize();
|
||||
}
|
||||
|
||||
__libnx_init_time();
|
||||
timeExit();
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsInitialize();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
rc = fsdevMountSdmc();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
FileUtils::RefreshFlags(true);
|
||||
g_has_initialized = true;
|
||||
FileUtils::LogLine("=== " TARGET " " TARGET_VERSION " ===");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void FileUtils::Exit()
|
||||
{
|
||||
if (!g_has_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_has_initialized = false;
|
||||
g_log_enabled = false;
|
||||
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
|
||||
void FileUtils::ParseLoaderKip() {
|
||||
const char* dirs[] = { "/", "/atmosphere/", "/atmosphere/kips/", "/bootloader/" };
|
||||
char* full_path = new char[0x200];
|
||||
SCOPE_EXIT { delete[] full_path; };
|
||||
|
||||
for (auto const& dir : dirs) {
|
||||
struct dirent *entry = NULL;
|
||||
DIR *dp = opendir(dir);
|
||||
if (!dp)
|
||||
continue;
|
||||
SCOPE_EXIT { closedir(dp); };
|
||||
|
||||
while ((entry = readdir(dp))) {
|
||||
if (entry->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
snprintf(full_path, 0x200, "%s%s", dir, entry->d_name);
|
||||
|
||||
FILE* fp = fopen(full_path, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long res = ftell(fp);
|
||||
fclose(fp);
|
||||
if (res == -1)
|
||||
continue;
|
||||
|
||||
size_t filesize = (size_t)res;
|
||||
if (filesize < 0x1000 || filesize > 512 * 1024)
|
||||
continue;
|
||||
|
||||
const char kip_ext[] = {'.', 'k', 'i', 'p'};
|
||||
size_t file_name_len = strnlen(reinterpret_cast<const char*>(&entry->d_name), 256);
|
||||
const char* file_ext = &entry->d_name[file_name_len - sizeof(kip_ext)];
|
||||
|
||||
if (strncasecmp((const char*)kip_ext, file_ext, sizeof(kip_ext)))
|
||||
continue;
|
||||
|
||||
if (R_SUCCEEDED(CustParser(full_path, filesize))) {
|
||||
LogLine("Parsed cust config from \"%s\"", full_path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_THROW("Cannot locate loader.kip in /, /atmosphere/, /atmosphere/kips/ and /bootloader/");
|
||||
}
|
||||
|
||||
Result FileUtils::CustParser(const char* filepath, size_t filesize) {
|
||||
enum ParseError {
|
||||
ParseError_Success = 0,
|
||||
ParseError_OpenReadFailed,
|
||||
ParseError_WrongKipMagic,
|
||||
ParseError_CustNotFound,
|
||||
ParseError_WrongCustRev,
|
||||
};
|
||||
|
||||
FILE* fp = fopen(filepath, "r");
|
||||
if (!fp)
|
||||
return ParseError_OpenReadFailed;
|
||||
SCOPE_EXIT { fclose(fp); };
|
||||
|
||||
constexpr uint8_t KIP_MAGIC[] = {'K', 'I', 'P', '1', 'L', 'o', 'a', 'd', 'e', 'r'};
|
||||
constexpr size_t BLOCK_SIZE = 0x1000;
|
||||
|
||||
char* tmp_block = new char[BLOCK_SIZE];
|
||||
SCOPE_EXIT { delete[] tmp_block; };
|
||||
fread(tmp_block, sizeof(char), BLOCK_SIZE, fp);
|
||||
|
||||
if (memcmp(KIP_MAGIC, tmp_block, sizeof(KIP_MAGIC)))
|
||||
return ParseError_WrongKipMagic;
|
||||
|
||||
CustTable table {};
|
||||
|
||||
fpos_t cust_pos = 0;
|
||||
long block_pos = 0;
|
||||
while ((block_pos = ftell(fp)) >= 0 && (size_t)block_pos < filesize) {
|
||||
for (size_t i = 0; i < BLOCK_SIZE; i += sizeof(table.cust)) {
|
||||
if (memcmp(table.cust, &tmp_block[i], sizeof(table.cust)))
|
||||
continue;
|
||||
|
||||
fgetpos(fp, &cust_pos);
|
||||
cust_pos = cust_pos + i - BLOCK_SIZE;
|
||||
goto found;
|
||||
}
|
||||
fread(tmp_block, sizeof(char), BLOCK_SIZE, fp);
|
||||
}
|
||||
|
||||
found:
|
||||
if (!cust_pos)
|
||||
return ParseError_CustNotFound;
|
||||
|
||||
memset(reinterpret_cast<void*>(&table), 0, sizeof(CustTable));
|
||||
fsetpos(fp, &cust_pos);
|
||||
if (!fread(reinterpret_cast<char*>(&table), 1, sizeof(CustTable), fp))
|
||||
return ParseError_OpenReadFailed;
|
||||
|
||||
if (table.custRev != CUST_REV)
|
||||
return ParseError_WrongCustRev;
|
||||
|
||||
if (table.commonCpuBoostClock)
|
||||
Clocks::boostCpuFreq = table.commonCpuBoostClock * 1000;
|
||||
|
||||
CustomizeCpuDvfsTable* cpu_dvfs_table = nullptr;
|
||||
CustomizeGpuDvfsTable* gpu_dvfs_table = nullptr;
|
||||
|
||||
if (Clocks::GetIsMariko()) {
|
||||
if (table.marikoEmcMaxClock)
|
||||
Clocks::maxMemFreq = table.marikoEmcMaxClock * 1000;
|
||||
if (table.marikoEmcVddqVolt && table.marikoEmcVddqVolt >= 550'000 && table.marikoEmcVddqVolt <= 650'000) {
|
||||
u32 mvolt = table.marikoEmcVddqVolt / 1000;
|
||||
Result res = I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDDQ, mvolt);
|
||||
LogLine("Set EMC Vddq volt to %u mV: %s", mvolt, R_FAILED(res) ? "Failed" : "OK");
|
||||
}
|
||||
if (table.commonEmcMemVolt && table.commonEmcMemVolt >= 1100'000 && table.commonEmcMemVolt <= 1250'000) {
|
||||
u32 mvolt = table.commonEmcMemVolt / 1000;
|
||||
Result res = I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDD2, mvolt);
|
||||
LogLine("Set MEM Vdd2 volt to %u mV: %s", mvolt, R_FAILED(res) ? "Failed" : "OK");
|
||||
}
|
||||
|
||||
cpu_dvfs_table = table.marikoCpuUV ? &table.marikoCpuDvfsTableSLT : &table.marikoCpuDvfsTable;
|
||||
switch (table.marikoGpuUV) {
|
||||
case 0:
|
||||
gpu_dvfs_table = &table.marikoGpuDvfsTable;
|
||||
break;
|
||||
case 1:
|
||||
gpu_dvfs_table = &table.marikoGpuDvfsTableSLT;
|
||||
break;
|
||||
case 2:
|
||||
gpu_dvfs_table = &table.marikoGpuDvfsTableHiOPT;
|
||||
break;
|
||||
default:
|
||||
gpu_dvfs_table = &table.marikoGpuDvfsTable;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (table.eristaEmcMaxClock)
|
||||
Clocks::maxMemFreq = table.eristaEmcMaxClock * 1000;
|
||||
|
||||
cpu_dvfs_table = &table.eristaCpuDvfsTable;
|
||||
gpu_dvfs_table = &table.eristaGpuDvfsTable;
|
||||
}
|
||||
|
||||
// Fill Clocks::freqTable
|
||||
cvb_entry_t* cpu_dvfs_entry = reinterpret_cast<cvb_entry_t *>(cpu_dvfs_table);
|
||||
for (size_t i = 0, j = 0; i < FREQ_TABLE_MAX_ENTRY_COUNT; i++) {
|
||||
// Skip CPU frequencies < 408 MHz that are not usable
|
||||
uint32_t freq = cpu_dvfs_entry[i].freq;
|
||||
if (freq < 408'000)
|
||||
continue;
|
||||
Clocks::freqTable[SysClkModule_CPU].freq[j++] = freq * 1000;
|
||||
}
|
||||
|
||||
cvb_entry_t* gpu_dvfs_entry = reinterpret_cast<cvb_entry_t *>(gpu_dvfs_table);
|
||||
for (size_t i = 0; i < FREQ_TABLE_MAX_ENTRY_COUNT; i++) {
|
||||
Clocks::freqTable[SysClkModule_GPU].freq[i] = gpu_dvfs_entry[i].freq * 1000;
|
||||
}
|
||||
|
||||
// Appending maximum mem freq to freqTable
|
||||
uint32_t* mem_entry = &Clocks::freqTable[SysClkModule_MEM].freq[0];
|
||||
while (*(++mem_entry));
|
||||
*mem_entry = Clocks::maxMemFreq;
|
||||
|
||||
return ParseError_Success;
|
||||
}
|
||||
|
||||
Result FileUtils::mkdir_p(const char* dirpath) {
|
||||
// https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950
|
||||
auto mkdir_wrapper = [](char* path) {
|
||||
errno = 0;
|
||||
size_t len = strnlen(path, 0x1000);
|
||||
bool isCWDir = (len == 0) || (len == 1 && (path[0] == '.' || path[0] == '/'));
|
||||
if (isCWDir)
|
||||
return 0;
|
||||
|
||||
if (R_SUCCEEDED(mkdir(path, S_IRWXU)))
|
||||
return 0;
|
||||
|
||||
struct stat st;
|
||||
if (errno == EEXIST &&
|
||||
R_SUCCEEDED(stat(path, &st)) &&
|
||||
S_ISDIR(st.st_mode)) {
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
Result res = 0;
|
||||
|
||||
size_t path_len = strnlen(dirpath, 0x1000);
|
||||
char* path_copy = new char[path_len];
|
||||
SCOPE_EXIT { delete[] path_copy; };
|
||||
memcpy(path_copy, dirpath, path_len);
|
||||
char* p = path_copy;
|
||||
while (*p) {
|
||||
if (*p == '/') {
|
||||
// Temporarily truncate
|
||||
*p = '\0';
|
||||
|
||||
if (R_FAILED(mkdir_wrapper(path_copy))) {
|
||||
res = -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
*p = '/';
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (R_FAILED(mkdir_wrapper(path_copy)))
|
||||
res = -1;
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <sysclk.h>
|
||||
|
||||
#define FILE_CONFIG_DIR "/config/sys-clk-oc"
|
||||
#define FILE_FLAG_CHECK_INTERVAL_NS 5000000000ULL
|
||||
#define FILE_CONTEXT_CSV_PATH FILE_CONFIG_DIR "/context.csv"
|
||||
#define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag"
|
||||
#define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt"
|
||||
|
||||
typedef struct cvb_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
} cvb_coefficients;
|
||||
|
||||
typedef struct cvb_entry_t {
|
||||
u64 freq;
|
||||
cvb_coefficients cvb_dfll_param;
|
||||
cvb_coefficients cvb_pll_param;
|
||||
} cvb_entry_t;
|
||||
static_assert(sizeof(cvb_entry_t) == 0x38);
|
||||
|
||||
using CustomizeCpuDvfsTable = cvb_entry_t[FREQ_TABLE_MAX_ENTRY_COUNT];
|
||||
using CustomizeGpuDvfsTable = cvb_entry_t[FREQ_TABLE_MAX_ENTRY_COUNT];
|
||||
|
||||
constexpr uint32_t CUST_REV = 10;
|
||||
|
||||
typedef struct CustTable {
|
||||
u8 cust[4] = {'C', 'U', 'S', 'T'};
|
||||
u32 custRev;
|
||||
u32 mtcConf;
|
||||
u32 commonCpuBoostClock;
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaCpuMaxVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 marikoCpuMaxVolt;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
u32 marikoCpuUV;
|
||||
u32 marikoGpuUV;
|
||||
u32 marikoEmcDvbShift;
|
||||
u32 ramTimingPresetOne;
|
||||
u32 ramTimingPresetTwo;
|
||||
u32 ramTimingPresetThree;
|
||||
u32 ramTimingPresetFour;
|
||||
u32 ramTimingPresetFive;
|
||||
u32 ramTimingPresetSix;
|
||||
u32 ramTimingPresetSeven;
|
||||
u32 marikoGpuVoltArray[17];
|
||||
CustomizeCpuDvfsTable eristaCpuDvfsTable;
|
||||
CustomizeCpuDvfsTable marikoCpuDvfsTable;
|
||||
CustomizeCpuDvfsTable marikoCpuDvfsTableSLT;
|
||||
CustomizeGpuDvfsTable eristaGpuDvfsTable;
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTable;
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableSLT;
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
|
||||
//void* eristaMtcTable;
|
||||
//void* marikoMtcTable;
|
||||
} CustTable;
|
||||
//static_assert(sizeof(CustTable) == sizeof(u8) * 4 + sizeof(u32) * 9 + sizeof(CustomizeCpuDvfsTable) * 4 + sizeof(void*) * 2);
|
||||
//static_assert(sizeof(CustTable) == 7000);
|
||||
|
||||
class FileUtils
|
||||
{
|
||||
public:
|
||||
static void Exit();
|
||||
static Result Initialize();
|
||||
static bool IsInitialized();
|
||||
static void InitializeAsync();
|
||||
static void LogLine(const char *format, ...);
|
||||
static void WriteContextToCsv(const SysClkContext* context);
|
||||
static void ParseLoaderKip();
|
||||
static Result mkdir_p(const char* dirpath);
|
||||
protected:
|
||||
static void RefreshFlags(bool force);
|
||||
static Result CustParser(const char* path, size_t filesize);
|
||||
};
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ipc_service.h"
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include "file_utils.h"
|
||||
#include "clock_manager.h"
|
||||
#include "errors.h"
|
||||
|
||||
IpcService::IpcService()
|
||||
{
|
||||
std::int32_t priority;
|
||||
Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
|
||||
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
|
||||
rc = ipcServerInit(&this->server, SYSCLK_IPC_SERVICE_NAME, 42);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerInit");
|
||||
rc = threadCreate(&this->thread, &IpcService::ProcessThreadFunc, this, NULL, 0x2000, priority, -2);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
this->running = false;
|
||||
}
|
||||
|
||||
void IpcService::SetRunning(bool running)
|
||||
{
|
||||
std::scoped_lock lock{this->threadMutex};
|
||||
if(this->running == running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->running = running;
|
||||
|
||||
if(running)
|
||||
{
|
||||
Result rc = threadStart(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
}
|
||||
else
|
||||
{
|
||||
svcCancelSynchronization(this->thread.handle);
|
||||
threadWaitForExit(&this->thread);
|
||||
}
|
||||
}
|
||||
|
||||
IpcService::~IpcService()
|
||||
{
|
||||
this->SetRunning(false);
|
||||
Result rc = threadClose(&this->thread);
|
||||
ASSERT_RESULT_OK(rc, "threadClose");
|
||||
rc = ipcServerExit(&this->server);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerExit");
|
||||
}
|
||||
|
||||
void IpcService::ProcessThreadFunc(void *arg)
|
||||
{
|
||||
Result rc;
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
while(true)
|
||||
{
|
||||
rc = ipcServerProcess(&ipcSrv->server, &IpcService::ServiceHandlerFunc, arg);
|
||||
if(R_FAILED(rc))
|
||||
{
|
||||
if(rc == KERNELRESULT(Cancelled))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(rc != KERNELRESULT(ConnectionClosed))
|
||||
{
|
||||
FileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize)
|
||||
{
|
||||
IpcService* ipcSrv = (IpcService*)arg;
|
||||
|
||||
switch(r->data.cmdId)
|
||||
{
|
||||
case SysClkIpcCmd_GetApiVersion:
|
||||
*out_dataSize = sizeof(u32);
|
||||
return ipcSrv->GetApiVersion((u32*)out_data);
|
||||
|
||||
case SysClkIpcCmd_GetVersionString:
|
||||
if(r->hipc.meta.num_recv_buffers >= 1)
|
||||
{
|
||||
return ipcSrv->GetVersionString(
|
||||
(char*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetCurrentContext:
|
||||
*out_dataSize = sizeof(SysClkContext);
|
||||
return ipcSrv->GetCurrentContext((SysClkContext*)out_data);
|
||||
|
||||
case SysClkIpcCmd_Exit:
|
||||
return ipcSrv->Exit();
|
||||
|
||||
case SysClkIpcCmd_GetProfileCount:
|
||||
if(r->data.size >= sizeof(std::uint64_t))
|
||||
{
|
||||
*out_dataSize = sizeof(std::uint8_t);
|
||||
return ipcSrv->GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetProfiles:
|
||||
if(r->data.size >= sizeof(std::uint64_t))
|
||||
{
|
||||
*out_dataSize = sizeof(SysClkTitleProfileList);
|
||||
return ipcSrv->GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetProfiles:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetProfiles_Args))
|
||||
{
|
||||
return ipcSrv->SetProfiles((SysClkIpc_SetProfiles_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetEnabled:
|
||||
if(r->data.size >= sizeof(std::uint8_t))
|
||||
{
|
||||
return ipcSrv->SetEnabled((std::uint8_t*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetOverride:
|
||||
if(r->data.size >= sizeof(SysClkIpc_SetOverride_Args))
|
||||
{
|
||||
return ipcSrv->SetOverride((SysClkIpc_SetOverride_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_GetConfigValues:
|
||||
*out_dataSize = sizeof(SysClkConfigValueList);
|
||||
return ipcSrv->GetConfigValues((SysClkConfigValueList*)out_data);
|
||||
|
||||
case SysClkIpcCmd_SetConfigValues:
|
||||
if(r->data.size >= sizeof(SysClkConfigValueList))
|
||||
{
|
||||
return ipcSrv->SetConfigValues((SysClkConfigValueList*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case SysClkIpcCmd_SetReverseNXRTMode:
|
||||
if (r->data.size >= sizeof(ReverseNXMode)) {
|
||||
ReverseNXMode mode = *((ReverseNXMode*)r->data.ptr);
|
||||
return ipcSrv->SetReverseNXRTMode(mode);
|
||||
}
|
||||
break;
|
||||
case SysClkIpcCmd_GetFrequencyTable:
|
||||
if(r->data.size >= sizeof(SysClkIpc_GetFrequencyTable_Args))
|
||||
{
|
||||
SysClkIpc_GetFrequencyTable_Args* in_args = (SysClkIpc_GetFrequencyTable_Args*)r->data.ptr;
|
||||
*out_dataSize = sizeof(SysClkFrequencyTable);
|
||||
return ipcSrv->GetFrequencyTable(in_args, (SysClkFrequencyTable*)out_data);
|
||||
}
|
||||
break;
|
||||
case SysClkIpcCmd_GetIsMariko:
|
||||
*out_dataSize = sizeof(bool);
|
||||
return ipcSrv->GetIsMariko((bool*)out_data);
|
||||
case SysClkIpcCmd_GetBatteryChargingDisabledOverride:
|
||||
*out_dataSize = sizeof(bool);
|
||||
return ipcSrv->GetBatteryChargingDisabledOverride((bool*)out_data);
|
||||
case SysClkIpcCmd_SetBatteryChargingDisabledOverride:
|
||||
if (r->data.size >= sizeof(bool)) {
|
||||
bool toggle_true = *((bool*)(r->data.ptr));
|
||||
return ipcSrv->SetBatteryChargingDisabledOverride(toggle_true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Result IpcService::GetApiVersion(u32* out_version)
|
||||
{
|
||||
*out_version = SYSCLK_IPC_API_VERSION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetVersionString(char* out_buf, size_t bufSize)
|
||||
{
|
||||
if(bufSize)
|
||||
{
|
||||
strncpy(out_buf, TARGET_VERSION, bufSize-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetCurrentContext(SysClkContext* out_ctx)
|
||||
{
|
||||
ClockManager* clockMgr = ClockManager::GetInstance();
|
||||
*out_ctx = clockMgr->GetCurrentContext();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::Exit()
|
||||
{
|
||||
ClockManager* clockMgr = ClockManager::GetInstance();
|
||||
clockMgr->SetRunning(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
*out_count = config->GetProfileCount(*tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
config->GetProfiles(*tid, out_profiles);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetProfiles(SysClkIpc_SetProfiles_Args* args)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkTitleProfileList profiles = args->profiles;
|
||||
|
||||
if(!config->SetProfiles(args->tid, &profiles, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetEnabled(std::uint8_t* enabled)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
config->SetEnabled(*enabled);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetOverride(SysClkIpc_SetOverride_Args* args)
|
||||
{
|
||||
SysClkModule module = args->module;
|
||||
std::uint32_t hz = args->hz;
|
||||
|
||||
if(!SYSCLK_ENUM_VALID(SysClkModule, args->module))
|
||||
{
|
||||
return SYSCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
config->SetOverrideHz(module, hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
config->GetConfigValues(out_configValues);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetConfigValues(SysClkConfigValueList* configValues)
|
||||
{
|
||||
Config* config = ClockManager::GetInstance()->GetConfig();
|
||||
if(!config->HasProfilesLoaded())
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
|
||||
SysClkConfigValueList configValuesCopy = *configValues;
|
||||
|
||||
if(!config->SetConfigValues(&configValuesCopy, true))
|
||||
{
|
||||
return SYSCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) {
|
||||
ClockManager::GetInstance()->SetRNXRTMode(mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, SysClkFrequencyTable* out_table) {
|
||||
return Clocks::GetTable(args->module, args->profile, out_table);
|
||||
}
|
||||
|
||||
Result IpcService::GetIsMariko(bool* out_is_mariko) {
|
||||
*out_is_mariko = Clocks::GetIsMariko();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetBatteryChargingDisabledOverride(bool* out_is_true) {
|
||||
*out_is_true = ClockManager::GetInstance()->GetBatteryChargingDisabledOverride();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Result IpcService::SetBatteryChargingDisabledOverride(bool toggle_true) {
|
||||
return ClockManager::GetInstance()->SetBatteryChargingDisabledOverride(toggle_true);
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
|
||||
class IpcService
|
||||
{
|
||||
public:
|
||||
IpcService();
|
||||
virtual ~IpcService();
|
||||
void SetRunning(bool running);
|
||||
|
||||
protected:
|
||||
static void ProcessThreadFunc(void *arg);
|
||||
static Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, std::uint8_t* out_data, size_t* out_dataSize);
|
||||
|
||||
Result GetApiVersion(u32* out_version);
|
||||
Result GetVersionString(char* out_buf, size_t bufSize);
|
||||
Result GetCurrentContext(SysClkContext* out_ctx);
|
||||
Result Exit();
|
||||
Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count);
|
||||
Result GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles);
|
||||
Result SetProfiles(SysClkIpc_SetProfiles_Args* args);
|
||||
Result SetEnabled(std::uint8_t* enabled);
|
||||
Result SetOverride(SysClkIpc_SetOverride_Args* args);
|
||||
Result GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
Result SetConfigValues(SysClkConfigValueList* configValues);
|
||||
Result SetReverseNXRTMode(ReverseNXMode mode);
|
||||
Result GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, SysClkFrequencyTable* out_table);
|
||||
Result GetIsMariko(bool* out_is_mariko);
|
||||
Result GetBatteryChargingDisabledOverride(bool* out_is_true);
|
||||
Result SetBatteryChargingDisabledOverride(bool toggle_true);
|
||||
|
||||
bool running;
|
||||
Thread thread;
|
||||
LockableMutex threadMutex;
|
||||
IpcServer server;
|
||||
};
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include "process_management.h"
|
||||
#include "clock_manager.h"
|
||||
#include "ipc_service.h"
|
||||
#include "oc_extra.h"
|
||||
|
||||
#define INNER_HEAP_SIZE 0x50000
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern std::uint32_t __start__;
|
||||
|
||||
//set applet type to use nvdrv* service
|
||||
std::uint32_t __nx_applet_type = AppletType_SystemApplication;
|
||||
TimeServiceType __nx_time_service_type = TimeServiceType_System;
|
||||
std::uint32_t __nx_fs_num_sessions = 1;
|
||||
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
// set transfermem size to 32kib as [Fizeau](https://github.com/averne/Fizeau/)
|
||||
// or LibnxError_OutOfMemory
|
||||
u32 __nx_nv_transfermem_size = 0x8000;
|
||||
|
||||
void __libnx_initheap(void)
|
||||
{
|
||||
void *addr = nx_inner_heap;
|
||||
size_t size = nx_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 __appInit(void)
|
||||
{
|
||||
if (R_FAILED(smInitialize()))
|
||||
{
|
||||
fatalThrow(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
Result rc = setsysInitialize();
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
SetSysFirmwareVersion fw;
|
||||
rc = setsysGetFirmwareVersion(&fw);
|
||||
if (R_SUCCEEDED(rc))
|
||||
hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro));
|
||||
setsysExit();
|
||||
}
|
||||
|
||||
rc = i2cInitialize();
|
||||
if (R_FAILED(rc))
|
||||
fatalThrow(rc);
|
||||
}
|
||||
|
||||
void __appExit(void)
|
||||
{
|
||||
i2cExit();
|
||||
smExit();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Result rc = FileUtils::Initialize();
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
fatalThrow(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Clocks::Initialize();
|
||||
ProcessManagement::Initialize();
|
||||
|
||||
ProcessManagement::WaitForQLaunch();
|
||||
ClockManager::Initialize();
|
||||
FileUtils::LogLine("Ready");
|
||||
|
||||
ClockManager *clockMgr = ClockManager::GetInstance();
|
||||
IpcService *ipcSrv = new IpcService();
|
||||
clockMgr->SetRunning(true);
|
||||
clockMgr->GetConfig()->SetEnabled(true);
|
||||
ipcSrv->SetRunning(true);
|
||||
|
||||
while (clockMgr->Running())
|
||||
{
|
||||
clockMgr->Tick();
|
||||
clockMgr->WaitForNextTick();
|
||||
}
|
||||
|
||||
ipcSrv->SetRunning(false);
|
||||
delete ipcSrv;
|
||||
ClockManager::Exit();
|
||||
ProcessManagement::Exit();
|
||||
Clocks::Exit();
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
FileUtils::LogLine("[!] %s", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::exception_ptr p = std::current_exception();
|
||||
FileUtils::LogLine("[!?] %s", p ? p.__cxa_exception_type()->name() : "...");
|
||||
}
|
||||
|
||||
FileUtils::LogLine("Exit");
|
||||
svcSleepThread(1000000ULL);
|
||||
FileUtils::Exit();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
#include "oc_extra.h"
|
||||
|
||||
CpuCoreUtil::CpuCoreUtil(int coreid = -2, uint64_t ns = 1000'000ULL)
|
||||
: m_core_id(coreid), m_wait_time_ns(ns) { }
|
||||
|
||||
uint32_t CpuCoreUtil::Get() {
|
||||
struct _ctx {
|
||||
uint64_t systick;
|
||||
uint64_t idletick;
|
||||
} begin, end;
|
||||
|
||||
begin.systick = armGetSystemTick();
|
||||
begin.idletick = GetIdleTickCount();
|
||||
|
||||
svcSleepThread(m_wait_time_ns);
|
||||
|
||||
end.systick = armGetSystemTick();
|
||||
end.idletick = GetIdleTickCount();
|
||||
|
||||
uint64_t diff_idletick = end.idletick - begin.idletick;
|
||||
uint64_t diff_systick = end.systick - begin.systick;
|
||||
return UTIL_MAX - diff_idletick * 10 * 100ULL / diff_systick;
|
||||
}
|
||||
|
||||
uint64_t CpuCoreUtil::GetIdleTickCount() {
|
||||
uint64_t idletick = 0;
|
||||
svcGetInfo(&idletick, InfoType_IdleTickCount, INVALID_HANDLE, m_core_id);
|
||||
return idletick;
|
||||
}
|
||||
|
||||
|
||||
GpuCoreUtil::GpuCoreUtil(uint32_t nvgpu_field)
|
||||
: m_nvgpu_field(nvgpu_field) { }
|
||||
|
||||
uint32_t GpuCoreUtil::Get() {
|
||||
uint32_t load;
|
||||
nvIoctl(m_nvgpu_field, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &load);
|
||||
return load;
|
||||
}
|
||||
|
||||
|
||||
ReverseNXSync::ReverseNXSync()
|
||||
: m_rt_mode(ReverseNX_NotFound), m_tool_mode(ReverseNX_NotFound) {
|
||||
FILE *fp = fopen("/atmosphere/contents/0000000000534C56/flags/boot2.flag", "r");
|
||||
m_tool_enabled = fp ? true : false;
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
SysClkProfile ReverseNXSync::GetProfile(SysClkProfile real) {
|
||||
switch (this->GetMode()) {
|
||||
case ReverseNX_Docked:
|
||||
return SysClkProfile_Docked;
|
||||
case ReverseNX_Handheld:
|
||||
if (real == SysClkProfile_Docked)
|
||||
return SysClkProfile_HandheldChargingOfficial;
|
||||
default:
|
||||
return real;
|
||||
}
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::GetMode() {
|
||||
if (!this->m_sync_enabled)
|
||||
return ReverseNX_NotFound;
|
||||
if (this->m_rt_mode)
|
||||
return this->m_rt_mode;
|
||||
return this->m_tool_mode;
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::GetToolModeFromPatch(const char* patch_path) {
|
||||
constexpr uint32_t DOCKED_MAGIC = 0x320003E0;
|
||||
constexpr uint32_t HANDHELD_MAGIC = 0x52A00000;
|
||||
FILE *fp = fopen(patch_path, "rb");
|
||||
if (fp) {
|
||||
uint32_t buf = 0;
|
||||
fread(&buf, sizeof(buf), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (buf == DOCKED_MAGIC)
|
||||
return ReverseNX_Docked;
|
||||
if (buf == HANDHELD_MAGIC)
|
||||
return ReverseNX_Handheld;
|
||||
}
|
||||
|
||||
return ReverseNX_NotFound;
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::RecheckToolMode() {
|
||||
ReverseNXMode mode = ReverseNX_NotFound;
|
||||
if (this->m_tool_enabled) {
|
||||
const char* fileName = "_ZN2nn2oe16GetOperationModeEv.asm64"; // or _ZN2nn2oe18GetPerformanceModeEv.asm64
|
||||
const char* filePath = new char[72];
|
||||
SCOPE_EXIT { delete[] filePath; };
|
||||
/* Check per-game patch */
|
||||
snprintf((char*)filePath, 72, "/SaltySD/patches/%016lX/%s", this->m_app_id, fileName);
|
||||
mode = this->GetToolModeFromPatch(filePath);
|
||||
if (!mode) {
|
||||
/* Check global patch */
|
||||
snprintf((char*)filePath, 72, "/SaltySD/patches/%s", fileName);
|
||||
mode = this->GetToolModeFromPatch(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
void PsmExt::ChargingHandler(ClockManager* instance) {
|
||||
u32 current;
|
||||
Result res = I2c_Bq24193_GetFastChargeCurrentLimit(¤t);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
current -= current % 100;
|
||||
u32 chargingCurrent = instance->GetConfig()->GetConfigValue(SysClkConfigValue_ChargingCurrentLimit);
|
||||
if (current != chargingCurrent)
|
||||
I2c_Bq24193_SetFastChargeCurrentLimit(chargingCurrent);
|
||||
}
|
||||
|
||||
PsmChargeInfo* info = new PsmChargeInfo;
|
||||
Service* session = psmGetServiceSession();
|
||||
serviceDispatchOut(session, Psm_GetBatteryChargeInfoFields, *info);
|
||||
|
||||
if (PsmIsChargerConnected(info)) {
|
||||
u32 chargeNow = 0;
|
||||
if (R_SUCCEEDED(psmGetBatteryChargePercentage(&chargeNow))) {
|
||||
bool isCharging = PsmIsCharging(info);
|
||||
u32 chargingLimit = instance->GetConfig()->GetConfigValue(SysClkConfigValue_ChargingLimitPercentage);
|
||||
bool forceDisabled = instance->GetBatteryChargingDisabledOverride();
|
||||
if (isCharging && (forceDisabled || chargingLimit <= chargeNow))
|
||||
serviceDispatch(session, Psm_DisableBatteryCharging);
|
||||
if (!isCharging && chargingLimit > chargeNow)
|
||||
serviceDispatch(session, Psm_EnableBatteryCharging);
|
||||
}
|
||||
}
|
||||
|
||||
delete info;
|
||||
}
|
||||
|
||||
namespace GovernorImpl {
|
||||
|
||||
// Schedutil: https://github.com/torvalds/linux/blob/master/kernel/sched/cpufreq_schedutil.c
|
||||
// C = 1.25, tipping-point 80.0% (used in Linux schedutil), 1.25 -> 1 + (1 >> 2)
|
||||
// C = 1.5, tipping-point 66.7%, 1.5 -> 1 + (1 >> 1)
|
||||
// Utilization is frequency-invariant :
|
||||
// target_freq = C * max_freq * util / max
|
||||
// Approximate the would-be frequency-invariant utilization (normalized) :
|
||||
// target_freq = C * curr_freq * util_raw / max
|
||||
void BaseGovernor::ApplyNewFreqFromNormUtil(uint32_t normUtil) {
|
||||
uint32_t curr_hz = m_target_hz;
|
||||
|
||||
auto FindHzInTable = [](uint32_t* list, uint32_t hz) -> uint32_t {
|
||||
uint32_t* p = list;
|
||||
for (; *p != 0; p++) {
|
||||
if (hz <= *p)
|
||||
return *p;
|
||||
}
|
||||
return *(--p);
|
||||
};
|
||||
|
||||
//uint32_t next_freq = m_ref_hz / UTIL_MAX * normUtil;
|
||||
uint32_t next_freq = max_hz / UTIL_MAX * normUtil;
|
||||
next_freq += next_freq >> 1;
|
||||
|
||||
uint32_t new_hz;
|
||||
if (next_freq >= max_hz)
|
||||
new_hz = max_hz;
|
||||
else if (next_freq <= min_hz)
|
||||
new_hz = min_hz;
|
||||
else
|
||||
new_hz = FindHzInTable(m_hz_list, next_freq);
|
||||
|
||||
if (new_hz != curr_hz)
|
||||
ApplyTargetFreq(new_hz);
|
||||
}
|
||||
|
||||
void CpuGovernor::GovernorWorker::Start() {
|
||||
if (this->running)
|
||||
return;
|
||||
|
||||
this->running = true;
|
||||
Result rc = 0;
|
||||
for (int id = 0; id < CORE_NUMS; id++) {
|
||||
WorkerContext* s = &contexts[id];
|
||||
s->super = this->super;
|
||||
s->id = id;
|
||||
int prio = (id == CORE_NUMS - 1) ? 0x3F : 0x3B; // Pre-emptive MT
|
||||
rc = threadCreate(&threads[id], &WorkerContext::Loop, (void*)s, NULL, 0x400, prio, id);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
rc = threadStart(&threads[id]);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
}
|
||||
}
|
||||
|
||||
void CpuGovernor::GovernorWorker::Stop() {
|
||||
if (!this->running)
|
||||
return;
|
||||
|
||||
this->running = false;
|
||||
svcSleepThread(TICK_TIME_NS);
|
||||
|
||||
for (auto &t : threads) {
|
||||
threadWaitForExit(&t);
|
||||
threadClose(&t);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuGovernor::Apply() {
|
||||
uint32_t util = 0;
|
||||
for (auto& ctx : this->m_worker.contexts) {
|
||||
uint32_t core_util = ctx.util;
|
||||
if (util < core_util)
|
||||
util = core_util;
|
||||
}
|
||||
|
||||
this->m_util.Update(util);
|
||||
if (this->auto_boost && this->m_worker.contexts[SYS_CORE_ID].util > BOOST_THRESHOLD)
|
||||
this->ApplyBoost();
|
||||
else
|
||||
this->ApplyNewFreqFromNormUtil(this->m_util.Get());
|
||||
}
|
||||
|
||||
void CpuGovernor::WorkerContext::Loop(void* args) {
|
||||
WorkerContext* s = static_cast<WorkerContext*>(args);
|
||||
CpuGovernor* self = s->super;
|
||||
GovernorWorker* worker = &(self->m_worker);
|
||||
int coreid = s->id;
|
||||
|
||||
while (worker->running) {
|
||||
uint64_t tick = s->tick = armGetSystemTick();
|
||||
s->util = self->CalcNormalizedUtil(CpuCoreUtil(coreid, TICK_TIME_NS).Get());
|
||||
|
||||
if (apmExtIsCPUBoosted(self->m_manager->GetPerfConf())) {
|
||||
svcSleepThread(TICK_TIME_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if other cores are stuck
|
||||
for (int id = 0; id < CORE_NUMS; id++) {
|
||||
if (id == coreid)
|
||||
continue;
|
||||
|
||||
uint64_t diff = std::abs((int64_t)worker->contexts[id].tick - (int64_t)tick);
|
||||
if (diff < SYSTICK_HZ / SAMPLE_RATE * 10)
|
||||
continue;
|
||||
|
||||
// Stuck on system core and auto boost enabled, apply boost
|
||||
if (id == SYS_CORE_ID && self->auto_boost) {
|
||||
self->ApplyBoost();
|
||||
break;
|
||||
}
|
||||
|
||||
// Stuck on other cores or auto boost disabled, apply max hz
|
||||
self->ApplyTargetFreq(self->max_hz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GpuGovernor::Apply() {
|
||||
uint32_t util = this->CalcNormalizedUtil(GpuCoreUtil(m_nvgpu_field).Get());
|
||||
this->m_util.Update(util);
|
||||
this->ApplyNewFreqFromNormUtil(this->m_util.Get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Governor::SetConfig(SysClkOcGovernorConfig config) {
|
||||
if (m_config == config)
|
||||
return;
|
||||
|
||||
m_config = config;
|
||||
m_cpu_gov->m_worker.onConfigUpdated(config);
|
||||
m_manager.onConfigUpdated(config);
|
||||
};
|
||||
|
||||
void Governor::SetPerfConf(uint32_t id) {
|
||||
m_perf_conf_id = id;
|
||||
m_apm_conf = Clocks::GetEmbeddedApmConfig(id);
|
||||
}
|
||||
|
||||
void Governor::SetMaxHz(uint32_t maxHz, SysClkModule module) {
|
||||
if (!maxHz) // Fallback to apm configuration
|
||||
maxHz = Clocks::GetStockClock(m_apm_conf, (SysClkModule)module);
|
||||
|
||||
switch (module) {
|
||||
case SysClkModule_CPU:
|
||||
m_cpu_gov->max_hz = maxHz;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
m_gpu_gov->max_hz = maxHz;
|
||||
m_gpu_gov->min_hz = (maxHz <= 153'600'000) ? maxHz : 153'600'000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::SetMinHz(uint32_t minHz, SysClkModule module) {
|
||||
if (module == SysClkModule_CPU) {
|
||||
m_cpu_gov->min_hz = minHz;
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::GovernorManager::Start() {
|
||||
if (this->running)
|
||||
return;
|
||||
|
||||
this->running = true;
|
||||
Result rc = threadCreate(&thread, &ContextManager, (void*)this, NULL, 0x400, 0x3F, 3);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
rc = threadStart(&thread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
}
|
||||
|
||||
void Governor::GovernorManager::Stop() {
|
||||
if (!this->running)
|
||||
return;
|
||||
|
||||
this->running = false;
|
||||
svcSleepThread(TICK_TIME_NS);
|
||||
threadWaitForExit(&thread);
|
||||
threadClose(&thread);
|
||||
}
|
||||
|
||||
void Governor::GovernorManager::ContextManager(void* args) {
|
||||
Governor* self = static_cast<Governor*>(args);
|
||||
|
||||
constexpr uint64_t UPDATE_CONTEXT_RATE = SAMPLE_RATE / 2;
|
||||
uint64_t update_ticks = UPDATE_CONTEXT_RATE;
|
||||
bool cpuBoosted = false, gpuThrottled = false;
|
||||
|
||||
while (self->m_manager.running) {
|
||||
bool shouldUpdateContext = ++update_ticks >= UPDATE_CONTEXT_RATE;
|
||||
if (shouldUpdateContext) {
|
||||
update_ticks = 0;
|
||||
|
||||
uint32_t hz = self->m_gpu_gov->RefreshContext();
|
||||
// Sleep mode detected, wait 10 ticks
|
||||
while (!hz) {
|
||||
svcSleepThread(10 * TICK_TIME_NS);
|
||||
hz = self->m_gpu_gov->RefreshContext();
|
||||
}
|
||||
|
||||
uint32_t perf_conf = self->GetPerfConf();
|
||||
if ((gpuThrottled = apmExtIsBoostMode(perf_conf)) && self->IsHandledByGovernor(SysClkModule_GPU))
|
||||
self->m_gpu_gov->ApplyBoost();
|
||||
|
||||
if ((cpuBoosted = apmExtIsCPUBoosted(perf_conf)) && self->IsHandledByGovernor(SysClkModule_CPU))
|
||||
self->m_cpu_gov->ApplyBoost();
|
||||
}
|
||||
|
||||
if (!gpuThrottled && self->IsHandledByGovernor(SysClkModule_GPU))
|
||||
self->m_gpu_gov->Apply();
|
||||
if (!cpuBoosted && self->IsHandledByGovernor(SysClkModule_CPU))
|
||||
self->m_cpu_gov->Apply();
|
||||
|
||||
svcSleepThread(TICK_TIME_NS);
|
||||
}
|
||||
};
|
||||
@@ -1,367 +0,0 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
|
||||
// Forward declaration
|
||||
class ClockManager;
|
||||
class Governor;
|
||||
#include "clock_manager.h"
|
||||
|
||||
|
||||
class CpuCoreUtil {
|
||||
public:
|
||||
CpuCoreUtil (int coreid, uint64_t ns);
|
||||
uint32_t Get();
|
||||
|
||||
protected:
|
||||
const int m_core_id;
|
||||
const uint64_t m_wait_time_ns;
|
||||
static constexpr uint64_t IDLETICKS_PER_MS = 192;
|
||||
static constexpr uint32_t UTIL_MAX = 100'0;
|
||||
|
||||
uint64_t GetIdleTickCount();
|
||||
};
|
||||
|
||||
|
||||
class GpuCoreUtil {
|
||||
public:
|
||||
GpuCoreUtil (uint32_t nvgpu_field);
|
||||
uint32_t Get();
|
||||
|
||||
protected:
|
||||
uint32_t m_nvgpu_field;
|
||||
static constexpr uint64_t NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD = 0x80044715;
|
||||
};
|
||||
|
||||
|
||||
class ReverseNXSync {
|
||||
public:
|
||||
ReverseNXSync ();
|
||||
|
||||
void ToggleSync(bool enable) { m_sync_enabled = enable; };
|
||||
void Reset(uint64_t app_id) { m_app_id = app_id; SetRTMode(ReverseNX_NotFound); GetToolMode(); }
|
||||
ReverseNXMode GetRTMode() { return m_rt_mode; };
|
||||
void SetRTMode(ReverseNXMode mode) { m_rt_mode = mode; };
|
||||
ReverseNXMode GetToolMode() { return m_tool_mode = RecheckToolMode(); };
|
||||
SysClkProfile GetProfile(SysClkProfile real);
|
||||
ReverseNXMode GetMode();
|
||||
|
||||
protected:
|
||||
std::atomic<ReverseNXMode> m_rt_mode;
|
||||
ReverseNXMode m_tool_mode;
|
||||
uint64_t m_app_id = 0;
|
||||
bool m_tool_enabled;
|
||||
bool m_sync_enabled;
|
||||
|
||||
ReverseNXMode GetToolModeFromPatch(const char* patch_path);
|
||||
ReverseNXMode RecheckToolMode();
|
||||
};
|
||||
|
||||
|
||||
namespace PsmExt {
|
||||
void ChargingHandler(ClockManager* instance);
|
||||
}
|
||||
|
||||
|
||||
constexpr uint64_t SAMPLE_RATE = 200;
|
||||
constexpr uint64_t TICK_TIME_NS = 1000'000'000 / SAMPLE_RATE;
|
||||
constexpr uint64_t SYSTICK_HZ = 19200000;
|
||||
|
||||
namespace GovernorImpl {
|
||||
constexpr uint32_t UTIL_MAX = 1000;
|
||||
|
||||
class BaseGovernor {
|
||||
public:
|
||||
BaseGovernor(SysClkModule module) : m_module(module) {
|
||||
m_hz_list = Clocks::freqTable[module].freq;
|
||||
m_ref_hz = *Clocks::freqRange[module].last;
|
||||
};
|
||||
|
||||
uint32_t RefreshContext() { return this->m_target_hz = Clocks::GetCurrentHz(this->m_module); };
|
||||
|
||||
uint32_t min_hz, max_hz, boost_hz;
|
||||
|
||||
protected:
|
||||
uint32_t CalcNormalizedUtil(uint32_t rawUtil) {
|
||||
//return ((uint64_t)rawUtil * m_target_hz / m_ref_hz);
|
||||
return ((uint64_t)rawUtil * m_target_hz / max_hz);
|
||||
};
|
||||
|
||||
void ApplyNewFreqFromNormUtil(uint32_t norm);
|
||||
|
||||
void ApplyTargetFreq(uint32_t hz) {
|
||||
if (!hz)
|
||||
return;
|
||||
|
||||
m_target_hz = hz;
|
||||
Clocks::SetHz(m_module, hz);
|
||||
};
|
||||
|
||||
SysClkModule m_module;
|
||||
uint32_t* m_hz_list;
|
||||
uint32_t m_target_hz, m_ref_hz;
|
||||
|
||||
friend Governor;
|
||||
};
|
||||
|
||||
class CpuGovernor : public BaseGovernor {
|
||||
public:
|
||||
CpuGovernor(Governor* manager)
|
||||
: BaseGovernor(SysClkModule_CPU), m_manager(manager) {
|
||||
boost_hz = Clocks::boostCpuFreq;
|
||||
m_worker.super = this;
|
||||
};
|
||||
|
||||
~CpuGovernor() { this->m_worker.Stop(); };
|
||||
|
||||
void Apply();
|
||||
|
||||
void ApplyBoost() {
|
||||
ApplyTargetFreq((max_hz > boost_hz) ? max_hz : boost_hz);
|
||||
};
|
||||
|
||||
bool auto_boost;
|
||||
|
||||
protected:
|
||||
static constexpr int CORE_NUMS = 4;
|
||||
static constexpr int SYS_CORE_ID = CORE_NUMS - 1;
|
||||
|
||||
// PELT: https://github.com/torvalds/linux/blob/master/kernel/sched/pelt.c
|
||||
// Util_acc_n = Util_0 + Util_1 * D + Util_2 * D^2 + ... + Util_n * D^n
|
||||
// To approximate D (decay multiplier):
|
||||
// After 50 ms (if SAMPLE_RATE == 200, 10 samples)
|
||||
// UTIL_MAX * D^10 ≈ 1 (UTIL_MAX decayed to 1)
|
||||
// D = 4129 / 8192
|
||||
// Util_acc_max = Util_acc_inf = 2012
|
||||
typedef struct PeltUtil {
|
||||
uint32_t util_acc = 0;
|
||||
|
||||
static constexpr uint32_t DECAY_DIVIDENT = 4129;
|
||||
static constexpr uint32_t DECAY_DIVISOR = 8192;
|
||||
static constexpr uint32_t UTIL_ACC_MAX = 2012;
|
||||
|
||||
uint32_t Get() { return (util_acc * UTIL_MAX / UTIL_ACC_MAX); };
|
||||
void Update(uint32_t util) { util_acc = util_acc * DECAY_DIVIDENT / DECAY_DIVISOR + util; };
|
||||
} PeltUtil;
|
||||
PeltUtil m_util;
|
||||
|
||||
typedef struct {
|
||||
CpuGovernor*super;
|
||||
int id;
|
||||
uint32_t util;
|
||||
uint64_t tick;
|
||||
|
||||
static void Loop(void* args);
|
||||
} WorkerContext;
|
||||
|
||||
typedef struct GovernorWorker {
|
||||
Thread threads[CORE_NUMS];
|
||||
WorkerContext contexts[CORE_NUMS];
|
||||
bool running;
|
||||
CpuGovernor* super;
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
void onConfigUpdated(SysClkOcGovernorConfig config) {
|
||||
bool expected = (config >> SysClkOcGovernorConfig_CPU_Shift) & 1;
|
||||
if (expected != running)
|
||||
expected ? Start() : Stop();
|
||||
};
|
||||
} GovernorWorker;
|
||||
GovernorWorker m_worker;
|
||||
|
||||
Governor* m_manager;
|
||||
|
||||
friend Governor;
|
||||
};
|
||||
|
||||
class GpuGovernor : public BaseGovernor {
|
||||
public:
|
||||
GpuGovernor() : BaseGovernor(SysClkModule_GPU) {
|
||||
min_hz = 153'600'000;
|
||||
boost_hz = 76'800'000;
|
||||
|
||||
nvInitialize();
|
||||
Result rc = nvOpen(&m_nvgpu_field, "/dev/nvhost-ctrl-gpu");
|
||||
if (R_FAILED(rc)) {
|
||||
ASSERT_RESULT_OK(rc, "nvOpen");
|
||||
nvExit();
|
||||
}
|
||||
};
|
||||
|
||||
~GpuGovernor() {
|
||||
nvClose(m_nvgpu_field);
|
||||
nvExit();
|
||||
};
|
||||
|
||||
void ApplyBoost() {
|
||||
ApplyTargetFreq(boost_hz);
|
||||
};
|
||||
|
||||
void Apply();
|
||||
|
||||
protected:
|
||||
// Get average value from a sliding window in O(1)
|
||||
template <typename T, size_t WINDOW_SIZE>
|
||||
class SWindowAvg {
|
||||
public:
|
||||
SWindowAvg() {}
|
||||
|
||||
void Add(T item) {
|
||||
T pop = m_queue[m_next];
|
||||
m_queue[m_next] = item;
|
||||
m_next = (m_next + 1) % WINDOW_SIZE;
|
||||
m_sum -= pop;
|
||||
m_sum += item;
|
||||
}
|
||||
|
||||
T Get() { return m_sum / WINDOW_SIZE; }
|
||||
|
||||
protected:
|
||||
size_t m_next = 0;
|
||||
T m_sum = 0;
|
||||
T m_queue[WINDOW_SIZE] = {};
|
||||
};
|
||||
|
||||
// Get max value from a sliding window in O(1)
|
||||
template <typename T, size_t WINDOW_SIZE>
|
||||
class SWindowMax {
|
||||
protected:
|
||||
typedef struct {
|
||||
T item;
|
||||
T max;
|
||||
} s_Entry;
|
||||
|
||||
struct s_Stack {
|
||||
s_Entry m_stack[WINDOW_SIZE] = {};
|
||||
size_t m_next = WINDOW_SIZE;
|
||||
|
||||
bool empty() { return m_next == 0; };
|
||||
s_Entry top() { return m_stack[m_next-1]; };
|
||||
s_Entry pop() { return m_stack[--m_next]; };
|
||||
void push(s_Entry item) {
|
||||
if (m_next == WINDOW_SIZE)
|
||||
return;
|
||||
m_stack[m_next++] = item;
|
||||
};
|
||||
};
|
||||
|
||||
s_Stack enqStack;
|
||||
s_Stack deqStack;
|
||||
|
||||
void Push(s_Stack& stack, T item) {
|
||||
s_Entry n = {
|
||||
.item = item,
|
||||
.max = enqStack.empty() ? item : std::max(item, enqStack.top().max)
|
||||
};
|
||||
stack.push(n);
|
||||
}
|
||||
|
||||
T Pop() {
|
||||
if (deqStack.empty()) {
|
||||
while (!enqStack.empty())
|
||||
Push(deqStack, enqStack.pop().max);
|
||||
}
|
||||
return deqStack.pop().item;
|
||||
}
|
||||
|
||||
public:
|
||||
SWindowMax() {}
|
||||
|
||||
void Add(T item) { Pop(); Push(enqStack, item); }
|
||||
|
||||
T Get() {
|
||||
if (!enqStack.empty()) {
|
||||
T enqMax = enqStack.top().max;
|
||||
if (!deqStack.empty()) {
|
||||
T deqMax = deqStack.top().max;
|
||||
return std::max(deqMax, enqMax);
|
||||
}
|
||||
return enqMax;
|
||||
}
|
||||
if (!deqStack.empty())
|
||||
return deqStack.top().max;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct MaxWindow {
|
||||
SWindowMax<uint32_t, 32> window {};
|
||||
uint32_t util_acc = 0;
|
||||
|
||||
// After 160 ms (if SAMPLE_RATE == 200, 32 samples)
|
||||
// UTIL_MAX * D^32 ≈ 1 (UTIL_MAX decayed to 1)
|
||||
// D = 6880 / 8192
|
||||
// Util_acc_max = Util_acc_inf = 6145
|
||||
static constexpr uint32_t DECAY_DIVIDENT = 6880;
|
||||
static constexpr uint32_t DECAY_DIVISOR = 8192;
|
||||
static constexpr uint32_t UTIL_ACC_MAX = 6145;
|
||||
|
||||
uint32_t Get() { return ((util_acc * UTIL_MAX / UTIL_ACC_MAX) + window.Get()) / 2; };
|
||||
void Update(uint32_t util) { window.Add(util); util_acc = util_acc * DECAY_DIVIDENT / DECAY_DIVISOR + util; };
|
||||
} MaxWindow;
|
||||
MaxWindow m_util;
|
||||
|
||||
uint32_t m_nvgpu_field;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class Governor {
|
||||
public:
|
||||
Governor() {
|
||||
m_cpu_gov = new GovernorImpl::CpuGovernor(this);
|
||||
m_gpu_gov = new GovernorImpl::GpuGovernor();
|
||||
};
|
||||
|
||||
~Governor() {
|
||||
m_manager.Stop();
|
||||
delete m_cpu_gov;
|
||||
delete m_gpu_gov;
|
||||
};
|
||||
|
||||
SysClkOcGovernorConfig GetConfig() { return m_config; };
|
||||
inline bool IsHandledByGovernor(SysClkModule module) { return GetGovernorEnabled(this->GetConfig(), module); };
|
||||
void SetConfig(SysClkOcGovernorConfig config);
|
||||
|
||||
void SetPerfConf(uint32_t id);
|
||||
uint32_t GetPerfConf() { return m_perf_conf_id; };
|
||||
|
||||
void SetMaxHz(uint32_t maxHz, SysClkModule module);
|
||||
void SetMinHz(uint32_t minHz, SysClkModule module);
|
||||
|
||||
void SetAutoCPUBoost(bool enabled) { m_cpu_gov->auto_boost = enabled; };
|
||||
void SetCPUBoostHz(uint32_t boostHz) { m_cpu_gov->boost_hz = boostHz; };
|
||||
|
||||
protected:
|
||||
typedef struct GovernorManager {
|
||||
bool running = false;
|
||||
Thread thread;
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void onConfigUpdated(SysClkOcGovernorConfig config) {
|
||||
bool shouldRun = (config != SysClkOcGovernorConfig_AllDisabled);
|
||||
shouldRun ? Start() : Stop();
|
||||
};
|
||||
static void ContextManager(void* args);
|
||||
} GovernorManager;
|
||||
GovernorManager m_manager;
|
||||
|
||||
SysClkOcGovernorConfig m_config = SysClkOcGovernorConfig_AllDisabled;
|
||||
|
||||
uint32_t m_perf_conf_id;
|
||||
SysClkApmConfiguration* m_apm_conf;
|
||||
|
||||
GovernorImpl::CpuGovernor* m_cpu_gov;
|
||||
GovernorImpl::GpuGovernor* m_gpu_gov;
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "process_management.h"
|
||||
#include "file_utils.h"
|
||||
#include "errors.h"
|
||||
|
||||
void ProcessManagement::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
rc = pmdmntInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||
|
||||
rc = pminfoInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pminfoInitialize");
|
||||
}
|
||||
|
||||
void ProcessManagement::WaitForQLaunch()
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint64_t pid = 0;
|
||||
do
|
||||
{
|
||||
rc = pmdmntGetProcessId(&pid, PROCESS_MANAGEMENT_QLAUNCH_TID);
|
||||
svcSleepThread(500000000ULL);
|
||||
} while (R_FAILED(rc));
|
||||
}
|
||||
|
||||
std::uint64_t ProcessManagement::GetCurrentApplicationId()
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint64_t pid = 0;
|
||||
std::uint64_t tid = 0;
|
||||
rc = pmdmntGetApplicationProcessId(&pid);
|
||||
|
||||
if (rc == 0x20f)
|
||||
{
|
||||
return PROCESS_MANAGEMENT_QLAUNCH_TID;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pmdmntGetApplicationProcessId");
|
||||
|
||||
rc = pminfoGetProgramId(&tid, pid);
|
||||
|
||||
if (rc == 0x20f)
|
||||
{
|
||||
return PROCESS_MANAGEMENT_QLAUNCH_TID;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pminfoGetProgramId");
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
void ProcessManagement::Exit()
|
||||
{
|
||||
pmdmntExit();
|
||||
pminfoExit();
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <cstdint>
|
||||
|
||||
#define PROCESS_MANAGEMENT_QLAUNCH_TID 0x0100000000001000ULL
|
||||
|
||||
class ProcessManagement
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void WaitForQLaunch();
|
||||
static std::uint64_t GetCurrentApplicationId();
|
||||
static void Exit();
|
||||
};
|
||||
Reference in New Issue
Block a user