Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
hardware
thermald
thermal_daemon-2.5.7.17.git+fc7464a.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File thermal_daemon-2.5.7.17.git+fc7464a.obscpio of Package thermald
07070100000000000081A400000000000000000000000166853FAC000001B9000000000000000000000000000000000000002F00000000thermal_daemon-2.5.7.17.git+fc7464a/.gitignore*~ .*.swp *.o *.lo *.la *.bz2 *.gir *.typelib *.stamp *-enum-types.[ch] Makefile Makefile.in* configure compile config.* aclocal.m4 depcomp install-sh libtool ltmain.sh missing mkinstalldirs POTFILES stamp-* .deps .libs autom4te.cache intltool-* gtk-doc.make TAGS INSTALL ABOUT-NLS *.pc cscope.*out thermald thd_dbus_interface.h .cproject .project *thermald.service .dirstamp version.xml .qmake.stash moc_*.cpp moc_*.h ui_*.h ThermalMonitor 07070100000001000081A400000000000000000000000166853FAC00000794000000000000000000000000000000000000002F00000000thermal_daemon-2.5.7.17.git+fc7464a/Android.mkLOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ src/android_main.cpp \ src/thd_engine.cpp \ src/thd_cdev.cpp \ src/thd_cdev_therm_sys_fs.cpp \ src/thd_engine_default.cpp \ src/thd_sys_fs.cpp \ src/thd_trip_point.cpp \ src/thd_zone.cpp \ src/thd_zone_cpu.cpp \ src/thd_zone_therm_sys_fs.cpp \ src/thd_zone_dynamic.cpp \ src/thd_preference.cpp \ src/thd_parse.cpp \ src/thd_sensor.cpp \ src/thd_sensor_virtual.cpp \ src/thd_kobj_uevent.cpp \ src/thd_cdev_order_parser.cpp \ src/thd_cdev_gen_sysfs.cpp \ src/thd_pid.cpp \ src/thd_zone_generic.cpp \ src/thd_cdev_cpufreq.cpp \ src/thd_cdev_rapl.cpp \ src/thd_cdev_intel_pstate_driver.cpp \ src/thd_rapl_power_meter.cpp \ src/thd_trt_art_reader.cpp \ src/thd_cdev_rapl_dram.cpp \ src/thd_cpu_default_binding.cpp \ src/thd_cdev_backlight.cpp \ src/thd_int3400.cpp \ src/thd_cdev_kbl_amdgpu.cpp \ src/thd_sensor_kbl_amdgpu_power.cpp \ src/thd_sensor_kbl_amdgpu_thermal.cpp \ src/thd_zone_kbl_g_mcp.cpp \ src/thd_sensor_kbl_g_mcp.cpp \ src/thd_zone_kbl_amdgpu.cpp \ src/thd_sensor_rapl_power.cpp \ src/thd_zone_rapl_power.cpp \ src/thd_engine_adaptive.cpp \ src/thd_lzma_dec.cpp \ src/LzmaDec.c \ src/thd_gddv.cpp LOCAL_C_INCLUDES += external/libxml2/include LOCAL_PROPRIETARY_MODULE := true LOCAL_CFLAGS := \ -DTDRUNDIR='"/data/vendor/thermal-daemon"' \ -DTDCONFDIR='"/system/vendor/etc/thermal-daemon"' \ -Wno-unused-parameter \ -fexceptions\ -Wall \ -Werror \ ifneq (,$(filter eng userdebug, $(TARGET_BUILD_VARIANT))) LOCAL_CFLAGS += -DLOG_DEBUG_INFO=1 endif LOCAL_STATIC_LIBRARIES := libxml2 ifeq ($(BOARD_VNDK_VERSION),current) LOCAL_SHARED_LIBRARIES := liblog libcutils libdl libc++ libutils else LOCAL_SHARED_LIBRARIES := liblog libcutils libdl libc++ libicuuc libutils endif LOCAL_PRELINK_MODULE := false LOCAL_MODULE := thermal-daemon include $(BUILD_EXECUTABLE) 07070100000002000081A400000000000000000000000166853FAC00004640000000000000000000000000000000000000002C00000000thermal_daemon-2.5.7.17.git+fc7464a/COPYING GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. 07070100000003000081A400000000000000000000000166853FAC0000082D000000000000000000000000000000000000003000000000thermal_daemon-2.5.7.17.git+fc7464a/Makefile.aminclude $(GLIB_MAKEFILE) SUBDIRS = . docs data ACLOCAL_AMFLAGS = # Global C Flags AM_CFLAGS = ${GLIB_CFLAGS} AM_CXXFLAGS = \ $(GLIB_CFLAGS) \ $(XML_CFLAGS) \ $(UPOWER_CFLAGS) \ $(EVDEV_CFLAGS) \ -DTDRUNDIR=\"$(tdrundir)\" \ -DTDCONFDIR=\"$(tdconfdir)\" \ $(CXXFLAGS) \ -I src EXTRA_DIST=Makefile.glib \ thermald.pc.in # Programs to build sbin_PROGRAMS = thermald # Evaluate Table Application thermald_CPPFLAGS = \ -I@top_srcdir@/src \ -DTDLOCALEDIR=\"$(datadir)/locale\" \ -DGLIB_SUPPORT thermald_includedir = @top_srcdir@ thermald_LDADD = \ $(GLIB_LIBS) \ $(LIBNL_LIBS) \ $(LIBM) \ $(LIBDL) \ $(XML_LIBS) \ $(UPOWER_LIBS) \ $(EVDEV_LIBS) thermald_SOURCES = \ src/main.cpp \ src/thd_dbus_interface.cpp \ src/thd_engine.cpp \ src/thd_cdev.cpp \ src/thd_cdev_therm_sys_fs.cpp \ src/thd_engine_default.cpp \ src/thd_engine_adaptive.cpp \ src/thd_sys_fs.cpp \ src/thd_trip_point.cpp \ src/thd_zone.cpp \ src/thd_zone_cpu.cpp \ src/thd_zone_therm_sys_fs.cpp \ src/thd_zone_dynamic.cpp \ src/thd_preference.cpp \ src/thd_parse.cpp \ src/thd_sensor.cpp \ src/thd_sensor_virtual.cpp \ src/thd_kobj_uevent.cpp \ src/thd_cdev_order_parser.cpp \ src/thd_cdev_gen_sysfs.cpp \ src/thd_pid.cpp \ src/thd_zone_generic.cpp \ src/thd_cdev_cpufreq.cpp \ src/thd_cdev_rapl.cpp \ src/thd_cdev_intel_pstate_driver.cpp \ src/thd_rapl_power_meter.cpp \ src/thd_trt_art_reader.cpp \ src/thd_cdev_rapl_dram.cpp \ src/thd_cdev_backlight.cpp \ src/thd_int3400.cpp \ src/thd_cdev_kbl_amdgpu.cpp \ src/thd_sensor_kbl_amdgpu_power.cpp \ src/thd_sensor_kbl_amdgpu_thermal.cpp \ src/thd_zone_kbl_g_mcp.cpp \ src/thd_sensor_kbl_g_mcp.cpp \ src/thd_zone_kbl_amdgpu.cpp \ src/thd_sensor_rapl_power.cpp \ src/thd_zone_rapl_power.cpp \ src/thd_gddv.cpp \ thermald-resource.c \ src/thd_lzma_dec.cpp \ src/LzmaDec.c man5_MANS = man/thermal-conf.xml.5 man8_MANS = man/thermald.8 thermald-resource.c: $(top_srcdir)/thermald-resource.gresource.xml $(AM_V_GEN) glib-compile-resources --generate-source --sourcedir=${top_srcdir} $< CLEANFILES = $(BUILT_SOURCES) 07070100000004000081A400000000000000000000000166853FAC00003837000000000000000000000000000000000000002F00000000thermal_daemon-2.5.7.17.git+fc7464a/README.txtUse man pages to check command line arguments in configuration: man thermald man thermal-conf.xml Prerequisites: Kernel Prefers kernel with Intel RAPL power capping driver : Available from Linux kernel 3.13.rc1 Intel P State driver (Available in Linux kernel stable release) Intel Power clamp driver (Available in Linux kernel stable release) Intel INT340X drivers Intel RAPL-mmio power capping driver: Available from 5.3-rc1 Companion tools ThermalMonitor Graphical front end for monitoring and control. Source code is as part of tools folder in this git repository. dptfxtract Download from: https://github.com/intel/dptfxtract This generates configuration files for thermald on some systems. Building and executing on Fedora 1. Install yum install automake yum install autoconf-archive yum install gcc yum install gcc-c++ yum install glib-devel yum install libxml2-devel yum install gtk-doc yum install upower-devel yum install libevdev-devel Replace yum with dnf for later Fedora versions. 2 Build ./autogen.sh prefix=/ make sudo make install The prefix value depends on the distribution version. This can be "/" or "/usr". So please check existing path of thermald install, if present to update and add appropriate prefix. 3 - start service sudo systemctl start thermald.service - Get status sudo systemctl status thermald.service - Stop service sudo systemctl stop thermald.service 4. Terminate using DBUS I/F sudo test/test_pref.sh and select "TERMINATE" choice. Building on Ubuntu 1. Install sudo apt install autoconf sudo apt install autoconf-archive sudo apt install g++ sudo apt install libglib2.0-dev sudo apt install libxml2-dev sudo apt install gtk-doc-tools sudo apt install libupower-glib-dev sudo apt install libevdev-dev 2 Build ./autogen.sh prefix=/ make sudo make install (It will give error for systemd configuration, but ignore) 3. If using systemd, use - start service sudo systemctl start thermald.service - Get status sudo systemctl status thermald.service - Stop service sudo systemctl stop thermald.service Building and executing on openSUSE 1. Install zypper in automake zypper in gcc zypper in gcc-c++ zypper in glib2-devel zypper in libxml2-devel zypper in automake autoconf-archive zypper in gtk-doc zypper in libupower-glib-devel zypper in libevdev-devel For build, follow the same procedure as Fedora. ------------------------------------------- Releases Release 2.5.8 - Lunar Lake support - Deprecate modem support - Remove dbus-glib-devel as requirment Release 2.5.7 - Remove dependency on lzma libs - Fix remaining issues with GDBUS transition - Seg fault when no config file for the first time Release 2.5.6 - Fix crash with GDBus port Release 2.5.5 - Use GDBus for dbus - Handle single trip to solve thermal shutdown issue for an Alder Lake desktop Release 2.5.4 - Android support - Workarounds for missing conditions/tables Release 2.5.3 - Support Meteor Lake Release 2.5.2 - Support Alder Lake N - Support ITMT version 2, which is used in some Raptor Lake systems Release 2.5.1 - Static analysis fixes - Missing init, which causes skipping of conditions in a Dell system Release 2.5 - Support of new thermal table for Alder Lake - Add Raptor Lake in the list Release 2.4.9 - Fix performance issues for Dell Latitude 5421 - Fix performance issues for Dell Latitude 7320/7420 (Depend on kernel patch "thermal: int340x: Update OS policy capability handshake") - Adaptive improvements from Benzea - Thermal Monitor fixes and cosmetic updates - Documentation updates from Colin King - Static analysis fixes from Benzea - Fix test for compressed data vaults Release 2.4.8 -Fix Ideapad thermal shutdown issue #328 Release 2.4.7 - Fix AC/DC power limit issue in some HP TigerLake systems - Regression fix for RAPL MSR usage in xml config file - Added Japer Lake and Alder Lake CPU models - Debug scripts for log collection to upload Release 2.4.6 - Fix for Ubuntu bug 1930422 Release 2.4.5 - Address low performance with Dell Latitude 5420 with the latest BIOS Release 2.4.4 - Address low performance with Dell Latitude 5420 Release 2.4.3 - Allow --ingore-cpuid-check to use with --adaptive option Release 2.4.2 - Issue with Dell Latitude 7400. Fix for issue #291 Release 2.4.1 - Minor change for Dell XPS 13 with Tiger Lake. Release 2.4 - Support for Rocket Lake and Commet Lake CPU model - Tiger Lake DPTF tables support - CPU stuck at low frequency on two models (issue 280) - Changes related to PID and exit codes Release 2.3 - Merged changes from mjg59 for adaptive - Requires Linux kernel version 5.8 or later - By default tries --adaptive and fallback to old style - At least some level of success to use adaptive option on: (not expected to be on par with Windows DPTF) Dell XPS 13 9360 Dell XPS 13 9370 Dell XPS 13 9380 Dell XPS 13 7390 2-in-1 Dell Insperion_7386 HP Spectre x360 Convertible 15-ch0xx HP ZBook 15 G5 Lenovo Thinkpad T480 - thermald will not run on Lenovo platforms with lap mode sysfs entry Release 2.2 - Ignore PPCC power limits when max and min power is same - Regression in cpufreq cooling device causing min state to get stuck Release 2.1 - Workaround for invalid PPCC power limits - Reduce polling for power when PPCC is not present Release 2.0 - Tiger Lake support - PL1 limit setting from PPCC as is - Optimize multi step, multi zone control - Add new tag for product identification "product_sku" Release 1.9.1 - Remove default CPU temperature compensation for auto generated configuration from dptfxtract - Minor Android build warnings Release 1.9 - The major change in this version is the active power limits adjustment. This will be useful to improve performance on some newer platform. But this will lead to increase in CPU and other temperatures. Hence this is important to run dptfxtract version 1.4.1 tool to get performance sensitive thermal limits (https://github.com/intel/dptfxtract/commits/v1.4.1). If the default configuration picked up by thermald is not optimal, user can select other less aggressive configuration. Refer to the README here https://github.com/intel/dptfxtract/blob/master/README.txt This power limit adjustment depends on some kernel changes released with kernel version v5.3-rc1. For older kernel release run thermald with --workaround-enabled But this will depend on /dev/mem access, which means that platforms with secure boot must update to newer kernels. - TCC offset limits As reported in some forums that some platforms have issue with high TCC offset settings. Under some special condition this offset is adjusted, but that currently needs msr module loaded to get MSR access from user space. I have submitted a patch to have this exported via sysfs for v5.4+ kernel. - To disable all the above performance optimization, use --disable-active-power. Since Linux Thermal Daemon implementation doesn't have capability to match Intel® Dynamic Platform and Thermal Framework (DPTF) implementation on other Operating systems, this option is very important if the user is experiencing thermal issues. If there is some OEM/manufactures have issue with this implementation, please get back to me for blacklist of platforms. - Added support for Ice Lake platform - ThermalMonitor Cleaned up the plots, so that only active sensors and trips gets plotted. Release 1.8 - Support of KBL-G with discrete GPU - Fast removal of any cooling action which was applied once temperature is normal - Android support - Add Hot trip point, which when reached just calls "suspend" - Adding new tag "DependsOn" which enable/disable trip based on some other trip - Polling interval can be configured via thermal xml config - Per trip PID control - Simplify RAPL cooling device Release 1.7.2 - Workwround for platform with invalid thermal table - Error printing for RAPL constraint sysfs read on failure - thermal-conf.xml.auto can be read from /etc/thermald, which allows user to modify generated thermal-conf.xml from /var/run/thermald and copy to /etc/thermald Release 1.7.1 - Removed dptfxtract binary as there is an issue in packaging this with GPL source for distributions Release 1.7 - Add GeminiLake - Add dptfxtract tool, which converts DPTF tables to thermald tables using best effort - Changes to accommodate dptfxtract tool conversions - Better facility to configure fan controls - PID control optimization - Fix powerlimit write errors because of bad FW settings of power limits - More restrictive compile options and warnings as errors - Improve logging - Android build fixes Release 1.6 - Add Kabylake and missing Broadwell CPU model - Removed deprecated modules - Added passive trip between critical and max, to allow fan to take control first - Fixed clash when multiple zones and trips controlling same cdev 1.5.4 - Use Processor thermal device in lieu of CPU zone when present - Haswell/Skylake PCH sensor - Fix regression in LCD/Backlight path Release 1.5.3 - PCH sensor support Release 1.5.2 - Security bug for bios lock fix Release 1.5.1 - Regression fix for the default config file location Release 1.5 - Default warning level increase so that doesn't print much in logs - Add new feature to set specific target state on reaching a threshold, this allows multiple thresholds (trips) - Android update for build - Additional backlight devices - New option to specify config file via command line - Prevent adding cooling device in /etc via dbus - Whitelist of processor models, to avoid startup on server platforms Release 1.4.3 - One new dbus message to get temp - Fixes to prevent warnings Release 1.4 - Extension of DBUS I/F for developing Monitoring and Control GUI - Added exampled to thermal-conf man page - Support INT340X class of thermal control introduced in kernel 4.0 - Reinit without restart thermald to load new parameters like new control temperature - Fix indexes when Linux thermal sysfs doesn't have contiguous zone numbering - Support for new Intel SoC platforms - Introduce back-light control as the Linux back light cooling device is removed - Restore modified passive trip points in thermal zones on exit - Virtual Sensor definition - Fix loop when uevents floods the system - Error message removal for rapl sysfs traversal - Coverity error Release 1.3 - Auto creation of configuration based on ACPI thermal relationship table - Default CPU bound load check for unbinded thermal sensors Release 1.2 - Several fixes for Klocworks and Coverity scans (0 issues remaining) - Baytrail RAPL support as this doesn't have max power limit value Release 1.1 - Use powercap Intel RAPL driver - Use skin temperature sensor by default if available - Specify thermal relationship - Clean up for MSR related controls as up stream kernel driver are capable now - Override capability of thermal sysfs for a specific sensor or zone - Friendly to new thermal sysfs Release 1.04 - Android and chrome os integration - Minor fixes for buggy max temp Release 1.03 - Allow negative step increments while configuring via XML - Use powercap RAPL driver I/F - Additional cpuids in the list - Add man page with details of usage - Added P state turbo on/off Release 1.02 - Allow user to change the max temperature via dbus message - Allow user to change the cooling method order via an XML configuration - Upstart fixes - Valgrind and zero warnings on build Release 1.01 - Implement RAPL using MSRs. - User can configure cooling device order via XML config file - Fix sensor path configuration for thermal-conf.xml, so that user cn specify custom sensor paths - Use CPU max scaling frequency to control CPU Frequencies - RPM generation scripts - Build and formatting fixes from Alexander Bersenev Release 1.0 - Tested on multiple platforms - Using PID version 0.9 - Replaced netlink with uevents - Fix issue with pre-configured thermal data to control daemon - Use pthreads version 0.8 - Fix RAPL PATH, which is submitted upstream - Handle case when there is no MSR access from user mode - Allow non Intel CPUs version 0.7 - Conditional per cpu control - Family id check - If no max use offset from critical temperature - Switch to hwmon if there is no coretemp - Error handling if MSR support is not enabled in kernel - Code clean up and comments Version 0.6 - Use Intel P state driver to control P states - Use RAPL cooling device - Fix valgrind reported errors and cleanup - Add document Version 0.5 - License update to GPL v2 or later - Change dbus session bus to system - Load thermal-conf.xml data if exact UUID match Version 0.4 - Added power clamp driver interface - Added per cpu controls by trying to calibrate in the background to learn sensor cpu relationship - Optimized p states and turbo states and cleaned up - systemd and service start stop interface Version 0.3 - Added P states t states turbo states as the cooling methods - No longer depend on any thermal sysfs, zone cooling device by default - Uses DTS core temperature and p/turbo/t states to cool system - By default only will use DTS core temperature and p/turbo/t states only - All the previous controls based on the zones/cdevs and XML configuration is only done, when activated via command line - The set points are calculated and stored in a config file when it hits thermal threshold and adjusted based on slope and angular increments to dynamically adjust set point Version 0.2 - Define XML interface to set configuration data. Refer to thermal-conf.xml. This allows overriding buggy Bios thermal comfiguration and also allows extending the capability. - Use platform DMI UUID to index into configuration data. If there is no UUID match, falls back to thermal sysfs - Terminate interface - Takes over control from kernel thermal processing - Clean up of classes. Version 0.1 - Dbus interface to set preferred policy: "performance", "quiet/power", "disabled" - Defines a C++ classes for zones, cooling devices, trip points, thermal engine - Methods can be overridden in a custom class to modify default behaviour - Read thermal zone and cooling devices, trip points etc, - Read temperature via netlink notification or via polling configurable via command line - Once a trip point is crossed, activate the associate cooling devices. Start with min tstate to max tstate for each cooling device. - Based on active or passive settings it decides the cooling devices 07070100000005000081A400000000000000000000000166853FAC000001A9000000000000000000000000000000000000003000000000thermal_daemon-2.5.7.17.git+fc7464a/SECURITY.md# Security Policy ## Report a Vulnerability Please report security issues or vulnerabilities to the [Intel Security Center]. For more information on how Intel works to resolve security issues, see [Vulnerability Handling Guidelines]. [Intel Security Center]:https://www.intel.com/security [Vulnerability Handling Guidelines]:https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html 07070100000006000081ED00000000000000000000000166853FAC00000152000000000000000000000000000000000000002F00000000thermal_daemon-2.5.7.17.git+fc7464a/autogen.sh#!/bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. olddir=`pwd` cd "$srcdir" aclocal --install || exit 1 gtkdocize --copy --flavour no-tmpl || exit 1 autoreconf --install --verbose || exit 1 cd "$olddir" if test -z "$NO_CONFIGURE"; then $srcdir/configure "$@" && echo "Now type 'make' to compile `basename $srcdir`." fi 07070100000007000081A400000000000000000000000166853FAC00000F08000000000000000000000000000000000000003100000000thermal_daemon-2.5.7.17.git+fc7464a/configure.acAC_PREREQ(1.0) m4_define([td_major_version], [2]) m4_define([td_minor_version], [5.8-rc1]) m4_define([td_version], [td_major_version.td_minor_version]) AC_INIT([thermald], [td_version], [], [thermald]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR(build-aux) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.11 foreign no-define subdir-objects]) AM_MAINTAINER_MODE([enable]) GTK_DOC_CHECK([1.11],[--flavour no-tmpl]) AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is])) if test -n "$with_dbus_sys_dir" ; then DBUS_SYS_DIR="$with_dbus_sys_dir" else DBUS_SYS_DIR="/usr/share/dbus-1/system.d" fi AC_SUBST(DBUS_SYS_DIR) AC_ARG_WITH(dbus-power-group, AS_HELP_STRING([--with-dbus-power-group=GROUP], [group that is allowed to connect to D-BUS service])) if test -n "$with_dbus_power_group" ; then DBUS_POWER_GROUP="$with_dbus_power_group" else DBUS_POWER_GROUP="power" fi AC_SUBST(DBUS_POWER_GROUP) # paths AC_SUBST(tdbinary, "$sbindir/$PACKAGE", [Binary executable]) AC_SUBST(tdconfdir, "$sysconfdir/$PACKAGE", [Configuration directory]) AC_SUBST(tdrundir, "$localstatedir/run/$PACKAGE", [Runtime state directory]) PKG_PROG_PKG_CONFIG AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) if test "x$with_systemdsystemunitdir" != xno; then AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) fi AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) # print configuration echo echo "System paths:" echo " prefix: $prefix" echo " exec_prefix: $exec_prefix" echo " systemdunitdir: $with_systemdsystemunitdir" echo " tdbinary: $tdbinary" echo " tdconfdir: $tdconfdir" echo " tdrundir: $tdrundir" echo GETTEXT_PACKAGE=thermald AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package]) GLIB_VERSION_DEFINES="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26" DBUS_CFLAGS="$DBUS_CFLAGS $GLIB_VERSION_DEFINES" PKG_CHECK_MODULES(GLIB, gio-unix-2.0 >= 2.22 gmodule-2.0 glib-2.0) GLIB_CFLAGS="$GLIB_CFLAGS $GLIB_VERSION_DEFINES" AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) PKG_CHECK_MODULES(XML, libxml-2.0 >= 2.4) AC_PATH_PROG([GDBUS_CODEGEN],[gdbus-codegen]) PKG_CHECK_MODULES(UPOWER, upower-glib) PKG_CHECK_MODULES(EVDEV, libevdev) AC_PROG_CC AC_PROG_CPP AC_PROG_CXX AC_PROG_INSTALL AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T m4_ifdef([AX_CHECK_COMPILE_FLAG], [ # Check for -Werror flag support. for flag in \ -Wall \ -Wclobbered \ -Wempty-body \ -Wformat \ -Wignored-qualifiers \ -Wmissing-field-initializers \ -Wreorder \ -Wreturn-type \ -Wsign-compare \ -Wtype-limits \ -Wuninitialized \ -Wunused-but-set-variable \ ; do AX_CHECK_COMPILE_FLAG([-Werror ${flag}], [CXXFLAGS="$CXXFLAGS ${flag}"]) done # Check for c++0x support. AX_CHECK_COMPILE_FLAG([-std=c++0x], [CXXFLAGS="$CXXFLAGS -std=c++0x"]) ], [AC_MSG_ERROR([AX_CHECK_COMPILE_FLAG not found, you'll need to install autoconf-archive])]) AC_ARG_ENABLE(werror, AS_HELP_STRING([--disable-werror], [Disable -Werror])) AS_IF([test "x$enable_werror" != "xno"], [CXXFLAGS="$CXXFLAGS -Werror"]) AC_CONFIG_FILES([Makefile docs/Makefile docs/version.xml data/Makefile]) AC_ARG_ENABLE(gdbus, [AS_HELP_STRING([--disable-gdbus], [Switch DBus backend to glib-dbus. (Default: GDBus)])], [], [AC_DEFINE([GDBUS], [1], [Enable GDBus support])]) AC_OUTPUT 07070100000008000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002900000000thermal_daemon-2.5.7.17.git+fc7464a/data07070100000009000081A400000000000000000000000166853FAC000004B5000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/data/Makefile.aminclude $(GLIB_MAKEFILE) if HAVE_SYSTEMD systemdsystemunit_DATA = \ thermald.service thermald.service: thermald.service.in @$(service_edit) $< >$@ servicedir = $(datadir)/dbus-1/system-services service_in_files = org.freedesktop.thermald.service.in service_DATA = $(service_in_files:.service.in=.service) $(service_DATA): $(service_in_files) Makefile @$(service_edit) $< >$@ service_edit = sed \ -e 's|@bindir[@]|$(bindir)|g' \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' endif # HAVE_SYSTEMD dbusservice_edit = sed \ -e 's|@dbusservicepowergrp[@]|$(dbusservicepowergrp)|g' org.freedesktop.thermald.conf: org.freedesktop.thermald.conf.in @$(dbusservice_edit) $< >$@ dbusservicepowergrp = $(DBUS_POWER_GROUP) dbusservicedir = $(DBUS_SYS_DIR) dbusservice_in_files = org.freedesktop.thermald.conf.in dbusservice_DATA = $(dbusservice_in_files:.conf.in=.conf) tdconfigdir = $(tdconfdir) tdconfig_DATA = \ thermal-cpu-cdev-order.xml EXTRA_DIST = \ thermald.service.in \ org.freedesktop.thermald.service.in \ $(dbusservice_DATA) \ $(tdconfig_DATA) CLEANFILES = thermald.service org.freedesktop.thermald.service 0707010000000A000081A400000000000000000000000166853FAC000003C2000000000000000000000000000000000000004A00000000thermal_daemon-2.5.7.17.git+fc7464a/data/org.freedesktop.thermald.conf.in<?xml version="1.0"?> <!--*-nxml-*--> <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy user="root"> <allow own="org.freedesktop.thermald"/> <allow send_destination="org.freedesktop.thermald"/> <allow receive_sender="org.freedesktop.thermald"/> </policy> <!-- Only allow members of the @dbusservicepowergrp@ group to communicate with the daemon --> <policy context="default"> <deny send_destination="org.freedesktop.thermald"/> <allow receive_sender="org.freedesktop.thermald"/> </policy> <policy group="@dbusservicepowergrp@"> <allow send_destination="org.freedesktop.thermald"/> <allow receive_sender="org.freedesktop.thermald"/> </policy> </busconfig> 0707010000000B000081A400000000000000000000000166853FAC0000007D000000000000000000000000000000000000004D00000000thermal_daemon-2.5.7.17.git+fc7464a/data/org.freedesktop.thermald.service.in[D-BUS Service] Name=org.freedesktop.thermald Exec=/bin/false User=root SystemdService=dbus-org.freedesktop.thermald.service 0707010000000C000081A400000000000000000000000166853FAC000011FF000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/data/thermal-conf.xml<?xml version="1.0"?> <!-- use "man thermal-conf.xml" for details --> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name>Generic X86 Laptop Device</Name> <ProductName>EXAMPLE_SYSTEM</ProductName> <Preference>QUIET</Preference> <ThermalSensors> <ThermalSensor> <Type>TSKN</Type> <AsyncCapable>1</AsyncCapable> </ThermalSensor> </ThermalSensors> <ThermalZones> <ThermalZone> <Type>SKIN</Type> <TripPoints> <TripPoint> <SensorType>TSKN</SensorType> <Temperature>55000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>rapl_controller</type> <influence> 100 </influence> <SamplingPeriod> 16 </SamplingPeriod> </CoolingDevice> <CoolingDevice> <index>2</index> <type>intel_powerclamp</type> <influence> 100 </influence> <SamplingPeriod> 12 </SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> <!-- Thermal configuration example only --> <Platform> <Name>Example Platform Name</Name> <!--UUID is optional, if present this will be matched --> <!-- Both product name and UUID can contain wild card "*", which matches any platform --> <UUID>Example UUID</UUID> <ProductName>Example Product Name</ProductName> <Preference>QUIET</Preference> <ThermalSensors> <ThermalSensor> <!-- New Sensor with a type and path --> <Type>example_sensor_1</Type> <Path>/some_path</Path> <AsyncCapable>0</AsyncCapable> </ThermalSensor> <ThermalSensor> <!-- Already present in thermal sysfs, enable this or add/change config For example, here we are indicating that sensor can do async events to avoid polling --> <Type>example_thermal_sysfs_sensor</Type> <!-- If async capable, then we don't need to poll --> <AsyncCapable>1</AsyncCapable> </ThermalSensor> <ThermalSensor> <!-- Example of a virtual sensor. This sensor depends on other real sensor or virtual sensor. E.g. here the temp will be temp of example_sensor_1 * 0.5 + 10 --> <Type>example_virtual_sensor</Type> <Virtual>1</Virtual> <SensorLink> <SensorType>example_sensor_1</SensorType> <Multiplier> 0.5 </Multiplier> <Offset> 10 </Offset> </SensorLink> </ThermalSensor> </ThermalSensors> <ThermalZones> <ThermalZone> <Type>Example Zone type</Type> <TripPoints> <TripPoint> <SensorType>example_sensor_1</SensorType> <!-- Temperature at which to take action --> <Temperature> 75000 </Temperature> <!-- max/passive/active If a MAX type is specified, then daemon will use PID control to aggressively throttle to avoid reaching this temp. --> <type>max</type> <!-- SEQUENTIAL | PARALLEL When a trip point temp is violated, then number of cooling device can be activated. If control type is SEQUENTIAL then It will exhaust first cooling device before trying next. --> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>example_cooling_device</type> <!-- Influence will be used order cooling devices. First cooling device will be used, which has highest influence. --> <influence> 100 </influence> <!-- Delay in using this cdev, this takes some time too actually cool a zone --> <SamplingPeriod> 12 </SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <!-- Cooling device can be specified by a type and optionally a sysfs path If the type already present in thermal sysfs no need of a path. Compensation can use min/max and step size to increasing cool the system. Debounce period can be used to force a waiting period for action --> <Type>example_cooling_device</Type> <MinState>0</MinState> <IncDecStep>10</IncDecStep> <ReadBack> 0 </ReadBack> <MaxState>50</MaxState> <DebouncePeriod>5000</DebouncePeriod> <!-- If there are no PID parameter compensation increase step wise and exponentaially if single step is not able to change trend. Alternatively a PID parameters can be specified then next step will use PID calculation using provided PID constants. -->> <PidControl> <kp>0.001</kp> <kd>0.0001</kd> <ki>0.0001</ki> </PidControl> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> <!-- END --> 0707010000000D000081A400000000000000000000000166853FAC000001FC000000000000000000000000000000000000004400000000thermal_daemon-2.5.7.17.git+fc7464a/data/thermal-cpu-cdev-order.xml <!-- Specifies the order of compensation to cool CPU only. There is a default already implemented in the code, but this file can be used to change order The Following cooling device can present --> <CoolingDeviceOrder> <!-- Specify Cooling device order --> <CoolingDevice>rapl_controller</CoolingDevice> <CoolingDevice>intel_pstate</CoolingDevice> <CoolingDevice>intel_powerclamp</CoolingDevice> <CoolingDevice>cpufreq</CoolingDevice> <CoolingDevice>Processor</CoolingDevice> </CoolingDeviceOrder> 0707010000000E000081A400000000000000000000000166853FAC00000135000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/data/thermald.service.in[Unit] Description=Thermal Daemon Service ConditionVirtualization=no [Service] Type=dbus SuccessExitStatus=2 BusName=org.freedesktop.thermald ExecStart=@sbindir@/thermald --systemd --dbus-enable --adaptive Restart=on-failure [Install] WantedBy=multi-user.target Alias=dbus-org.freedesktop.thermald.service 0707010000000F000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/distribution_integration07070100000010000081A400000000000000000000000166853FAC000001EB000000000000000000000000000000000000005E00000000thermal_daemon-2.5.7.17.git+fc7464a/distribution_integration/android_dbus_service_informationAndroid Integration Thermald required dbus daemon. To start dbus daemon add the following to your init.rc. service dbus /system/bin/dbus-daemon --system --nofork class main socket dbus stream 660 root root user root group system inet To start thermald as a service service thermald /system/bin/thermald --dbus-enable user root group system class main disabled oneshot By default it is disabled. So need explicit command somewhere: "start thermald" 07070100000011000081A400000000000000000000000166853FAC000002A1000000000000000000000000000000000000005900000000thermal_daemon-2.5.7.17.git+fc7464a/distribution_integration/chromeos_gentoo_ebuild_spec# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ EAPI=4 inherit autotools systemd DESCRIPTION="Thermal daemon for Intel architectures" HOMEPAGE="https://github.com/intel/thermal_daemon" SRC_URI="https://github.com/intel/thermal_daemon/archive/v${PV}.tar.gz -> ${P}.tar.gz" LICENSE="GPL-2+" SLOT="0" KEYWORDS="amd64 ia64 x86" IUSE="" S=${WORKDIR}/thermal_daemon-${PV} DEPEND="dev-libs/dbus-glib dev-libs/libxml2" RDEPEND="${DEPEND}" DOCS=( ThermalDaemon_Introduction.pdf README.txt ) src_prepare() { eautoreconf } src_configure() { econf --with-systemdsystemunitdir=$(systemd_get_unitdir) } 07070100000012000081ED00000000000000000000000166853FAC0000037E000000000000000000000000000000000000004900000000thermal_daemon-2.5.7.17.git+fc7464a/distribution_integration/make_rpm.sh#!/bin/bash BRANCH=$(git branch | grep '^\*' | sed 's/^..\(.*\)/\1/') HASH=$(git rev-parse ${BRANCH}) TAG=$(git describe --tags --abbrev=0) RELEASE=$(git describe --tags | cut -d- -f2 | tr - _) SHORT_COMMIT=$(git rev-parse HEAD | cut -c 1-7) sed -i "s,^%global commit.*,%global commit ${HASH}," thermal-daemon.spec sed -i "s,^Version:.*,Version: ${TAG}," thermal-daemon.spec sed -i "s,^Release.*,Release: ${RELEASE}%{?dist}," thermal-daemon.spec rsync -a --exclude='rpms' --exclude='.git' ../../thermal_daemon . mv thermal_daemon thermal_daemon-${SHORT_COMMIT} tar czf ~/rpmbuild/SOURCES/thermal_daemon-${TAG}-${SHORT_COMMIT}.tar.gz thermal_daemon-${SHORT_COMMIT} rpmbuild -bs thermal-daemon.spec # /usr/bin/mock -r fedora-21-x86_64 ~/SRPMS/thermal-daemon-*.src.rpm rpmbuild -ba thermal-daemon.spec rpmlint thermal-daemon.spec ~/rpmbuild/SRPMS/thermal* rm -rf thermal_daemon-${SHORT_COMMIT} 07070100000013000081A400000000000000000000000166853FAC00000A80000000000000000000000000000000000000005100000000thermal_daemon-2.5.7.17.git+fc7464a/distribution_integration/thermal-daemon.specName: thermald Version: 1.6 Release: 1%{?dist} Summary: The "Linux Thermal Daemon" program from 01.org License: GPLv2+ URL: https://github.com/intel/thermal_daemon %global pkgname thermal_daemon Source0: https://github.com/intel/thermal_daemon/archive/v%{version}.tar.gz BuildRequires: automake BuildRequires: autoconf BuildRequires: glib-devel BuildRequires: dbus-glib-devel BuildRequires: libxml2-devel BuildRequires: systemd Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units %description Thermal Daemon monitors and controls platform temperature. %prep %setup -qn %{pkgname}-%{version} %build autoreconf -f -i %configure prefix=%{_prefix} make %{?_smp_mflags} # Although there is a folder test in the upstream repo, this is not for self tests. # Hence check section is not present. %install %make_install DESTDIR=%{buildroot} %post %systemd_post thermald.service %preun %systemd_preun thermald.service %postun %systemd_postun_with_restart thermald.service %files %{_sbindir}/thermald %{_datadir}/dbus-1/system.d/org.freedesktop.thermald.conf %{_datadir}/dbus-1/system-services/org.freedesktop.thermald.service %config(noreplace) %{_sysconfdir}/thermald/thermal-conf.xml %config(noreplace) %{_sysconfdir}/thermald/thermal-cpu-cdev-order.xml %doc COPYING README.txt %{_mandir}/man8/thermald.8.gz %{_mandir}/man5/thermal-conf.xml.5.gz %{_unitdir}/thermald.service %exclude %{_sysconfdir}/init %changelog * Mon Apr 03 2017 James Ye <jye836@gmail.com> 1.6-1 - Updated to thermal daemon 1.6 * Tue Oct 25 2016 Michael P. Moran <> 1.5-4 - Updated spec file for version 1.5.4 * Tue Mar 29 2016 Alexey Slaykovsky <alexey@slaykovsky.com> 1.5-3 - Updated spec file for a Thermal Daemon 1.5.3 version * Fri Oct 17 2014 António Meireles <antonio.meireles@reformi.st> 1.3-3 - update spec file * Tue Oct 01 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.03-1 - Upgraded to thermal daemon 1.03 * Mon Jun 24 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.02-5 - Replaced underscore with dash in the package name * Thu Jun 20 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.02-4 - Resolved prefix and RPM_BUILD_ROOT as per review comments * Wed Jun 19 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.02-3 - Removed libxml2 requirement and uses shortcommit in the Source0 * Tue Jun 18 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.02-2 - Update spec file after first review * Fri Jun 14 2013 Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> 1.02-1 - Initial package 07070100000014000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002900000000thermal_daemon-2.5.7.17.git+fc7464a/docs07070100000015000081A400000000000000000000000166853FAC000004C9000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/docs/Makefile.amNULL = AUTOMAKE_OPTIONS = 1.7 # The name of the module. DOC_MODULE=thermal_daemon # The top-level SGML file. DOC_MAIN_SGML_FILE=thermal_daemon-docs.xml # Extra options to supply to gtkdoc-scan SCAN_OPTIONS= \ --ignore-headers="config.h thd*.h thermald.h acpi_*.h" \ --rebuild-sections --rebuild-types # The directory containing the source code. Relative to $(srcdir) DOC_SOURCE_DIR= # Used for dependencies HFILE_GLOB= CFILE_GLOB= # Headers to ignore IGNORE_HFILES= \ $(NULL) GTKDOC_LIBS = # Extra options to supply to gtkdoc-mkdb MKDB_OPTIONS=--sgml-mode --output-format=xml docs-thd_dbus_interface.xml: $(top_srcdir)/src/thd_dbus_interface.xml $(AM_V_GEN)$(GDBUS_CODEGEN) --generate-docbook=docs $< # Non-autogenerated SGML files to be included in $(DOC_MAIN_SGML_FILE) content_files = \ version.xml \ docs-thd_dbus_interface.xml \ $(NULL) # FIXME MAINTAINERCLEANFILES = \ *~ \ Makefile.in \ thermal_daemon.types \ thermal_daemon-*.txt \ $(NULL) include $(top_srcdir)/gtk-doc.make EXTRA_DIST = thermal_daemon-docs.xml CLEANFILES += \ docs-thermal_daemon.xml \ $(NULL) # Version information for marking the documentation EXTRA_DIST += version.xml.in -include $(top_srcdir)/git.mk 07070100000016000081A400000000000000000000000166853FAC000009B4000000000000000000000000000000000000004100000000thermal_daemon-2.5.7.17.git+fc7464a/docs/thermal_daemon-docs.xml<?xml version="1.0"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ <!ENTITY version SYSTEM "version.xml"> ]> <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> <bookinfo> <title>Thermal Daemon Reference Manual</title> <releaseinfo>Version &version;</releaseinfo> <authorgroup> <author> <firstname>Bastien</firstname> <surname>Nocera</surname> <affiliation> <address> <email>hadess@hadess.net</email> </address> </affiliation> </author> </authorgroup> <copyright> <year>2020</year> <holder>Red Hat, Inc.</holder> </copyright> <legalnotice> <para> Permission is granted to copy, distribute and/or modify this document under the terms of the <citetitle>GNU Free Documentation License</citetitle>, Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the <citetitle>GNU Free Documentation License</citetitle> from the Free Software Foundation by visiting <ulink type="http" url="https://www.fsf.org">their Web site</ulink> or by writing to: <address> The Free Software Foundation, Inc., <street>59 Temple Place</street> - Suite 330, <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode>, <country>USA</country> </address> </para> <para> Many of the names used by companies to distinguish their products and services are claimed as trademarks. Where those names appear in any GNOME documentation, and those trademarks are made aware to the members of the GNOME Documentation Project, the names have been printed in caps or initial caps. </para> </legalnotice> </bookinfo> <reference id="ref-dbus"> <title>D-Bus API Reference</title> <partintro> <para> This part documents the D-Bus interface used to access the Thermal Daemon service. </para> </partintro> <xi:include href="docs-org.freedesktop.thermald.xml"/> </reference> <index> <title>Index</title> </index> <!-- License --> <appendix id="license"> <title>License</title> <para> <programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../COPYING" parse="text"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting> </para> </appendix> </book> 07070100000017000081A400000000000000000000000166853FAC0000000A000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/docs/version.xml.in@VERSION@ 07070100000018000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002800000000thermal_daemon-2.5.7.17.git+fc7464a/man07070100000019000081A400000000000000000000000166853FAC000045B9000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/man/thermal-conf.xml.5.\" thermal-conf.xml(5) manual page .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public Licence along .\" with this manual; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Copyright (C) 2012 Intel Corporation. All rights reserved. .\" .TH thermal-conf.xml "5" "Dec 18, 2018" .SH NAME thermal-conf.xml \- Configuration file for thermal daemon .SH SYNOPSIS $(TDCONFDIR)/etc/thermald/thermal-conf.xml .SH DESCRIPTION .B thermal-conf.xml is a configuration file for the thermal daemon. It is used to configure thermal sensors, zone and cooling devices. The location of this file depends on the configuration option used during build time. .PP The terminology used in this file conforms to "Advanced Configuration and Power Interface Specification". The ACPI thermal model is based around conceptual platform regions called thermal zones that physically contain devices, thermal sensors, and cooling controls. For example of a thermal zone can be a CPU or a laptop cover. A zone can contain multiple sensors for monitoring temperature. A cooling device provides interface to reduce the temperature of a source device, which causes increase in the temperature. An example of a cooling device is a FAN or some Linux driver which can throttle the source device. .PP A thermal zone configuration includes one or more trip points. A trip point is a temperature at which a cooling device needs to be activated. .PP A cooling device can be either active or passive. An example of an active device is a FAN, which will not reduce performance at the cost of consuming more power and noise. A passive device uses performance throttling to control temperature. In addition to cooling devices present in the thermal sysfs, the following cooling devices are built into the thermald, which can be used as valid cooling device type: .IP \(bu 2 rapl_controller .IP \(bu 2 intel_pstate .IP \(bu 2 cpufreq .IP \(bu 2 LCD .PP The thermal sysfs under Linux (/sys/class/thermal) provides a way to represent per platform ACPI configuration. The kernel thermal governor uses this data to keep the platform thermals under control. But there are some limitations, which thermald tries to resolve. For example: .IP \(bu 2 If the ACPI data is not optimized or buggy. In this case thermal-conf.xml can be used to correct the behavior without change in BIOS. .IP \(bu 2 There may be thermal zones exposed by the thermal sysfs without associated cooling actions. In this case thermal conf.xml can be used to tie the cooling devices to those zones. .IP \(bu 2 The best cooling method may not be in the thermal sysfs. In this case thermal-conf.xml can be used to bind a zone to an external cooling device. .IP \(bu 2 Specify thermal relationships. A zone can be influenced by multiple source devices with varying degrees. In this case thermal-conf.xml can be used to define the relative influence for apply compensation. .SH FILE FORMAT The configuration file format conforms to XML specifications. A set of tags defined to define platform, sensors, zones, cooling devices and trip points. .sp 1 .EX <ThermalConfiguration> <Platform> <Name>Example Platform Name</Name> <!-- UUID is optional, if present this will be matched. Both product name and UUID can contain wild card "*", which matches any platform. --> <UUID>Example UUID</UUID> <!-- configuration file format conforms to XML specifications. A set of tags defined to define platform, sensors, zones, cooling devices and trip points. --> <ProductName>Example Product Name</ProductName> <Preference>QUIET|PERFORMANCE</Preference> <!-- Quiet mode will only use passive cooling device. "PERFORMANCE" will only select active devices. --> <ThermalSensors> <ThermalSensor> <!-- New Sensor with a type and path --> <Type>example_sensor_1</Type> <Path>/some_path</Path> <AsyncCapable>0</AsyncCapable> </ThermalSensor> <ThermalSensor> <!-- Already present in thermal sysfs, enable this or add/change config For example, here we are indicating that sensor can do async events to avoid polling. --> <Type>example_thermal_sysfs_sensor</Type> <!-- If async capable, then we don't need to poll. --> <AsyncCapable>1</AsyncCapable> </ThermalSensor> </ThermalSensors> <ThermalZones> <ThermalZone> <Type>Example Zone type</Type> <TripPoints> <TripPoint> <SensorType>example_sensor_1</SensorType> <!-- Temperature at which to take action. --> <Temperature> 75000 </Temperature> <!-- max/passive/active If a MAX type is specified, then daemon will use PID control to aggressively throttle to avoid reaching this temp. --> <type>max</type> <!-- SEQUENTIAL | PARALLEL. When a trip point temp is violated, then number of cooling devices can be activated. If control type is SEQUENTIAL then, it will exhaust first cooling device before trying next. --> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>example_cooling_device</type> <!-- Influence will be used order cooling devices. First cooling device will be used, which has highest influence. --> <influence> 100 </influence> <!-- Delay in using this cdev, this takes some time too actually cool a zone. --> <SamplingPeriod> 12 </SamplingPeriod> <!-- Set a specific state of this cooling device when this trip is violated. --> <TargetState> 6 </TargetState> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <!-- Cooling device can be specified by a type and optionally a sysfs path. If the type is already present in thermal sysfs, there is no need of a path. Compensation can use min/max and step size to increasing cool the system. Debounce period can be used to force a waiting period for action. --> <Type>example_cooling_device</Type> <MinState>0</MinState> <IncDecStep>10</IncDecStep> <ReadBack> 0 </ReadBack> <MaxState>50</MaxState> <DebouncePeriod>5000</DebouncePeriod> <!-- If there are no PID parameters, compensation increase step wise and exponentially (if single step is not able to change trend). Alternatively a PID parameters can be specified then next step will use PID calculation using provided PID constants. --> <PidControl> <kp>0.001</kp> <kd>0.0001</kd> <ki>0.0001</ki> </PidControl> <!-- Write some prefix attached to state value, like below the prefix is "level ". It will preserve spaces as entered when writing to sysfs. --> <WritePrefix>level </WritePrefix> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> .EE .SH EXAMPLE CONFIGURATIONS .B Example 1: This is a very simple configuration, to change the passive limit on the CPU. Instead of default, this new temperature 86C in the configuration is used. This will start cooling, once the temperature reaches 86C. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Override CPU default passive</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>cpu</Type> <TripPoints> <TripPoint> <Temperature>86000</Temperature> <type>passive</type> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> .EE .PP .B Example 2: In this configuration, we are controlling backlight when some sensor "SEN2" reaches 60C. Here "LCD" is a standard cooling device, which uses Linux backlight sysfs interface. "LCD_Zone" is a valid thermal zone in Linux thermal sysfs on the test platform, hence we don't need to provide path for sysfs for "LCD_Zone". The Linux thermal sysfs is already parsed and loaded by the thermald program. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Change Backlight</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>LCD_Zone</Type> <TripPoints> <TripPoint> <SensorType>SEN2</SensorType> <Temperature>60000</Temperature> <type>passive</type> <CoolingDevice> <Type>LCD</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> .EE .PP .B Example 3: In this example Lenovo Thinkpad X220 and fan speed is controlled. Here a cooling device "_Fan", can be controlled via sysfs /sys/devices/platform/thinkpad_hwmon/pwm1. When the x86_pkg_temp reaches 45C, Fan is started with increasing speeds, if the temperature can't be controlled at 45C. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Lenovo ThinkPad X220</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>x86_pkg_temp</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>45000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>_Fan</type> <influence> 100 </influence> <SamplingPeriod> 12 </SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Type>_Fan</Type> <Path>/sys/devices/platform/thinkpad_hwmon/pwm1</Path> <MinState>0</MinState> <IncDecStep>30</IncDecStep> <ReadBack> 0 </ReadBack> <MaxState>255</MaxState> <DebouncePeriod>5</DebouncePeriod> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> .EE .PP .B Example 4: The following example shows how PID can be used. Here once temperature exceeds 80C, compensation is calculated using PID using 80C as set point of PID. The compensation depends on error from the set point. Here the default built in processor cooling device is used with min state as 0 and max state as 10. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Use PID param </Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>x86_pkg_temp</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>80000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <type>Processor</type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Type>Processor</Type> <PidControl> <kp>0.0002</kp> <kd>0</kd> <ki>0</ki> </PidControl> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> .EE .PP .B Example 5: The following example shows how to control Fan when the sysfs expects some string prefix. For example instead of just write a number to fan control sysfs, the interface requires "level " in front of the speed index value. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Use Fan control first then CPU throttle </Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>x86_pkg_temp</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>80000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <type>_fan_</type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Type>_fan_</Type> <Path>/proc/acpi/ibm/fan</Path> <WritePrefix>level </WritePrefix> <MinState>0</MinState> <MaxState>5</MaxState> <DebouncePeriod>10</DebouncePeriod> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> .EE .PP Example 6: Similar to example 5, but write different speeds at different temperatures. .sp 1 .EX <?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Use Fan control first then CPU throttle </Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>x86_pkg_temp</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>80000</Temperature> <type>passive</type> <CoolingDevice> <type>_fan_</type> <TargetState>1</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>85000</Temperature> <type>passive</type> <CoolingDevice> <type>_fan_</type> <TargetState>2</TargetState> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Type>_fan_</Type> <Path>/proc/acpi/ibm/fan</Path> <WritePrefix>level </WritePrefix> <MinState>0</MinState> <MaxState>5</MaxState> <DebouncePeriod>10</DebouncePeriod> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> .EE .PP Example 7: Use RAPL power limits to control. .sp 1 .EX <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> TEST </Name> <ProductName>Example_RAPL_Power </ProductName> <Preference>QUIET</Preference> <PPCC> <!-- Specify the Maximum/Minimum RAPL power limit for the platform. The limits are in milli watts. The step size to reduce/increase for each sampling interval Time window in miili seconds. --> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMaximum>15000</PowerLimitMaximum> <PowerLimitMinimum>2000</PowerLimitMinimum> <TimeWindowMinimum>20</TimeWindowMinimum> <TimeWindowMaximum>30</TimeWindowMaximum> <StepSize>1000</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>TestZone</Type> <TripPoints> <TripPoint> <SensorType>SEN3</SensorType> <Temperature>50000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>3</SamplingPeriod> <TargetState>2147483647</TargetState> <!-- This setting means that when SEN3 reaches 50C, set the RAPL max power limit to whatever the maximum power limit of the platform. --> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>SEN3</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>3</SamplingPeriod> <TargetState>8500000</TargetState> <!-- This setting means that when SEN3 reaches 52C, set the RAPL max power limit to 8.5W. --> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>SEN3</SensorType> <Temperature>60000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>3</SamplingPeriod> <TargetState>4500000</TargetState> <!-- This setting means that when SEN3 reaches 60C, set the RAPL max power limit to 4.5W. --> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>SEN3</SensorType> <Temperature>65000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>3</SamplingPeriod> <!-- This setting means that when SEN3 reaches 65C, set the RAPL max power limit to minimum power limit for the platform. --> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> .EE 0707010000001A000081A400000000000000000000000166853FAC0000154C000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/man/thermald.8.\" thermald (8) manual page .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public Licence along .\" with this manual; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Copyright (C) 2012 Intel Corporation. All rights reserved. .\" .TH thermald "8" "May 8, 2013" .SH NAME thermald \- start Linux thermal daemon .SH SYNOPSIS .B thermald .RI " [ " OPTIONS " ] .SH DESCRIPTION .B thermald is a Linux daemon used to prevent the overheating of platforms. This daemon monitors temperature and applies compensation using available cooling methods. By default, it monitors CPU temperature using available CPU digital temperature sensors and maintains CPU temperature under control, before HW takes aggressive correction action. Thermal daemon looks for thermal sensors and thermal cooling drivers in the Linux thermal sysfs (/sys/class/thermal) and builds a list of sensors and cooling drivers. Each of the thermal sensors can optionally be binded to a cooling driver by the in kernel drivers. In this case the Linux kernel thermal core can directly take actions based on the temperature trip points, for each sensor and associated cooling device. For example a trip temperature X in a sensor can be associates a cooling driver Y. So when the sensor temperature = X, the cooling driver "Y" is activated. Thermal daemon allows one to change this relationship or add new one via a thermal configuration file (thermal-conf.xml). This file is automatically created (thermal-conf.xml.auto) and used, if the platform has ACPI thermal relationship table. If not this needs to be manually configured. For manual configuration refer to the manual page of the thermal-conf.xml. In some newer platforms the auto creation of the config file is done by a companion tool "dptfxtract". This tool can be downloaded from "https://github.com/intel/dptfxtract". It is suggested as parts of the install process, run dptfxtract. There can be multiple configuration files. User can select a configuration file via -config-file option to override the default selection. The default selection picks one of the file in the following order: - /etc/thermald/thermal-conf.xml.auto - /var/run/thermald/thermal-conf.xml.auto - /etc/thermald/thermal-conf.xml (*Assuming configure prefix=/ is used during build.) There is another companion tool "ThermalMonitor", which presents a graphical front end. This allows the monitoring of sensors and changing of thermal trips to give the user more control. The source code of "ThermalMonitor" is a part of the thermald github source, in the tools folder. .SH OPTIONS .TP .B \-h, \-\-help Show help options. .TP .B \-\-version Print thermald version and exit. .TP .B \-\-no-daemon Don't become a daemon: Default is daemon mode. .TP .B \-\-loglevel=info log severity: info level and up. .TP .B \-\-loglevel=debug log severity: debug level and up: Max logging. .TP .B \-\-poll-interval Poll interval in seconds: Poll for zone temperature changes. To disable polling, set to zero. Polling can only be disabled, if available temperature sensors can notify temperature change asynchronously. .TP .B \-\-dbus-enable Enable Dbus. .TP .B \-\-exclusive-control Act as exclusive thermal controller. This will use user-space governor for thermal sysfs and take over control. .TP .B \-\-ignore-cpuid-check Ignore cpuid check for supported CPU models. .TP .B \-\-config-file Specify thermal-conf.xml path and ignore default thermal-conf.xml. .TP .B \-\-ignore-default-control Ignore default CPU temperature control. Strictly follow thermal-conf.xml or thermal-conf.xml.auto. .TP .B \-\-workaround-enabled Enable special workarounds for RAPL MMIO power limit and TCC offset every 30 seconds. This helps to disable RAPL MMIO when not used and adjust TCC offset in certain Lenovo laptops. .TP .B \-\-disable-active-power Disable active power management. This will not set active power limits using RAPL MMIO. This may result in constrained performance, if the system boots up with lower power limits. .TP .B \-\-adaptive Use DPTF adaptive tables when present. This will ignore thermald config via xml files. .TP .B \-\-test-mode Force use adaptive mode and exit if not supported, instead of restarting in non adaptive mode. This option is primarily for developers. .TP .B \-\-systemd Assume that thermald is started by systemd. This will prevent running as daemon irrespective of --no-daemon option. .TP .B \-\-ignore-critical-trip If the configuration defined a critical temperature point, which is too low, this option will avoid shutting down the system on reaching this temperature limit. .SH SEE ALSO thermal-conf.xml(5) 0707010000001B000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002900000000thermal_daemon-2.5.7.17.git+fc7464a/misc0707010000001C000081A400000000000000000000000166853FAC0000083F000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/misc/valgrind.supp# Valgrind suppressions file for glib, stl string calls { Syscall param write Memcheck:Param ... fun:g_main_loop_run fun:main } { g_type_init_with_debug_flags Memcheck:Leak ... fun:g_type_init_with_debug_flags fun:main } { g_main_loop_run Memcheck:Leak fun:memalign ... fun:g_main_loop_run fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC?Ev fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs4_Rep9_S_createEmmRKSaIcE fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag fun:_ZNSsC1EPKcRKSaIcE fun:_ZN8cthd_msrC1Ev fun:_ZN15cthd_engine_dts26check_intel_p_state_driverEv fun:_ZN15cthd_engine_dts20read_cooling_devicesEv fun:_ZN11cthd_engine16thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC1Ev fun:_ZN??c_rapl_interfaceC?Ei fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC?Ev fun:_ZN??cthd_engine_dts??check_intel_p_state_driverEv fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSt?vectorIiSaIiEE??_M_insert_auxEN?__gnu_cxx??__normal_iteratorIPiS?_EERKi fun:_ZN??cthd_cdev_pstates?initEv fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } 0707010000001D000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002800000000thermal_daemon-2.5.7.17.git+fc7464a/src0707010000001E000081A400000000000000000000000166853FAC000022E7000000000000000000000000000000000000003200000000thermal_daemon-2.5.7.17.git+fc7464a/src/7zTypes.h// From Intel DPTF github project ESIF/Products/ESIF_CMP/Sources // Fix white spaces from the source. /* 7zTypes.h -- Basic types 2018-08-04 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #ifdef _WIN32 /* #include <windows.h> */ #endif #include <stddef.h> #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_BEGIN #define EXTERN_C_END #endif #endif EXTERN_C_BEGIN #define SZ_OK 0 #define SZ_ERROR_DATA 1 #define SZ_ERROR_MEM 2 #define SZ_ERROR_CRC 3 #define SZ_ERROR_UNSUPPORTED 4 #define SZ_ERROR_PARAM 5 #define SZ_ERROR_INPUT_EOF 6 #define SZ_ERROR_OUTPUT_EOF 7 #define SZ_ERROR_READ 8 #define SZ_ERROR_WRITE 9 #define SZ_ERROR_PROGRESS 10 #define SZ_ERROR_FAIL 11 #define SZ_ERROR_THREAD 12 #define SZ_ERROR_ARCHIVE 16 #define SZ_ERROR_NO_ARCHIVE 17 typedef int SRes; #ifdef _WIN32 /* typedef DWORD WRes; */ typedef unsigned WRes; #define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x) #else typedef int WRes; #define MY__FACILITY_WIN32 7 #define MY__FACILITY__WRes MY__FACILITY_WIN32 #define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000))) #endif #ifndef RINOK #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif typedef unsigned char Byte; typedef short Int16; typedef unsigned short UInt16; #ifdef _LZMA_UINT32_IS_ULONG typedef long Int32; typedef unsigned long UInt32; #else typedef int Int32; typedef unsigned int UInt32; #endif #ifdef _SZ_NO_INT_64 /* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. NOTES: Some code will work incorrectly in that case! */ typedef long Int64; typedef unsigned long UInt64; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 Int64; typedef unsigned __int64 UInt64; #define UINT64_CONST(n) n #else typedef long long int Int64; typedef unsigned long long int UInt64; #define UINT64_CONST(n) n ## ULL #endif #endif #ifdef _LZMA_NO_SYSTEM_SIZE_T typedef UInt32 SizeT; #else typedef size_t SizeT; #endif typedef int BoolInt; /* typedef BoolInt Bool; */ #define True 1 #define False 0 #ifdef _WIN32 #define MY_STD_CALL __stdcall #else #define MY_STD_CALL #endif #ifdef _MSC_VER #if _MSC_VER >= 1300 #define MY_NO_INLINE __declspec(noinline) #else #define MY_NO_INLINE #endif #define MY_FORCE_INLINE __forceinline #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_NO_INLINE #define MY_FORCE_INLINE #define MY_CDECL #define MY_FAST_CALL /* inline keyword : for C++ / C99 */ /* GCC, clang: */ /* #if defined (__GNUC__) && (__GNUC__ >= 4) #define MY_FORCE_INLINE __attribute__((always_inline)) #define MY_NO_INLINE __attribute__((noinline)) #endif */ #endif /* The following interfaces use first parameter as pointer to structure */ typedef struct IByteIn IByteIn; struct IByteIn { Byte (*Read)(const IByteIn *p); /* reads one byte, returns 0 in case of EOF or error */ }; #define IByteIn_Read(p) (p)->Read(p) typedef struct IByteOut IByteOut; struct IByteOut { void (*Write)(const IByteOut *p, Byte b); }; #define IByteOut_Write(p, b) (p)->Write(p, b) typedef struct ISeqInStream ISeqInStream; struct ISeqInStream { SRes (*Read)(const ISeqInStream *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ }; #define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size) /* it can return SZ_ERROR_INPUT_EOF */ SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size); SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType); SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf); typedef struct ISeqOutStream ISeqOutStream; struct ISeqOutStream { size_t (*Write)(const ISeqOutStream *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ }; #define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size) typedef enum { SZ_SEEK_SET = 0, SZ_SEEK_CUR = 1, SZ_SEEK_END = 2 } ESzSeek; typedef struct ISeekInStream ISeekInStream; struct ISeekInStream { SRes (*Read)(const ISeekInStream *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ SRes (*Seek)(const ISeekInStream *p, Int64 *pos, ESzSeek origin); }; #define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size) #define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) typedef struct ILookInStream ILookInStream; struct ILookInStream { SRes (*Look)(const ILookInStream *p, const void **buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) > input(*size)) is not allowed (output(*size) < input(*size)) is allowed */ SRes (*Skip)(const ILookInStream *p, size_t offset); /* offset must be <= output(*size) of Look */ SRes (*Read)(const ILookInStream *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ SRes (*Seek)(const ILookInStream *p, Int64 *pos, ESzSeek origin); }; #define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size) #define ILookInStream_Skip(p, offset) (p)->Skip(p, offset) #define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size) #define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size); SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType); SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size); typedef struct { ILookInStream vt; const ISeekInStream *realStream; size_t pos; size_t size; /* it's data size */ /* the following variables must be set outside */ Byte *buf; size_t bufSize; } CLookToRead2; void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead); #define LookToRead2_Init(p) { (p)->pos = (p)->size = 0; } typedef struct { ISeqInStream vt; const ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); typedef struct { ISeqInStream vt; const ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); typedef struct ICompressProgress ICompressProgress; struct ICompressProgress { SRes (*Progress)(const ICompressProgress *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ }; #define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize) typedef struct ISzAlloc ISzAlloc; typedef const ISzAlloc * ISzAllocPtr; struct ISzAlloc { void *(*Alloc)(ISzAllocPtr p, size_t size); void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ }; #define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size) #define ISzAlloc_Free(p, a) (p)->Free(p, a) /* deprecated */ #define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size) #define IAlloc_Free(p, a) ISzAlloc_Free(p, a) #ifndef MY_offsetof #ifdef offsetof #define MY_offsetof(type, m) offsetof(type, m) /* #define MY_offsetof(type, m) FIELD_OFFSET(type, m) */ #else #define MY_offsetof(type, m) ((size_t)&(((type *)0)->m)) #endif #endif #ifndef MY_container_of /* #define MY_container_of(ptr, type, m) container_of(ptr, type, m) #define MY_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m) #define MY_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m))) #define MY_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m)))) */ /* GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly" GCC 3.4.4 : classes with constructor GCC 4.8.1 : classes with non-public variable members" */ #define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m))) #endif #define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr)) /* #define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) */ #define CONTAINER_FROM_VTBL(ptr, type, m) MY_container_of(ptr, type, m) #define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) /* #define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL(ptr, type, m) */ #ifdef _WIN32 #define CHAR_PATH_SEPARATOR '\\' #define WCHAR_PATH_SEPARATOR L'\\' #define STRING_PATH_SEPARATOR "\\" #define WSTRING_PATH_SEPARATOR L"\\" #else #define CHAR_PATH_SEPARATOR '/' #define WCHAR_PATH_SEPARATOR L'/' #define STRING_PATH_SEPARATOR "/" #define WSTRING_PATH_SEPARATOR L"/" #endif EXTERN_C_END #endif 0707010000001F000081A400000000000000000000000166853FAC00007D6A000000000000000000000000000000000000003200000000thermal_daemon-2.5.7.17.git+fc7464a/src/LzmaDec.c// From Intel DPTF github project ESIF/Products/ESIF_CMP/Sources // Fix white spaces from the source. /* LzmaDec.c -- LZMA Decoder 2018-07-04 : Igor Pavlov : Public domain */ //#include "Precomp.h" #include <string.h> /* #include "CpuArch.h" */ #include "LzmaDec.h" // For MyMemCpy function only exclude #include "EsifSdl.h" #define MyMemcpy(dst, src, len) memcpy(dst, src, len) #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define TREE_GET_BIT(probs, i) { GET_BIT2(probs + i, i, ;, ;); } #define REV_BIT(p, i, A0, A1) IF_BIT_0(p + i) \ { UPDATE_0(p + i); A0; } else \ { UPDATE_1(p + i); A1; } #define REV_BIT_VAR( p, i, m) REV_BIT(p, i, i += m; m += m, m += m; i += m; ) #define REV_BIT_CONST(p, i, m) REV_BIT(p, i, i += m; , i += m * 2; ) #define REV_BIT_LAST( p, i, m) REV_BIT(p, i, i -= m , ; ) #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMAL_LITER_DEC TREE_GET_BIT(prob, symbol) #define MATCHED_LITER_DEC \ matchByte += matchByte; \ bit = offs; \ offs &= matchByte; \ probLit = prob + (offs + bit + symbol); \ GET_BIT2(probLit, symbol, offs ^= bit; , ;) #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define REV_BIT_CHECK(p, i, m) IF_BIT_0_CHECK(p + i) \ { UPDATE_0_CHECK; i += m; m += m; } else \ { UPDATE_1_CHECK; m += m; i += m; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenLow 0 #define LenHigh (LenLow + 2 * (kNumPosStatesMax << kLenNumLowBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define LenChoice LenLow #define LenChoice2 (LenLow + (1 << kLenNumLowBits)) #define kNumStates 12 #define kNumStates2 16 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols * 2 + kLenNumHighSymbols) /* External ASM code needs same CLzmaProb array layout. So don't change it. */ /* (probs_1664) is faster and better for code size at some platforms */ /* #ifdef MY_CPU_X86_OR_AMD64 */ #define kStartOffset 1664 #define GET_PROBS p->probs_1664 /* #define GET_PROBS p->probs + kStartOffset #else #define kStartOffset 0 #define GET_PROBS p->probs #endif */ #define SpecPos (-kStartOffset) #define IsRep0Long (SpecPos + kNumFullDistances) #define RepLenCoder (IsRep0Long + (kNumStates2 << kNumPosBitsMax)) #define LenCoder (RepLenCoder + kNumLenProbs) #define IsMatch (LenCoder + kNumLenProbs) #define Align (IsMatch + (kNumStates2 << kNumPosBitsMax)) #define IsRep (Align + kAlignTableSize) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define PosSlot (IsRepG2 + kNumStates) #define Literal (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define NUM_BASE_PROBS (Literal + kStartOffset) #if Align != 0 && kStartOffset != 0 #error Stop_Compiling_Bad_LZMA_kAlign #endif #if NUM_BASE_PROBS != 1984 #error Stop_Compiling_Bad_LZMA_PROBS #endif #define LZMA_LIT_SIZE 0x300 #define LzmaProps_GetNumProbs(p) (NUM_BASE_PROBS + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #define CALC_POS_STATE(processedPos, pbMask) (((processedPos) & (pbMask)) << 4) #define COMBINED_PS_STATE (posState + state) #define GET_LEN_STATE (posState) #define LZMA_DIC_MIN (1 << 12) /* p->remainLen : shows status of LZMA decoder: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished = kMatchSpecLenStart + 1 : need init range coder = kMatchSpecLenStart + 2 : need init range coder and state */ /* ---------- LZMA_DECODE_REAL ---------- */ /* LzmaDec_DecodeReal_3() can be implemented in external ASM file. 3 - is the code compatibility version of that function for check at link time. */ #define LZMA_DECODE_REAL LzmaDec_DecodeReal_3 /* LZMA_DECODE_REAL() In: RangeCoder is normalized if (p->dicPos == limit) { LzmaDec_TryDummy() was called before to exclude LITERAL and MATCH-REP cases. So first symbol can be only MATCH-NON-REP. And if that MATCH-NON-REP symbol is not END_OF_PAYALOAD_MARKER, then function returns error code. } Processing: first LZMA symbol will be decoded in any case All checks for limits are at the end of main loop, It will decode new LZMA-symbols while (p->buf < bufLimit && dicPos < limit), RangeCoder is still without last normalization when (p->buf < bufLimit) is being checked. Out: RangeCoder is normalized Result: SZ_OK - OK SZ_ERROR_DATA - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished */ #ifdef _LZMA_DEC_OPT int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit); #else static int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { CLzmaProb *probs = GET_PROBS; unsigned state = (unsigned)p->state; UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; unsigned lc = p->prop.lc; unsigned lpMask = ((unsigned)0x100 << p->prop.lp) - ((unsigned)0x100 >> lc); Byte *dic = p->dic; SizeT dicBufSize = p->dicBufSize; SizeT dicPos = p->dicPos; UInt32 processedPos = p->processedPos; UInt32 checkDicSize = p->checkDicSize; unsigned len = 0; const Byte *buf = p->buf; UInt32 range = p->range; UInt32 code = p->code; do { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = CALC_POS_STATE(processedPos, pbMask); prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (processedPos != 0 || checkDicSize != 0) prob += (UInt32)3 * ((((processedPos << 8) + dic[(dicPos == 0 ? dicBufSize : dicPos) - 1]) & lpMask) << lc); processedPos++; if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; #ifdef _LZMA_SIZE_OPT do { NORMAL_LITER_DEC } while (symbol < 0x100); #else NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC #endif } else { unsigned matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; #ifdef _LZMA_SIZE_OPT do { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC } while (symbol < 0x100); #else { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC } #endif } dic[dicPos++] = (Byte)symbol; continue; } { UPDATE_1(prob); prob = probs + IsRep + state; IF_BIT_0(prob) { UPDATE_0(prob); state += kNumStates; prob = probs + LenCoder; } else { UPDATE_1(prob); /* // that case was checked before with kBadRepCode if (checkDicSize == 0 && processedPos == 0) return SZ_ERROR_DATA; */ prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); prob = probs + IsRep0Long + COMBINED_PS_STATE; IF_BIT_0(prob) { UPDATE_0(prob); dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; continue; } UPDATE_1(prob); } else { UInt32 distance; UPDATE_1(prob); prob = probs + IsRepG1 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep1; } else { UPDATE_1(prob); prob = probs + IsRepG2 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep2; } else { UPDATE_1(prob); distance = rep3; rep3 = rep2; } rep2 = rep1; } rep1 = rep0; rep0 = distance; } state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } #ifdef _LZMA_SIZE_OPT { unsigned lim, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + GET_LEN_STATE; offset = 0; lim = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); offset = kLenNumLowSymbols; lim = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols * 2; lim = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, lim, len); len += offset; } #else { CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + GET_LEN_STATE; len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); len -= 8; } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); } else { UPDATE_1(probLen); probLen = prob + LenHigh; TREE_DECODE(probLen, (1 << kLenNumHighBits), len); len += kLenNumLowSymbols * 2; } } } #endif if (state >= kNumStates) { UInt32 distance; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_6_DECODE(prob, distance); if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; unsigned numDirectBits = (unsigned)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; prob = probs + SpecPos; { UInt32 m = 1; distance++; do { REV_BIT_VAR(prob, distance, m); } while (--numDirectBits); distance -= m; } } else { numDirectBits -= kNumAlignBits; do { NORMALIZE range >>= 1; { UInt32 t; code -= range; t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ distance = (distance << 1) + (t + 1); code += range & t; } /* distance <<= 1; if (code >= range) { code -= range; distance |= 1; } */ } while (--numDirectBits); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; REV_BIT_CONST(prob, i, 1); REV_BIT_CONST(prob, i, 2); REV_BIT_CONST(prob, i, 4); REV_BIT_LAST (prob, i, 8); distance |= i; } if (distance == (UInt32)0xFFFFFFFF) { len = kMatchSpecLenStart; state -= kNumStates; break; } } } rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance + 1; state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; if (distance >= (checkDicSize == 0 ? processedPos: checkDicSize)) { p->dicPos = dicPos; return SZ_ERROR_DATA; } } len += kMatchMinLen; { SizeT rem; unsigned curLen; SizeT pos; if ((rem = limit - dicPos) == 0) { p->dicPos = dicPos; return SZ_ERROR_DATA; } curLen = ((rem < len) ? (unsigned)rem : len); pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); processedPos += (UInt32)curLen; len -= curLen; if (curLen <= dicBufSize - pos) { Byte *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; const Byte *lim = dest + curLen; dicPos += (SizeT)curLen; do *(dest) = (Byte)*(dest + src); while (++dest != lim); } else { do { dic[dicPos++] = dic[pos]; if (++pos == dicBufSize) pos = 0; } while (--curLen != 0); } } } } while (dicPos < limit && buf < bufLimit); NORMALIZE; p->buf = buf; p->range = range; p->code = code; p->remainLen = (UInt32)len; p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; p->reps[1] = rep1; p->reps[2] = rep2; p->reps[3] = rep3; p->state = (UInt32)state; return SZ_OK; } #endif static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) { Byte *dic = p->dic; SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; unsigned len = (unsigned)p->remainLen; SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ SizeT rem = limit - dicPos; if (rem < len) len = (unsigned)(rem); if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; p->processedPos += (UInt32)len; p->remainLen -= (UInt32)len; while (len != 0) { len--; dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; } } #define kRange0 0xFFFFFFFF #define kBound0 ((kRange0 >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1)) #define kBadRepCode (kBound0 + (((kRange0 - kBound0) >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1))) #if kBadRepCode != (0xC0000000 - 0x400) #error Stop_Compiling_Bad_LZMA_Check #endif static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { do { SizeT limit2 = limit; if (p->checkDicSize == 0) { UInt32 rem = p->prop.dicSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; if (p->processedPos == 0) if (p->code >= kBadRepCode) return SZ_ERROR_DATA; } RINOK(LZMA_DECODE_REAL(p, limit2, bufLimit)); if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); return 0; } typedef enum { DUMMY_ERROR, /* unexpected end of input stream */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) { UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; const CLzmaProb *probs = GET_PROBS; unsigned state = (unsigned)p->state; ELzmaDummy res; { const CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = CALC_POS_STATE(p->processedPos, (1 << p->prop.pb) - 1); prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += ((UInt32)LZMA_LIT_SIZE * ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { unsigned symbol = 1; do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + (p->dicPos < p->reps[0] ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; const CLzmaProb *probLit; matchByte += matchByte; bit = offs; offs &= matchByte; probLit = prob + (offs + bit + symbol); GET_BIT2_CHECK(probLit, symbol, offs ^= bit; , ; ) } while (symbol < 0x100); } res = DUMMY_LIT; } else { unsigned len; UPDATE_1_CHECK; prob = probs + IsRep + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; state = 0; prob = probs + LenCoder; res = DUMMY_MATCH; } else { UPDATE_1_CHECK; res = DUMMY_REP; prob = probs + IsRepG0 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; prob = probs + IsRep0Long + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; NORMALIZE_CHECK; return DUMMY_REP; } else { UPDATE_1_CHECK; } } else { UPDATE_1_CHECK; prob = probs + IsRepG1 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; prob = probs + IsRepG2 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; } } } state = kNumStates; prob = probs + RepLenCoder; } { unsigned limit, offset; const CLzmaProb *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + GET_LEN_STATE; offset = 0; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenChoice2; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); offset = kLenNumLowSymbols; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; offset = kLenNumLowSymbols * 2; limit = 1 << kLenNumHighBits; } } TREE_DECODE_CHECK(probLen, limit, len); len += offset; } if (state < 4) { unsigned posSlot; prob = probs + PosSlot + ((len < kNumLenToPosStates - 1 ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { unsigned numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits); } else { numDirectBits -= kNumAlignBits; do { NORMALIZE_CHECK range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } while (--numDirectBits); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; unsigned m = 1; do { REV_BIT_CHECK(prob, i, m); } while (--numDirectBits); } } } } } NORMALIZE_CHECK; return res; } void LzmaDec_InitDicAndState(CLzmaDec *p, BoolInt initDic, BoolInt initState) { p->remainLen = kMatchSpecLenStart + 1; p->tempBufSize = 0; if (initDic) { p->processedPos = 0; p->checkDicSize = 0; p->remainLen = kMatchSpecLenStart + 2; } if (initState) p->remainLen = kMatchSpecLenStart + 2; } void LzmaDec_Init(CLzmaDec *p) { p->dicPos = 0; LzmaDec_InitDicAndState(p, True, True); } SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; *status = LZMA_STATUS_NOT_SPECIFIED; if (p->remainLen > kMatchSpecLenStart) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize != 0 && p->tempBuf[0] != 0) return SZ_ERROR_DATA; if (p->tempBufSize < RC_INIT_SIZE) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } p->code = ((UInt32)p->tempBuf[1] << 24) | ((UInt32)p->tempBuf[2] << 16) | ((UInt32)p->tempBuf[3] << 8) | ((UInt32)p->tempBuf[4]); p->range = 0xFFFFFFFF; p->tempBufSize = 0; if (p->remainLen > kMatchSpecLenStart + 1) { SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); SizeT i; CLzmaProb *probs = p->probs; for (i = 0; i < numProbs; i++) probs[i] = kBitModelTotal >> 1; p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; p->state = 0; } p->remainLen = 0; } LzmaDec_WriteRem(p, dicLimit); while (p->remainLen != kMatchSpecLenStart) { int checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) { *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; return SZ_OK; } if (finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->remainLen != 0) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } checkEndMarkNow = 1; } if (p->tempBufSize == 0) { SizeT processed; const Byte *bufLimit; if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, src, inSize); if (dummyRes == DUMMY_ERROR) { MyMemcpy(p->tempBuf, src, inSize); p->tempBufSize = (unsigned)inSize; (*srcLen) += inSize; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } bufLimit = src; } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; p->buf = src; if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) return SZ_ERROR_DATA; processed = (SizeT)(p->buf - src); (*srcLen) += processed; src += processed; inSize -= processed; } else { unsigned rem = p->tempBufSize, lookAhead = 0; while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) p->tempBuf[rem++] = src[lookAhead++]; p->tempBufSize = rem; if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, (SizeT)rem); if (dummyRes == DUMMY_ERROR) { (*srcLen) += (SizeT)lookAhead; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } } p->buf = p->tempBuf; if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) return SZ_ERROR_DATA; { unsigned kkk = (unsigned)(p->buf - p->tempBuf); if (rem < kkk) return SZ_ERROR_FAIL; /* some internal error */ rem -= kkk; if (lookAhead < rem) return SZ_ERROR_FAIL; /* some internal error */ lookAhead -= rem; } (*srcLen) += (SizeT)lookAhead; src += lookAhead; inSize -= (SizeT)lookAhead; p->tempBufSize = 0; } } if (p->code != 0) return SZ_ERROR_DATA; *status = LZMA_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen; SizeT inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT inSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->dicPos == p->dicBufSize) p->dicPos = 0; dicPos = p->dicPos; if (outSize > p->dicBufSize - dicPos) { outSizeCur = p->dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); src += inSizeCur; inSize -= inSizeCur; *srcLen += inSizeCur; outSizeCur = p->dicPos - dicPos; MyMemcpy(dest, p->dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->probs); p->probs = NULL; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAllocPtr alloc) { ISzAlloc_Free(alloc, p->dic); p->dic = NULL; } void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc) { LzmaDec_FreeProbs(p, alloc); LzmaDec_FreeDict(p, alloc); } SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) { UInt32 dicSize; Byte d; if (size < LZMA_PROPS_SIZE) return SZ_ERROR_UNSUPPORTED; else dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); if (dicSize < LZMA_DIC_MIN) dicSize = LZMA_DIC_MIN; p->dicSize = dicSize; d = data[0]; if (d >= (9 * 5 * 5)) return SZ_ERROR_UNSUPPORTED; p->lc = (Byte)(d % 9); d /= 9; p->pb = (Byte)(d / 5); p->lp = (Byte)(d % 5); return SZ_OK; } static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAllocPtr alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (!p->probs || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)ISzAlloc_Alloc(alloc, numProbs * sizeof(CLzmaProb)); if (!p->probs) return SZ_ERROR_MEM; p->probs_1664 = p->probs + 1664; p->numProbs = numProbs; } return SZ_OK; } SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc) { CLzmaProps propNew = { 0 }; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); p->prop = propNew; return SZ_OK; } SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc) { CLzmaProps propNew = { 0 }; SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); { UInt32 dictSize = propNew.dicSize; SizeT mask = ((UInt32)1 << 12) - 1; if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;; dicBufSize = ((SizeT)dictSize + mask) & ~mask; if (dicBufSize < dictSize) dicBufSize = dictSize; } if (!p->dic || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)ISzAlloc_Alloc(alloc, dicBufSize); if (!p->dic) { LzmaDec_FreeProbs(p, alloc); return SZ_ERROR_MEM; } } p->dicBufSize = dicBufSize; p->prop = propNew; return SZ_OK; } SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAllocPtr alloc) { CLzmaDec p; SRes res; SizeT outSize = *destLen, inSize = *srcLen; *destLen = *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; if (inSize < RC_INIT_SIZE) return SZ_ERROR_INPUT_EOF; LzmaDec_Construct(&p); RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc)); p.dic = dest; p.dicBufSize = outSize; LzmaDec_Init(&p); *srcLen = inSize; res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); *destLen = p.dicPos; if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; LzmaDec_FreeProbs(&p, alloc); return res; } 07070100000020000081A400000000000000000000000166853FAC00001B56000000000000000000000000000000000000003200000000thermal_daemon-2.5.7.17.git+fc7464a/src/LzmaDec.h// From Intel DPTF github project ESIF/Products/ESIF_CMP/Sources // Fix white spaces from the source. /* LzmaDec.h -- LZMA Decoder 2018-04-21 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H #include "7zTypes.h" EXTERN_C_BEGIN /* #define _LZMA_PROB32 */ /* _LZMA_PROB32 can increase the speed on some CPUs, but memory usage for CLzmaDec::probs will be doubled in that case */ typedef #ifdef _LZMA_PROB32 UInt32 #else UInt16 #endif CLzmaProb; /* ---------- LZMA Properties ---------- */ #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaProps { Byte lc; Byte lp; Byte pb; Byte _pad_; UInt32 dicSize; } CLzmaProps; /* LzmaProps_Decode - decodes properties Returns: SZ_OK SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); /* ---------- LZMA Decoder state ---------- */ /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ #define LZMA_REQUIRED_INPUT_MAX 20 typedef struct { /* Don't change this structure. ASM code can use it. */ CLzmaProps prop; CLzmaProb *probs; CLzmaProb *probs_1664; Byte *dic; SizeT dicBufSize; SizeT dicPos; const Byte *buf; UInt32 range; UInt32 code; UInt32 processedPos; UInt32 checkDicSize; UInt32 reps[4]; UInt32 state; UInt32 remainLen; UInt32 numProbs; unsigned tempBufSize; Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; #define LzmaDec_Construct(p) { (p)->dic = NULL; (p)->probs = NULL; } void LzmaDec_Init(CLzmaDec *p); /* There are two types of LZMA streams: - Stream with end mark. That end mark adds about 6 bytes to compressed size. - Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { LZMA_FINISH_ANY, /* finish at any point */ LZMA_FINISH_END /* block must be finished at the end */ } ELzmaFinishMode; /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! You must use LZMA_FINISH_END, when you know that current output buffer covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, and output value of destLen will be less than output buffer size limit. You can check status result also. You can use multiple checks to test data integrity after full decompression: 1) Check Result and "status" variable. 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. You must use correct finish mode in that case. */ typedef enum { LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ } ELzmaStatus; /* ELzmaStatus is used only as output value for function call */ /* ---------- Interfaces ---------- */ /* There are 3 levels of interfaces: 1) Dictionary Interface 2) Buffer Interface 3) One Call Interface You can select any of these interfaces, but don't mix functions from different groups for same object. */ /* There are two variants to allocate state for Dictionary Interface: 1) LzmaDec_Allocate / LzmaDec_Free 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs You can use variant 2, if you set dictionary buffer manually. For Buffer Interface you must always use variant 1. LzmaDec_Allocate* can return: SZ_OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc); void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc); SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc); void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc); /* ---------- Dictionary Interface ---------- */ /* You can use it, if you want to eliminate the overhead for data copying from dictionary to some other external buffer. You must work with CLzmaDec variables directly in this interface. STEPS: LzmaDec_Construct() LzmaDec_Allocate() for (each new stream) { LzmaDec_Init() while (it needs more decompression) { LzmaDec_DecodeToDic() use data from CLzmaDec::dic and update CLzmaDec::dicPos } } LzmaDec_Free() */ /* LzmaDec_DecodeToDic The decoding to internal dictionary buffer (CLzmaDec::dic). You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! finishMode: It has meaning only if the decoding reaches output limit (dicLimit). LZMA_FINISH_ANY - Decode just dicLimit bytes. LZMA_FINISH_END - Stream must be finished after dicLimit. Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error */ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- Buffer Interface ---------- */ /* It's zlib-like interface. See LzmaDec_DecodeToDic description for information about STEPS and return results, but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need to work with CLzmaDec variables manually. finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). */ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* LzmaDecode finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAllocPtr alloc); EXTERN_C_END #endif 07070100000021000081A400000000000000000000000166853FAC00000C44000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/acpi_thermal_rel_ioct.h/* * acpi_thermal_rel_ioct.h: head file of _ART and _TRT objects * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef __ACPI_ACPI_THERMAL_H #define __ACPI_ACPI_THERMAL_H #include <asm/ioctl.h> #define ACPI_THERMAL_MAGIC 's' #define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long) #define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long) #define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long) #define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long) #define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long) #define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long) /* * ACPI_THERMAL_GET_PSVT_COUNT = Number of PSVT entries * ACPI_THERMAL_GET_PSVT_LEN = Total return data size (PSVT count x each * PSVT entry size) * ACPI_THERMAL_GET_PSVT = Get the data as an array of psvt_objects */ #define ACPI_THERMAL_GET_PSVT_LEN _IOR(ACPI_THERMAL_MAGIC, 7, unsigned long) #define ACPI_THERMAL_GET_PSVT_COUNT _IOR(ACPI_THERMAL_MAGIC, 8, unsigned long) #define ACPI_THERMAL_GET_PSVT _IOR(ACPI_THERMAL_MAGIC, 9, unsigned long) #ifndef __KERNEL__ #define u64 unsigned long long #endif union art_object { struct { char source_device[8]; /* ACPI single name */ char target_device[8]; /* ACPI single name */ u64 weight; u64 ac0_max_level; u64 ac1_max_level; u64 ac2_max_level; u64 ac3_max_level; u64 ac4_max_level; u64 ac5_max_level; u64 ac6_max_level; u64 ac7_max_level; u64 ac8_max_level; u64 ac9_max_level; }acpi_art_entry; u64 __data[13]; }; union trt_object { struct { char source_device[8]; /* ACPI single name */ char target_device[8]; /* ACPI single name */ u64 influence; u64 sample_period; u64 reserved[4]; } acpi_trt_entry; u64 __data[8]; }; #define ACPI_LIMIT_STR_MAX_LEN 8 #define ACPI_NR_PSVT_ELEMENTS 12 union psvt_object { struct { char source_device[8]; char target_device[8]; u64 priority; u64 sample_period; u64 passive_temp; u64 source_domain; u64 control_knob; union { u64 integer; char string[ACPI_LIMIT_STR_MAX_LEN]; } limit; u64 step_size; u64 limit_coeff; u64 unlimit_coeff; u64 control_knob_type; } acpi_psvt_entry; u64 __data[ACPI_NR_PSVT_ELEMENTS]; }; #ifdef __KERNEL__ int acpi_thermal_rel_misc_device_add(acpi_handle handle); int acpi_thermal_rel_misc_device_remove(acpi_handle handle); #endif #endif /* __ACPI_ACPI_THERMAL_H */ 07070100000022000081A400000000000000000000000166853FAC00001ECB000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/src/android_main.cpp/* * android_main.cpp: Thermal Daemon entry point tuned for Android * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * * This is the main entry point for thermal daemon. This has main function * which parses command line arguments, setup logs and starts thermal * engine. */ #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_adaptive.h" #include "thd_engine_default.h" #include "thd_parse.h" #include <sys/file.h> #define EXIT_UNSUPPORTED 2 // getdtablesize() is removed from bionic/libc in LPDK*/ // use POSIX alternative available. Otherwise fail # ifdef _POSIX_OPEN_MAX # define getdtablesize() (_POSIX_OPEN_MAX) # endif // for AID_* constatns #include <cutils/android_filesystem_config.h> // getdtablesize() is removed from bionic/libc in LPDK*/ // use POSIX alternative available. Otherwise fail # ifdef _POSIX_OPEN_MAX # define getdtablesize() (_POSIX_OPEN_MAX) # endif // poll mode int thd_poll_interval = 4; //in seconds bool thd_ignore_default_control = false; bool workaround_enabled = false; bool disable_active_power = false; bool ignore_critical = false; static int pid_file_handle; // Stop daemon static void daemonShutdown() { if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; } // signal handler static void signal_handler(int sig) { switch (sig) { case SIGHUP: thd_log_warn("Received SIGHUP signal. \n"); break; case SIGINT: case SIGTERM: thd_log_info("Daemon exiting \n"); daemonShutdown(); exit(EXIT_SUCCESS); break; default: thd_log_warn("Unhandled signal %s\n", strsignal(sig)); break; } } static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; struct sigaction sig_actions; sigset_t sig_set; if (getppid() == 1) { return; } sigemptyset(&sig_set); sigaddset(&sig_set, SIGCHLD); sigaddset(&sig_set, SIGTSTP); sigaddset(&sig_set, SIGTTOU); sigaddset(&sig_set, SIGTTIN); sigprocmask(SIG_BLOCK, &sig_set, NULL); sig_actions.sa_handler = signal_handler; sigemptyset(&sig_actions.sa_mask); sig_actions.sa_flags = 0; sigaction(SIGHUP, &sig_actions, NULL); sigaction(SIGTERM, &sig_actions, NULL); sigaction(SIGINT, &sig_actions, NULL); pid = fork(); if (pid < 0) { /* Could not fork */ exit(EXIT_FAILURE); } if (pid > 0) { thd_log_info("Child process created: %d\n", pid); exit(EXIT_SUCCESS); } umask(027); sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } /* close all descriptors */ for (i = getdtablesize(); i >= 0; --i) { close(i); } i = open("/dev/null", O_RDWR); dup(i); dup(i); chdir(rundir); pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ thd_log_info("Could not open PID lock file %s, exiting\n", pidfile); exit(EXIT_FAILURE); } /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { #else if (flock(pid_file_handle,LOCK_EX|LOCK_NB) < 0) { #endif /* Couldn't get lock on lock file */ thd_log_info("Couldn't get lock file %d\n", getpid()); exit(EXIT_FAILURE); } thd_log_info("Thermal PID %d\n", getpid()); snprintf(str, sizeof(str), "%d\n", getpid()); write(pid_file_handle, str, strlen(str)); } static void print_usage(FILE* stream, int exit_code) { fprintf(stream, "Usage: thermal-daemon options [ ... ]\n"); fprintf(stream, " --help Display this usage information.\n" " --version Show version.\n" " --no-daemon No daemon.\n" " --poll-interval Poll interval 0 to disable.\n" " --exclusive_control To act as exclusive thermal controller.\n" " --config-file Configuration file to use other than the default config. \n"); exit(exit_code); } int main(int argc, char *argv[]) { int c; int option_index = 0; bool no_daemon = false; bool exclusive_control = false; bool test_mode = false; bool is_privileged_user = false; char *conf_file = NULL; bool ignore_cpuid_check = false; bool adaptive = false; int ret; const char* const short_options = "hvnp:detc:ia"; static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "no-daemon", no_argument, 0, 'n' }, { "poll-interval", required_argument, 0, 'p' }, { "exclusive_control", no_argument, 0, 'e' }, { "test-mode", no_argument, 0, 't' }, { "config-file", required_argument, 0, 'c' }, { "ignore-cpuid-check", no_argument, 0, 'i'}, { "ignore-default-control", no_argument, 0, 'd'}, { "adaptive", no_argument, 0, 'a'}, { NULL, 0, NULL, 0 } }; if (argc > 1) { while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { switch (c) { case 'h': print_usage(stdout, 0); break; case 'v': fprintf(stdout, "1.1\n"); exit(EXIT_SUCCESS); break; case 'n': no_daemon = true; break; case 'p': thd_poll_interval = atoi(optarg); break; case 'e': exclusive_control = true; break; case 't': test_mode = true; break; case 'c': conf_file = optarg; break; case 'i': ignore_cpuid_check = true; break; case 'd': thd_ignore_default_control = true; break; case 'a': adaptive = true; break; case -1: case 0: break; default: break; } } } is_privileged_user = (getuid() == 0) || (getuid() == AID_SYSTEM); if (!is_privileged_user && !test_mode) { thd_log_error("You do not have correct permissions to run thermal daemon!\n"); exit(1); } if (mkdir(TDRUNDIR, 0755) != 0) { if (errno != EEXIST) { fprintf(stderr, "Cannot create '%s': %s\n", TDRUNDIR, strerror(errno)); exit(EXIT_FAILURE); } } mkdir(TDCONFDIR, 0755); // Don't care return value as directory if (!no_daemon) { daemonize((char *) "/data/vendor/thermal-daemon", (char *) "/data/vendor/thermal-daemon/thermald.pid"); } else signal(SIGINT, signal_handler); thd_log_info( "Linux Thermal Daemon is starting mode %d : poll_interval %d :ex_control %d\n", no_daemon, thd_poll_interval, exclusive_control); if (adaptive) { ignore_cpuid_check= true; ret = thd_engine_create_adaptive_engine((bool) ignore_cpuid_check, test_mode); if (ret != THD_SUCCESS) { thd_log_info("--adaptive option failed on this platform\n"); thd_log_info("Ignoring --adaptive option\n"); ret = thd_engine_create_default_engine((bool) ignore_cpuid_check, (bool) exclusive_control, conf_file); } } else { ret = thd_engine_create_default_engine((bool) ignore_cpuid_check, (bool) exclusive_control, conf_file); } if (ret != THD_SUCCESS) { if (ret == THD_ERROR) exit(EXIT_UNSUPPORTED); else exit(EXIT_FAILURE); } #ifdef VALGRIND_TEST // lots of STL lib function don't free memory // when called with exit(). // Here just run for some time and gracefully return. sleep(10); if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; #else for (;;) sleep(0xffff); thd_log_info("Linux Thermal Daemon is exiting \n"); #endif return 0; } 07070100000023000081A400000000000000000000000166853FAC00002CFF000000000000000000000000000000000000003100000000thermal_daemon-2.5.7.17.git+fc7464a/src/main.cpp/* * main.cpp: Thermal Daemon entry point * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * * This is the main entry point for thermal daemon. This has main function * which parses command line arguments, set up dbus server and log related * functions. */ /* This implements main() function. This will parse command line options and * a new instance of cthd_engine object. By default it will create an engine * which uses dts engine, which DTS sensor and use P states to control * temperature, without any configuration. Alternatively if the * thermal-conf.xml has exact UUID match then it can use the zones and * cooling devices defined it to control thermals. This file will allow fine * tune ACPI thermal config or create new thermal config using custom * sensors. * Dbus interface allows user to switch between active/passive thermal controls * if the thermal-conf.xml defines parameters. */ #include <glib.h> #include <glib-unix.h> #include <syslog.h> #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_adaptive.h" #include "thd_engine_default.h" #include "thd_parse.h" #include <syslog.h> #if !defined(TD_DIST_VERSION) #define TD_DIST_VERSION PACKAGE_VERSION #endif #define EXIT_UNSUPPORTED 2 extern int thd_dbus_server_init(gboolean (*exit_handler)(void)); // Lock file static int lock_file_handle = -1; static const char *lock_file = TDRUNDIR "/thermald.pid"; // Default log level static int thd_log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE; // Daemonize or not static gboolean thd_daemonize; static gboolean use_syslog; // Disable dbus static gboolean dbus_enable; // poll mode int thd_poll_interval = 4; //in seconds bool thd_ignore_default_control = false; bool workaround_enabled = false; bool disable_active_power = false; bool ignore_critical = false; // check cpuid static gboolean ignore_cpuid_check = false; gboolean exclusive_control = FALSE; static GMainLoop *g_main_loop; #ifdef GDBUS gint own_id = 0; #endif // g_log handler. All logs will be directed here void thd_logger(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if (!(thd_log_level & log_level)) return; int syslog_priority; const char *prefix; time_t seconds; switch (log_level) { case G_LOG_LEVEL_ERROR: prefix = "[CRIT]"; syslog_priority = LOG_CRIT; break; case G_LOG_LEVEL_CRITICAL: prefix = "[ERR]"; syslog_priority = LOG_ERR; break; case G_LOG_LEVEL_WARNING: prefix = "[WARN]"; syslog_priority = LOG_WARNING; break; case G_LOG_LEVEL_MESSAGE: prefix = "[MSG]"; syslog_priority = LOG_NOTICE; break; case G_LOG_LEVEL_DEBUG: prefix = "[DEBUG]"; syslog_priority = LOG_DEBUG; break; case G_LOG_LEVEL_INFO: default: prefix = "[INFO]"; syslog_priority = LOG_INFO; break; } seconds = time(NULL); if (use_syslog) syslog(syslog_priority, "%s", message); else g_print("[%lld]%s%s", (long long) seconds, prefix, message); } void clean_up_lockfile(void) { if (lock_file_handle != -1) { (void) close(lock_file_handle); (void) unlink(lock_file); } } bool check_thermald_running() { lock_file_handle = open(lock_file, O_RDWR | O_CREAT, 0600); if (lock_file_handle == -1) { /* Couldn't open lock file */ thd_log_error("Could not open PID lock file %s, exiting\n", lock_file); return false; } /* Try to lock file */ if (lockf(lock_file_handle, F_TLOCK, 0) == -1) { /* Couldn't get lock on lock file */ thd_log_error("Couldn't get lock file %d\n", getpid()); close(lock_file_handle); return true; } return false; } // SIGTERM & SIGINT handler gboolean sig_int_handler(void) { if (thd_engine) thd_engine->thd_engine_terminate(); sleep(1); if (g_main_loop) g_main_loop_quit(g_main_loop); delete thd_engine; clean_up_lockfile(); exit(EXIT_SUCCESS); return FALSE; } // main function int main(int argc, char *argv[]) { gboolean show_version = FALSE; gboolean log_info = FALSE; gboolean log_debug = FALSE; gboolean no_daemon = FALSE; gboolean systemd = FALSE; gboolean test_mode = FALSE; gboolean adaptive = FALSE; gboolean ignore_default_control = FALSE; gchar *conf_file = NULL; gint poll_interval = -1; gboolean success; GOptionContext *opt_ctx; int ret; thd_daemonize = TRUE; use_syslog = TRUE; dbus_enable = FALSE; GOptionEntry options[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Print thermald version and exit"), NULL }, { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_( "Don't become a daemon: Default is daemon mode"), NULL }, { "systemd", 0, 0, G_OPTION_ARG_NONE, &systemd, N_( "Assume daemon is started by systemd"), NULL }, { "loglevel=info", 0, 0, G_OPTION_ARG_NONE, &log_info, N_( "log severity: info level and up"), NULL }, { "loglevel=debug", 0, 0, G_OPTION_ARG_NONE, &log_debug, N_( "log severity: debug level and up: Max logging"), NULL }, { "test-mode", 0, 0, G_OPTION_ARG_NONE, &test_mode, N_( "Test Mode only: Allow non root user"), NULL }, { "adaptive", 0, 0, G_OPTION_ARG_NONE, &adaptive, N_( "adaptive mode: use adaptive performance tables if available"), NULL }, { "poll-interval", 0, 0, G_OPTION_ARG_INT, &poll_interval, N_("Poll interval in seconds: Poll for zone temperature changes. " "If want to disable polling set to zero."), NULL }, { "dbus-enable", 0, 0, G_OPTION_ARG_NONE, &dbus_enable, N_( "Enable Dbus."), NULL }, { "exclusive-control", 0, 0, G_OPTION_ARG_NONE, &exclusive_control, N_( "Take over thermal control from kernel thermal driver."), NULL }, { "ignore-cpuid-check", 0, 0, G_OPTION_ARG_NONE, &ignore_cpuid_check, N_("Ignore CPU ID check."), NULL }, { "config-file", 0, 0, G_OPTION_ARG_STRING, &conf_file, N_( "configuration file"), NULL }, { "ignore-default-control", 0, 0, G_OPTION_ARG_NONE, &ignore_default_control, N_( "Ignore default CPU temperature control. " "Strictly follow thermal-conf.xml"), NULL }, { "workaround-enabled", 0, 0, G_OPTION_ARG_NONE, &workaround_enabled, N_( "Enable workarounds for power"), NULL }, { "disable-active-power", 0, 0, G_OPTION_ARG_NONE, &disable_active_power, N_( "Disable active power settings to reduce thermal impact"), NULL }, { "ignore-critical-trip", 0, 0, G_OPTION_ARG_NONE, &ignore_critical, N_( "Ignore critical trips for reboot"), NULL }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } }; if (!g_module_supported()) { fprintf(stderr, "GModules are not supported on your platform!\n"); exit(EXIT_FAILURE); } /* Set locale to be able to use environment variables */ setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, TDLOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); /* Parse options */ opt_ctx = g_option_context_new(NULL); g_option_context_set_translation_domain(opt_ctx, GETTEXT_PACKAGE); g_option_context_set_ignore_unknown_options(opt_ctx, FALSE); g_option_context_set_help_enabled(opt_ctx, TRUE); g_option_context_add_main_entries(opt_ctx, options, NULL); g_option_context_set_summary(opt_ctx, "Thermal daemon monitors temperature sensors and decides the best action\n" "based on the temperature readings and user preferences.\n\n" "Copyright (c) 2022, Intel Corporation\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This work is licensed under GPL v2.\n" "Refer to https://github.com/intel/thermal_daemon/blob/master/COPYING."); success = g_option_context_parse(opt_ctx, &argc, &argv, NULL); g_option_context_free(opt_ctx); if (!success) { fprintf(stderr, "Invalid option. Please use --help to see a list of valid options.\n"); exit(EXIT_FAILURE); } if (show_version) { fprintf(stdout, TD_DIST_VERSION "\n"); exit(EXIT_SUCCESS); } if (getuid() != 0 && !test_mode) { fprintf(stderr, "You must be root to run thermald!\n"); exit(EXIT_FAILURE); } if (g_mkdir_with_parents(TDRUNDIR, 0755) != 0) { fprintf(stderr, "Cannot create '%s': %s", TDRUNDIR, strerror(errno)); exit(EXIT_FAILURE); } g_mkdir_with_parents(TDCONFDIR, 0755); // Don't care return value as directory // may already exist if (log_info) { thd_log_level |= G_LOG_LEVEL_INFO; } if (log_debug) { thd_log_level |= G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG; } if (poll_interval >= 0) { fprintf(stdout, "Polling enabled: %d\n", poll_interval); thd_poll_interval = poll_interval; } thd_ignore_default_control = ignore_default_control; openlog("thermald", LOG_PID, LOG_USER | LOG_DAEMON | LOG_SYSLOG); // Don't care return val //setlogmask(LOG_CRIT | LOG_ERR | LOG_WARNING | LOG_NOTICE | LOG_DEBUG | LOG_INFO); thd_daemonize = !no_daemon && !systemd; use_syslog = !no_daemon || systemd; g_log_set_handler(NULL, G_LOG_LEVEL_MASK, thd_logger, NULL); if (check_thermald_running()) { thd_log_error( "An instance of thermald is already running, exiting ...\n"); exit(EXIT_FAILURE); } if (!thd_daemonize) { g_unix_signal_add (SIGINT, G_SOURCE_FUNC (sig_int_handler), NULL); g_unix_signal_add (SIGTERM, G_SOURCE_FUNC (sig_int_handler), NULL); } // Create a main loop that will dispatch callbacks g_main_loop = g_main_loop_new(NULL, FALSE); if (g_main_loop == NULL) { clean_up_lockfile(); thd_log_error("Couldn't create GMainLoop:\n"); return THD_FATAL_ERROR; } if (dbus_enable) thd_dbus_server_init(sig_int_handler); if (thd_daemonize) { printf("Ready to serve requests: Daemonizing.. %d\n", thd_daemonize); thd_log_info( "thermald ver %s: Ready to serve requests: Daemonizing..\n", TD_DIST_VERSION); if (daemon(0, 0) != 0) { clean_up_lockfile(); thd_log_error("Failed to daemonize.\n"); return THD_FATAL_ERROR; } } if (adaptive) { ret = thd_engine_create_adaptive_engine((bool) ignore_cpuid_check, (bool) test_mode); if (ret != THD_SUCCESS) { thd_log_info("--adaptive option failed on this platform\n"); thd_log_info("Ignoring --adaptive option\n"); ret = thd_engine_create_default_engine((bool) ignore_cpuid_check, (bool) exclusive_control, conf_file); } } else { ret = thd_engine_create_default_engine((bool) ignore_cpuid_check, (bool) exclusive_control, conf_file); } if (ret != THD_SUCCESS) { clean_up_lockfile(); closelog(); if (ret == THD_ERROR) exit(EXIT_UNSUPPORTED); else exit(EXIT_FAILURE); } // Start service requests on the D-Bus thd_log_debug("Start main loop\n"); g_main_loop_run(g_main_loop); thd_log_warn("Oops g main loop exit..\n"); #ifdef GDBUS g_bus_unown_name (own_id); #endif fprintf(stdout, "Exiting ..\n"); clean_up_lockfile(); closelog(); } 07070100000024000081A400000000000000000000000166853FAC00000482000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_adaptive_types.h/* * cthd_engine_adaptive.cpp: Adaptive thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Copyright 2020 Google LLC * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name Matthew Garrett <mjg59@google.com> * */ #ifndef THD_ADAPTIVE_TYPES_H_ #define THD_ADAPTIVE_TYPES_H_ #include <stdint.h> struct adaptive_target { uint64_t target_id; std::string name; std::string participant; uint64_t domain; std::string code; std::string argument; }; #endif /* THD_ADAPTIVE_TYPES_H_ */ 07070100000025000081A400000000000000000000000166853FAC00003DCC000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev.cpp/* * thd_cdev.cpp: thermal cooling class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* * This is parent class for all cooling devices. Most of the functions * are implemented in interface file except set state. * set_state uses the interface to get the current state, max state and * set the device state. * When state = 0, it causes reduction in the cooling device state * when state = 1, it increments cooling device state * When increments, it goes step by step, unless it finds that the temperature * can't be controlled by previous state with in def_poll_interval, otherwise * it will increase exponentially. * Reduction is always uses step by step to reduce ping pong affect. * */ #include "thd_cdev.h" #include "thd_engine.h" // Clamp to cdev min state or trip specific min if valid int cthd_cdev::thd_clamp_state_min(int _state, int temp_min_state) { int _min_state = min_state; if (_min_state > max_state) { if (temp_min_state && temp_min_state < _min_state) _min_state = temp_min_state; } else { if (temp_min_state && temp_min_state > _min_state) _min_state = temp_min_state; } thd_log_debug("def_min_state:%d curr_min_state:%d\n", min_state, _min_state); if ((_min_state < max_state && _state < _min_state) || (_min_state > max_state && _state > _min_state)) return _min_state; else return _state; } // Clamp to cdev max state or trip specific max if valid int cthd_cdev::thd_clamp_state_max(int _state, int temp_max_state) { int _max_state = max_state; if (min_state > _max_state) { if (temp_max_state && temp_max_state > _max_state) _max_state = temp_max_state; } else { if (temp_max_state && temp_max_state < _max_state) _max_state = temp_max_state; } thd_log_debug("def_max_state:%d temp_max_state:%d curr_max_state:%d\n", max_state, temp_max_state, _max_state); if ((min_state < _max_state && _state > _max_state) || (min_state > _max_state && _state < _max_state)) return _max_state; else return _state; } int cthd_cdev::thd_cdev_exponential_controller(int set_point, int target_temp, int temperature, int state, int zone_id, int temp_min_state, int temp_max_state) { int control = state; int _curr_state, _max_state, _state; _max_state = get_max_state(); if (state) { // Get the latest state, which for some devices read the state from the hardware _curr_state = get_curr_state(true); // Clamp the current state to min_state, as we start from min to max for // activation of a cooling device _curr_state = thd_clamp_state_min(_curr_state, temp_min_state); thd_log_debug( "thd_cdev_set_%d:curr state %d max state %d temp_min:%d temp_max:%d\n", index, _curr_state, _max_state, temp_min_state, temp_max_state); if (inc_val) _state = _curr_state + inc_val; else _state = _curr_state + inc_dec_val; if (trend_increase) { // This means this is a repeat call for activation if (curr_pow == 0) base_pow_state = _curr_state; ++curr_pow; if (inc_val) _state = base_pow_state + int_2_pow(curr_pow) * inc_val; else _state = base_pow_state + int_2_pow(curr_pow) * inc_dec_val; // Check for the overflow exhaust the valid range if (((inc_val < 0) && (base_pow_state < _state)) || ((inc_val > 0) && (base_pow_state > _state))) _state = max_state; thd_log_info( "cdev index:%d consecutive call, increment exponentially state %d (min %d max %d) (%d:%d)\n", index, _state, min_state, _max_state, base_pow_state, curr_pow); } else { curr_pow = 0; } // Make sure that the state is not beyond max_state _state = thd_clamp_state_max(_state, temp_max_state); trend_increase = true; thd_log_debug("op->device:%s %d\n", type_str.c_str(), _state); set_curr_state(_state, control); } else { // Get the latest state, which is not the latest from the hardware but last set state to the device _curr_state = get_curr_state(); _curr_state = thd_clamp_state_max(_curr_state); thd_log_debug("thd_cdev_set_%d:curr state %d max state %d\n", index, _curr_state, _max_state); curr_pow = 0; trend_increase = false; if (auto_down_adjust == false) { if (dec_val) _state = _curr_state - dec_val; else _state = _curr_state - inc_dec_val; // Make sure that it is not beyond min_state _state = thd_clamp_state_min(_state); thd_log_info("op->device:%s %d\n", type_str.c_str(), _state); set_curr_state(_state, control); } else { thd_log_debug("op->device: force min %s %d\n", type_str.c_str(), min_state); set_curr_state(min_state, control); } } thd_log_info( "Set : threshold:%d, temperature:%d, cdev:%d(%s), curr_state:%d, max_state:%d\n", set_point, temperature, index, type_str.c_str(), get_curr_state(), max_state); thd_log_debug("<<thd_cdev_set_state %d\n", state); return THD_SUCCESS; } static bool sort_clamp_values_asc(zone_trip_limits_t limit_1, zone_trip_limits_t limit_2) { return (limit_1.target_value < limit_2.target_value); } static bool sort_clamp_values_dec(zone_trip_limits_t limit_1, zone_trip_limits_t limit_2) { return (limit_1.target_value > limit_2.target_value); } static bool sort_min_max_values_asc(zone_trip_limits_t limit_1, zone_trip_limits_t limit_2) { return (limit_1._min_state < limit_2._min_state); } static bool sort_min_max_values_dec(zone_trip_limits_t limit_1, zone_trip_limits_t limit_2) { return (limit_1._max_state > limit_2._max_state); } /* * How the state is set? * If the state set is called before debounce interval, then simply return * success. * It is possible that same cdev is used by two trips in the same zone or * different zone. So if one trip activated a cdev (state = 1), even if the * other trip calls for deactivation (state = 0), the trip shouldn't be * deactivated. This is implemented by adding each activation to push the * zone, trip and target_value to a list if not present and remove on * call for deactivation. After deleting from the list if the list still * has members, that means that this device is still under activation from * some other trip. So in this case simply return without changing the state * of cdev. * * Special handling when a target value is passed: * * In addition a zone trip can call for a particular target value for this * cdev (When it doesn't want the exponential or pid control to use, this * is true when multiple trips wants to use the cdev with different state * values. They way we support this: * When a valid target value is set then we push to the list, sorted using * increasing target values. The passed target value is set, no check is * done here to check the state higher/lower than the current state. This * is done during trip_point_check class. * When off is called for device, then we check if the zone in our list, * if yes, we remove this zone and set the next higher value state from * the list. If this is the current zone is the last then we remove * the zone and set the state to minimum state. */ int cthd_cdev::thd_cdev_set_state(int set_point, int target_temp, int temperature, int hard_target, int state, int zone_id, int trip_id, int target_state_valid, int target_value, pid_param_t *pid_param, cthd_pid &pid, bool force, int min_max_valid, int _min_state, int _max_state) { time_t tm; int ret; if (!state && in_min_state() && zone_trip_limits.size() == 0) { // This means that the there is no device in activated state // There are no entries in the list, cdev is min state and // there is a call for deactivation. return THD_SUCCESS; } time(&tm); thd_log_debug( ">>thd_cdev_set_state temperature %d:%d index:%d state:%d :zone:%d trip_id:%d target_state_valid:%d target_value :%d force:%d min_state:%d max_state:%d\n", target_temp, temperature, index, state, zone_id, trip_id, target_state_valid, target_value, force, _min_state, _max_state); if (state) { bool found = false; bool first_entry = false; if (zone_trip_limits.size() == 0) first_entry = true; // Search for the zone and trip id in the list for (unsigned int i = 0; i < zone_trip_limits.size(); ++i) { if (zone_trip_limits[i].zone == zone_id && zone_trip_limits[i].trip == trip_id) { found = true; break; } } // If not found in the list add to the list if (!found) { zone_trip_limits_t limit; limit.zone = zone_id; limit.trip = trip_id; limit.target_state_valid = target_state_valid; limit.target_value = target_value; // Cap the trip min max limits to actual cooling device trips // Here min_state and max_state are for cooling device and // _min_state and _max_state are defined for per trip // in thermal table if (min_max_valid) { if (_min_state == TRIP_PT_INVALID_TARGET_STATE || !_min_state) _min_state = min_state; if (_max_state == TRIP_PT_INVALID_TARGET_STATE || !_max_state) _max_state = max_state; } else { _max_state = 0; _min_state = 0; } limit._max_state = _max_state; limit._min_state = _min_state; limit._min_max_valid = min_max_valid; thd_log_info("Added zone %d trip %d clamp_valid %d clamp %d _min:%d _max:%d\n", limit.zone, limit.trip, limit.target_state_valid, limit.target_value, limit._min_state, limit._max_state); zone_trip_limits.push_back(limit); if (target_state_valid) { if (min_state < max_state) { std::sort(zone_trip_limits.begin(), zone_trip_limits.end(), sort_clamp_values_asc); } else { std::sort(zone_trip_limits.begin(), zone_trip_limits.end(), sort_clamp_values_dec); } } // Target state and min_max_valid or mutually exclusive // in thermal tables. So no need to consolidate min/max with // target // The table will be ordered so that most restrictive is the // first entry if (min_max_valid) { if (min_state > max_state) { std::sort(zone_trip_limits.begin(), zone_trip_limits.end(), sort_min_max_values_dec); } else { std::sort(zone_trip_limits.begin(), zone_trip_limits.end(), sort_min_max_values_asc); } } } zone_trip_limits_t limit; limit = zone_trip_limits[zone_trip_limits.size() - 1]; target_state_valid = limit.target_state_valid; target_value = limit.target_value; _max_state = limit._max_state; _min_state = limit._min_state; if (!first_entry && target_state_valid && cmp_current_state( map_target_state(target_state_valid, target_value)) <= 0) { thd_log_debug("Already more constraint\n"); return THD_SUCCESS; } if (!force && last_state == state && state && (tm - last_action_time) <= debounce_interval) { thd_log_debug( "Ignore: delay < debounce interval : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); return THD_SUCCESS; } } else { thd_log_debug("zone_trip_limits.size() %zu\n", (size_t)zone_trip_limits.size()); if (zone_trip_limits.size() > 0) { int length = zone_trip_limits.size(); int _target_state_valid = 0; int i; int erased = 0; zone_trip_limits_t limit; if (length) { limit = zone_trip_limits[zone_trip_limits.size() - 1]; if (limit.zone == zone_id && limit.trip == trip_id) { i = zone_trip_limits.size() - 1; _target_state_valid = limit.target_state_valid; zone_trip_limits.erase(zone_trip_limits.begin() + i); thd_log_info("Erased [%d: %d %d\n", zone_id, trip_id, target_value); erased = 1; } } if (zone_trip_limits.size()) { limit = zone_trip_limits[zone_trip_limits.size() - 1]; target_value = limit.target_value; target_state_valid = limit.target_state_valid; zone_id = limit.zone; trip_id = limit.trip; // If the above loop caused erase of last control // then the next one in the line will be activated. // If not erased, this means that the previous // lower control is still active. if (!erased) { thd_log_debug( "Currently active limit by [%d: %d %d %d]: ignore\n", zone_id, trip_id, target_state_valid, target_value); return THD_SUCCESS; } } else if (_target_state_valid){ // If the deleted entry has a target then on deactivation // set the state to min_state target_value = get_min_state(); } else if (force) { thd_log_info("forced to min_state \n"); target_state_valid = 1; target_value = get_min_state(); } } else { if (force) { thd_log_info("forced to min_state \n"); target_state_valid = 1; target_value = get_min_state(); } else { target_state_valid = 0; } } } last_action_time = tm; last_state = state; curr_state = get_curr_state(); if (curr_state == get_min_state()) { control_begin(); curr_pow = 0; trend_increase = false; } if (target_state_valid) { set_curr_state_raw(target_value, state); curr_state = target_value; ret = THD_SUCCESS; thd_log_info("Set : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); } else if (hard_target) { ret = get_max_state(); set_curr_state_raw(ret, state); thd_log_info("Set max : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); ret = THD_SUCCESS; } else if (pid_param && pid_param->valid) { // Handle PID param unique to a trip pid.set_target_temp(target_temp); ret = pid.pid_output(temperature, get_curr_state(true) - get_min_state()); ret += get_min_state(); if (get_min_state() < get_max_state()) { if (ret > get_max_state()) ret = get_max_state(); if (ret < get_min_state()) ret = get_min_state(); } else { if (ret < get_max_state()) ret = get_max_state(); if (ret > get_min_state()) ret = get_min_state(); } set_curr_state_raw(ret, state); thd_log_info("Set pid : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); ret = THD_SUCCESS; if (state == 0) pid.reset(); } else if (pid_enable) { // Handle PID param common to whole cooling device pid_ctrl.set_target_temp(target_temp); ret = pid_ctrl.pid_output(temperature); ret += get_min_state(); if (get_min_state() < get_max_state()) { if (ret > get_max_state()) ret = get_max_state(); if (ret < get_min_state()) ret = get_min_state(); } else { if (ret < get_max_state()) ret = get_max_state(); if (ret > get_min_state()) ret = get_min_state(); } set_curr_state_raw(ret, state); thd_log_info("Set : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); ret = THD_SUCCESS; } else { ret = thd_cdev_exponential_controller(set_point, target_temp, temperature, state, zone_id, _min_state, _max_state); } if (curr_state == get_max_state()) { control_end(); } return ret; } int cthd_cdev::thd_cdev_set_min_state(int zone_id, int trip_id) { trend_increase = false; cthd_pid unused; thd_cdev_set_state(0, 0, 0, 0, 0, zone_id, trip_id, 1, min_state, NULL, unused, true, 0, 0, 0); return THD_SUCCESS; } 07070100000026000081A400000000000000000000000166853FAC00001A07000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev.h/* * thd_cdev.h: thermal cooling class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_H #define THD_CDEV_H #include <time.h> #include <vector> #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_pid.h" #include "thd_adaptive_types.h" typedef struct { int zone; int trip; int target_state_valid; int target_value; int _min_state; int _max_state; int _min_max_valid; } zone_trip_limits_t; #define ZONE_TRIP_LIMIT_COUNT 12 class cthd_cdev { protected: int index; csys_fs cdev_sysfs; unsigned int trip_point; int max_state; int min_state; int curr_state; int curr_pow; int base_pow_state; int inc_dec_val; bool auto_down_adjust; bool read_back; std::string type_str; std::string alias_str; int debounce_interval; time_t last_action_time; bool trend_increase; bool pid_enable; cthd_pid pid_ctrl; int last_state; std::vector<zone_trip_limits_t> zone_trip_limits; std::string write_prefix; int inc_val; int dec_val; private: unsigned int int_2_pow(int pow) { int i; int _pow = 1; for (i = 0; i < pow; ++i) _pow = _pow * 2; return _pow; } int thd_cdev_exponential_controller(int set_point, int target_temp, int temperature, int state, int arg, int temp_min_state = 0, int temp_max_state = 0); int thd_clamp_state_min(int _state, int temp_min_state = 0); int thd_clamp_state_max(int _state, int temp_max_state = 0); public: static const int default_debounce_interval = 2; // In seconds cthd_cdev(unsigned int _index, std::string control_path) : index(_index), cdev_sysfs(control_path.c_str()), trip_point(0), max_state( 0), min_state(0), curr_state(0), curr_pow(0), base_pow_state( 0), inc_dec_val(1), auto_down_adjust(false), read_back( true), debounce_interval(default_debounce_interval), last_action_time( 0), trend_increase(false), pid_enable(false), pid_ctrl(), last_state( 0), write_prefix(""), inc_val(0), dec_val(0) { } virtual ~cthd_cdev() { } virtual int thd_cdev_set_state(int set_point, int target_temp, int temperature, int hard_target, int state, int zone_id, int trip_id, int target_state_valid, int target_value, pid_param_t *pid_param, cthd_pid &pid, bool force, int min_max_valid, int _min_state, int _max_state); virtual int thd_cdev_set_min_state(int zone_id, int trip_id); virtual void thd_cdev_set_min_state_param(int arg) { min_state = arg; } virtual void thd_cdev_set_max_state_param(int arg) { max_state = arg; } virtual void thd_cdev_set_read_back_param(bool arg) { read_back = arg; } virtual int thd_cdev_get_index() { return index; } virtual int init() { return 0; } ; virtual int control_begin() { if (pid_enable) { pid_ctrl.reset(); } return 0; } ; virtual int control_end() { return 0; } ; virtual void set_curr_state(int state, int arg) { } virtual void set_curr_state_raw(int state, int arg) { if (state > max_state) state = max_state; if (state < min_state) state = min_state; set_curr_state(state, arg); } virtual int get_curr_state() { return curr_state; } virtual int get_curr_state(bool read_again) { return curr_state; } virtual int get_min_state() { return min_state; } virtual int get_max_state() { return max_state; } virtual int get_phy_max_state() { return max_state; } virtual int update() { return 0; } ; virtual void set_inc_dec_value(int value) { inc_dec_val = value; } virtual void set_inc_value(int value) { inc_val = value; } virtual void set_dec_value(int value) { dec_val = value; } virtual void set_down_adjust_control(bool value) { auto_down_adjust = value; } virtual int map_target_state(int target_valid, int target_state) { return target_state; } virtual void set_adaptive_target(struct adaptive_target) {}; void set_debounce_interval(int interval) { debounce_interval = interval; } void set_min_state(int _min_state) { min_state = _min_state; } void set_max_state(int _max_state) { max_state = _max_state; } bool in_min_state() { if ((min_state < max_state && get_curr_state() <= min_state) || (min_state > max_state && get_curr_state() >= min_state)) return true; return false; } bool in_max_state() { if ((min_state < max_state && get_curr_state() >= get_max_state()) || (min_state > max_state && get_curr_state() <= get_max_state())) return true; return false; } int cmp_current_state(int state) { if (get_curr_state() == state) return 0; if (min_state < max_state) { if (state > get_curr_state()) return 1; else return -1; } if (min_state > max_state) { if (state > get_curr_state()) return -1; else return 1; } return 0; } std::string get_cdev_type() { return type_str; } std::string get_cdev_alias() { return alias_str; } std::string get_base_path() { return cdev_sysfs.get_base_path(); } void set_cdev_type(std::string _type_str) { type_str = _type_str; } void set_cdev_alias(std::string _alias_str) { alias_str = _alias_str; } void set_pid_param(double kp, double ki, double kd) { pid_ctrl.kp = kp; pid_ctrl.ki = ki; pid_ctrl.kd = kd; thd_log_info("set_pid_param %d [%g.%g,%g]\n", index, kp, ki, kd); } void enable_pid() { thd_log_info("PID control enabled %d\n", index); pid_enable = true; } void thd_cdev_set_write_prefix(std::string prefix) { write_prefix = prefix; } void cdev_dump() { if (inc_val || dec_val){ thd_log_info("%d: %s, C:%d MN: %d MX:%d Inc ST:%d Dec ST:%d pt:%s rd_bk %d \n", index, type_str.c_str(), curr_state, min_state, max_state, inc_val, dec_val, get_base_path().c_str(), read_back); } else { thd_log_info("%d: %s, C:%d MN: %d MX:%d ST:%d pt:%s rd_bk %d \n", index, type_str.c_str(), curr_state, min_state, max_state, inc_dec_val, get_base_path().c_str(), read_back); } } }; #endif 07070100000027000081A400000000000000000000000166853FAC00000E82000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_backlight.cpp/* * thd_cdev_backlight.cpp: thermal backlight cooling implementation * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_backlight.h" #define MAX_BACKLIGHT_DEV 4 const std::string cthd_cdev_backlight::backlight_devices[MAX_BACKLIGHT_DEV] = { "/sys/class/backlight/intel_backlight/", "/sys/class/backlight/acpi_video0/", "/sys/class/leds/lcd-backlight/", "/sys/class/backlight/lcd-backlight/" }; cthd_cdev_backlight::cthd_cdev_backlight(unsigned int _index, int _cpu_index) : cthd_cdev(_index, backlight_devices[0]), ref_backlight_state(0) { int active_device = 0; do { cdev_sysfs.update_path(backlight_devices[active_device]); if (update() == THD_SUCCESS) break; active_device++; } while (active_device < MAX_BACKLIGHT_DEV); } int cthd_cdev_backlight::update() { int ret; if (cdev_sysfs.exists()) { std::string temp_str; ret = cdev_sysfs.read("max_brightness", temp_str); if (ret < 0) return ret; std::istringstream(temp_str) >> max_state; } if (max_state <= 0) return THD_ERROR; set_inc_dec_value(max_state * (float) 10 / 100); // Don't let backlight less than min_backlight_percent percent min_back_light = max_state * min_backlight_percent /100; return THD_SUCCESS; } int cthd_cdev_backlight::map_target_state(int target_valid, int target_state) { if (!target_valid) return target_state; if (target_state > max_state) return 0; return max_state - target_state; } void cthd_cdev_backlight::set_curr_state(int state, int arg) { int ret; int backlight_val; /* * When state > 0, then we need to reduce backlight. But we should start * from backlight which triggered thermal condition. That is here stored * in ref_backlight_state. * When the first state more than 0 is called, then this variable is * updated with the current backlight value. When the state == 0, * it is restored. */ if (state == 0) { if (ref_backlight_state) { thd_log_debug("LCD restore original %d\n", ref_backlight_state); ret = cdev_sysfs.write("brightness", ref_backlight_state); if (ret < 0) { thd_log_warn("Failed to write brightness\n"); return; } ref_backlight_state = 0; } curr_state = state; return; } if (ref_backlight_state == 0 && state == inc_dec_val) { std::string temp_str; // First time enter to throttle LCD after normal or state == 0 // Store the current backlight ret = cdev_sysfs.read("brightness", temp_str); if (ret < 0) return; std::istringstream(temp_str) >> ref_backlight_state; thd_log_debug("LCD ref state is %d\n", ref_backlight_state); } backlight_val = ref_backlight_state - state; if (backlight_val <= min_back_light) { thd_log_debug("LCD reached min state\n"); backlight_val = min_back_light; } ret = cdev_sysfs.write("brightness", backlight_val); if (ret < 0) { thd_log_warn("Failed to write brightness\n"); return; } curr_state = state; } 07070100000028000081A400000000000000000000000166853FAC00000579000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_backlight.h/* * thd_cdev_backlight.h: thermal backlight cooling interface * * Copyright (C) 2015 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef SRC_THD_CDEV_BACKLIGHT_H_ #define SRC_THD_CDEV_BACKLIGHT_H_ #include "thd_cdev.h" class cthd_cdev_backlight: public cthd_cdev { private: static const std::string backlight_devices[]; int ref_backlight_state; int min_back_light; public: static const int min_backlight_percent = 25; // max percent from max cthd_cdev_backlight(unsigned int _index, int _cpu_index); void set_curr_state(int state, int arg); int update(); int map_target_state(int target_valid, int target_state); }; #endif /* SRC_THD_CDEV_BACKLIGHT_H_ */ 07070100000029000081A400000000000000000000000166853FAC000015B5000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_cpufreq.cpp/* * thd_cdev_pstates.cpp: thermal cooling class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* Control P states using cpufreq. Each step reduces to next lower frequency * */ #include "thd_cdev_cpufreq.h" #include "thd_engine.h" int cthd_cdev_cpufreq::init() { // Get number of CPUs if (cdev_sysfs.exists("present")) { std::string count_str; size_t p0 = 0, p1; cdev_sysfs.read("present", count_str); p1 = count_str.find_first_of("-", p0); if (p1 == std::string::npos) return THD_ERROR; std::string token1 = count_str.substr(p0, p1 - p0); if (token1.empty()) return THD_ERROR; std::istringstream(token1) >> cpu_start_index; if ((p1 + 1) >= count_str.size()) return THD_ERROR; std::string token2 = count_str.substr(p1 + 1); if (token2.empty()) return THD_ERROR; std::istringstream(token2) >> cpu_end_index; if ((cpu_end_index <= 0) || (cpu_end_index < cpu_start_index) || cpu_end_index > 63) return THD_ERROR; } else { return THD_ERROR; } thd_log_debug("pstate CPU present %d-%d\n", cpu_start_index, cpu_end_index); // Get list of available frequencies for each CPU // Assuming every core supports same sets of frequencies, so // just reading for cpu0 std::vector<std::string> _cpufreqs; if (cdev_sysfs.exists("cpu0/cpufreq/scaling_available_frequencies")) { std::string p = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"; std::ifstream f(p.c_str(), std::fstream::in); if (f.fail()) return -EINVAL; while (!f.eof()) { std::string token; f >> token; if (!f.bad()) { if (!token.empty()) _cpufreqs.push_back(token); } } f.close(); } else return THD_ERROR; // Check scaling max frequency and min frequency // Remove frequencies above and below this in the freq list // The available list contains these frequencies even if they are not allowed unsigned int scaling_min_frequency = 0; unsigned int scaling_max_frequency = 0; for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; std::string freq_str; str << "cpu" << i << "/cpufreq/scaling_min_freq"; if (cdev_sysfs.exists(str.str())) { cdev_sysfs.read(str.str(), freq_str); unsigned int freq_int; std::istringstream(freq_str) >> freq_int; if (scaling_min_frequency == 0 || freq_int < scaling_min_frequency) scaling_min_frequency = freq_int; } } for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; std::string freq_str; str << "cpu" << i << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { cdev_sysfs.read(str.str(), freq_str); unsigned int freq_int; std::istringstream(freq_str) >> freq_int; if (scaling_max_frequency == 0 || freq_int > scaling_max_frequency) scaling_max_frequency = freq_int; } } thd_log_debug("cpu freq max %u min %u\n", scaling_max_frequency, scaling_min_frequency); for (unsigned int i = 0; i < _cpufreqs.size(); ++i) { thd_log_debug("cpu freq Add %d: %s\n", i, _cpufreqs[i].c_str()); unsigned int freq_int; std::istringstream(_cpufreqs[i]) >> freq_int; if (freq_int >= scaling_min_frequency && freq_int <= scaling_max_frequency) { add_frequency(freq_int); } } for (unsigned int i = 0; i < cpufreqs.size(); ++i) { thd_log_debug("cpu freq %d: %d\n", i, cpufreqs[i]); } if (cpufreqs.size()) max_state = cpufreqs.size() - 1; pstate_active_freq_index = 0; return THD_SUCCESS; } void cthd_cdev_cpufreq::add_frequency(unsigned int freq_int) { if (cpufreqs.empty() || cpufreqs.at(0) > (int) freq_int) cpufreqs.push_back(freq_int); else { std::vector<int>::iterator it; it = cpufreqs.begin(); cpufreqs.insert(it, freq_int); } } void cthd_cdev_cpufreq::set_curr_state(int state, int arg) { if (state < (int) cpufreqs.size()) { thd_log_debug("cpu freq set_curr_stat %d: %d\n", state, cpufreqs[state]); if (cpu_index == -1) { for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; str << "cpu" << i << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { std::stringstream speed; speed << cpufreqs[state]; cdev_sysfs.write(str.str(), speed.str()); } pstate_active_freq_index = state; curr_state = state; } } else { if (thd_engine->apply_cpu_operation(cpu_index)) { std::stringstream str; str << "cpu" << cpu_index << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { std::stringstream speed; speed << cpufreqs[state]; cdev_sysfs.write(str.str(), speed.str()); } pstate_active_freq_index = state; curr_state = state; } } } } int cthd_cdev_cpufreq::get_max_state() { return cpufreqs.size() - 1; } int cthd_cdev_cpufreq::update() { return init(); } 0707010000002A000081A400000000000000000000000166853FAC00000636000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_cpufreq.h/* * thd_cdev_pstates.h: thermal cooling class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_PSTATES_H_ #define THD_CDEV_PSTATES_H_ #include <string> #include <vector> #include "thd_cdev.h" class cthd_cdev_cpufreq: public cthd_cdev { private: int cpu_start_index; int cpu_end_index; std::vector<int> cpufreqs; int pstate_active_freq_index; std::string last_governor; int cpu_index; void add_frequency(unsigned int freq_int); public: cthd_cdev_cpufreq(unsigned int _index, int _cpu_index) : cthd_cdev(_index, "/sys/devices/system/cpu/"), cpu_start_index(0), cpu_end_index( 0), pstate_active_freq_index(0), last_governor( ""), cpu_index(_cpu_index) { } int init(); void set_curr_state(int state, int arg); int get_max_state(); int update(); }; #endif /* THD_CDEV_PSTATES_H_ */ 0707010000002B000081A400000000000000000000000166853FAC00000734000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_gen_sysfs.cpp/* * cthd_sysfs_gen_sysfs.cpp: thermal cooling class interface * for non thermal cdev sysfs * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_gen_sysfs.h" int cthd_gen_sysfs_cdev::update() { if (cdev_sysfs.exists()) { std::string state_str; cdev_sysfs.read("", state_str); std::istringstream(state_str) >> curr_state; min_state = max_state = curr_state; } else { if (cdev_sysfs.get_base_path() && !strcmp(cdev_sysfs.get_base_path(), "")) return THD_ERROR; int ret = cdev_sysfs.create(); if (ret < 0) return THD_ERROR; ret = cdev_sysfs.write("", 0); if (ret < 0) return THD_ERROR; } return THD_SUCCESS; } void cthd_gen_sysfs_cdev::set_curr_state(int state, int arg) { std::stringstream state_str; if (write_prefix.length()) state_str << write_prefix; state_str << state; thd_log_debug("set cdev state index %d state %d %s\n", index, state, state_str.str().c_str()); cdev_sysfs.write("", state_str.str()); curr_state = state; } void cthd_gen_sysfs_cdev::set_curr_state_raw(int state, int arg) { set_curr_state(state, arg); } 0707010000002C000081A400000000000000000000000166853FAC00000522000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_gen_sysfs.h/* * cthd_sysfs_gen_sysfs.h: thermal cooling class interface * for non thermal cdev sysfs * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_GEN_SYSFS_H_ #define THD_CDEV_GEN_SYSFS_H_ #include "thd_cdev.h" class cthd_gen_sysfs_cdev: public cthd_cdev { protected: public: cthd_gen_sysfs_cdev(unsigned int _index, std::string control_path) : cthd_cdev(_index, control_path) { } virtual void set_curr_state(int state, int arg); virtual void set_curr_state_raw(int state, int arg); virtual int update(); }; #endif /* THD_CDEV_GEN_SYSFS_H_ */ 0707010000002D000081A400000000000000000000000166853FAC00000F3A000000000000000000000000000000000000004900000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_intel_pstate_driver.cpp/* * thd_sysfs_intel_pstate_driver.cpp: thermal cooling class implementation * using Intel p state driver * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_intel_pstate_driver.h" /* * This implementation allows controlling get max state and * set current state of using Intel P state driver * P state drives uses a percent count. 100% means full * performance, setting anything lower limits performance. * Each lower state reduces performance by unit value. * unit value is calculated using number of possible p states * . Each step reduces bye one p state. * Contents of "/sys/devices/system/cpu/intel_pstate/" max_perf_pct, min_perf_pct, no_turbo */ void cthd_intel_p_state_cdev::set_curr_state(int state, int arg) { std::stringstream tc_state_dev; int new_state; tc_state_dev << "/max_perf_pct"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::stringstream state_str; if (state == 0) new_state = 100; else { new_state = 100 - (state + min_compensation) * unit_value; } state_str << new_state; thd_log_debug("set cdev state index %d state %d percent %d\n", index, state, new_state); if (new_state <= turbo_disable_percent) set_turbo_disable_status(true); else set_turbo_disable_status(false); if (cdev_sysfs.write(tc_state_dev.str(), state_str.str()) < 0) curr_state = (state == 0) ? 0 : max_state; else curr_state = state; } else curr_state = (state == 0) ? 0 : max_state; } void cthd_intel_p_state_cdev::set_turbo_disable_status(bool enable) { std::stringstream tc_state_dev; if (enable == turbo_status) { return; } tc_state_dev << "/no_turbo"; if (enable) { cdev_sysfs.write(tc_state_dev.str(), "1"); thd_log_info("turbo disabled \n"); } else { cdev_sysfs.write(tc_state_dev.str(), "0"); thd_log_info("turbo enabled \n"); } turbo_status = enable; } int cthd_intel_p_state_cdev::get_max_state() { return max_state; } int cthd_intel_p_state_cdev::map_target_state(int target_valid, int target_state) { if (!target_valid) return target_state; if (target_state > 100) return 0; return (100 - target_state) / unit_value; } int cthd_intel_p_state_cdev::update() { std::stringstream tc_state_dev; std::stringstream status_attr; status_attr << "/status"; if (cdev_sysfs.exists(status_attr.str())) { std::string status_str; int ret; ret = cdev_sysfs.read(status_attr.str(), status_str); if (ret >= 0 && status_str != "active") { thd_log_info("intel pstate is not in active mode\n"); return THD_ERROR; } } tc_state_dev << "/max_perf_pct"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else { return THD_ERROR; } thd_log_info("Use Default pstate drv settings\n"); max_state = default_max_state; min_compensation = 0; unit_value = 100.0 / max_state; curr_state = 0; thd_log_debug( "cooling dev index:%d, curr_state:%d, max_state:%d, unit:%f, min_com:%d, type:%s\n", index, curr_state, max_state, unit_value, min_compensation, type_str.c_str()); return THD_SUCCESS; } 0707010000002E000081A400000000000000000000000166853FAC00000695000000000000000000000000000000000000004700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_intel_pstate_driver.h/* * thd_sysfs_intel_pstate_driver.h: thermal cooling class interface * using Intel p state driver * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_INTEL_PSATATE_DRIVER_H_ #define THD_CDEV_INTEL_PSATATE_DRIVER_H_ #include "thd_cdev.h" class cthd_intel_p_state_cdev: public cthd_cdev { private: float unit_value; int min_compensation; bool turbo_status; void set_turbo_disable_status(bool enable); public: static const int intel_pstate_limit_ratio = 2; static const int default_max_state = 10; static const int turbo_disable_percent = 70; cthd_intel_p_state_cdev(unsigned int _index) : cthd_cdev(_index, "/sys/devices/system/cpu/intel_pstate/"), unit_value( 1), min_compensation(0), turbo_status(false) { } ; void set_curr_state(int state, int arg); int get_max_state(); int update(); int map_target_state(int target_valid, int target_state); }; #endif /* THD_CDEV_INTEL_PSATATE_DRIVER_H_ */ 0707010000002F000081A400000000000000000000000166853FAC00000DC2000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_kbl_amdgpu.cpp/* * thd_cdev_kbl_amdgpu: amdgpu power control * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_cdev_kbl_amdgpu.h" cthd_cdev_kgl_amdgpu::cthd_cdev_kgl_amdgpu(unsigned int _index, int _cpu_index) : cthd_cdev(_index, ""),activated(0) { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/hwmon/"; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { // Check name std::string name_path = base_path + entry->d_name + "/name"; std::ifstream ifs(name_path.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { if (line == "amdgpu") { cdev_sysfs.update_path(base_path + entry->d_name + "/"); } } ifs.close(); } } closedir(dir); } } int cthd_cdev_kgl_amdgpu::get_curr_state() { if (activated) return get_curr_state(true); else return min_state; } int cthd_cdev_kgl_amdgpu::get_curr_state(bool read_again) { int ret; int state; ret = cdev_sysfs.read("power1_average", &state); if (ret < 0) { return min_state; } //workaround: Looks for comment in set_curr_state if (state == 1000000) state = max_state; return state; } void cthd_cdev_kgl_amdgpu::set_curr_state(int state, int arg) { int new_state = state; if (!arg) { activated = 0; thd_log_info("ignore \n"); new_state = min_state; } else activated = 1; // When power1_cap = 0 is written to sysfs it changes to max powercap value // So this is a workaround if (state == 0) new_state = min_state; if (cdev_sysfs.write("power1_cap", new_state) > 0) curr_state = state; thd_log_info("set cdev state index %d state %d wr:%d\n", index, state, state); } void cthd_cdev_kgl_amdgpu::set_curr_state_raw(int state, int arg) { set_curr_state(state, arg); } int cthd_cdev_kgl_amdgpu::update() { // min and max are opposite of actual sysfs value in thermald terms if (cdev_sysfs.exists("power1_cap_min")) { std::string type_str; int ret; ret = cdev_sysfs.read("power1_cap_min", &max_state); if (ret < 0) { thd_log_info("cthd_cdev_kgl_amdgpu : not present\n"); return THD_ERROR; } } else { return THD_ERROR; } if (cdev_sysfs.exists("power1_cap_max")) { std::string type_str; int ret; ret = cdev_sysfs.read("power1_cap_max", &min_state); if (ret < 0) { thd_log_info("cthd_cdev_kgl_amdgpu : not present\n"); return THD_ERROR; } } else { return THD_ERROR; } set_inc_dec_value(-(min_state * (float) 10 / 100)); set_pid_param(-0.4, 0, 0); return THD_SUCCESS; } int cthd_cdev_kgl_amdgpu::map_target_state(int target_valid, int target_state) { return 0; } int cthd_cdev_kgl_amdgpu::get_phy_max_state() { return min_state; } 07070100000030000081A400000000000000000000000166853FAC00000560000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_kbl_amdgpu.h/* * thd_cdev_kbl_amdgpu: amdgpu power control * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef SRC_THD_CDEV_KBL_AMD_GPU_H_ #define SRC_THD_CDEV_KBL_AMD_GPU_H_ #include "thd_cdev.h" class cthd_cdev_kgl_amdgpu: public cthd_cdev { private: int activated; public: cthd_cdev_kgl_amdgpu(unsigned int _index, int _cpu_index); void set_curr_state(int state, int arg); void set_curr_state_raw(int state, int arg); int get_curr_state(); int get_curr_state(bool read_again); int update(); int map_target_state(int target_valid, int target_state); int get_phy_max_state(); }; #endif /* SRC_THD_CDEV_BACKLIGHT_H_ */ 07070100000031000081A400000000000000000000000166853FAC00000A86000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_order_parser.cpp/* * thd_cdev_order_parser.cpp: Specify cdev order * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_order_parser.h" #include "thd_sys_fs.h" cthd_cdev_order_parse::cthd_cdev_order_parse() : doc(NULL), root_element(NULL) { std::string name = TDCONFDIR; filename = name + "/" "thermal-cpu-cdev-order.xml"; } int cthd_cdev_order_parse::parser_init() { struct stat s; if (stat(filename.c_str(), &s)) return THD_ERROR; doc = xmlReadFile(filename.c_str(), NULL, 0); if (doc == NULL) { thd_log_msg("error: could not parse file %s\n", filename.c_str()); return THD_ERROR; } root_element = xmlDocGetRootElement(doc); if (root_element == NULL) { thd_log_warn("error: could not get root element \n"); return THD_ERROR; } return THD_SUCCESS; } int cthd_cdev_order_parse::start_parse() { parse(root_element, doc); return THD_SUCCESS; } void cthd_cdev_order_parse::parser_deinit() { xmlFreeDoc(doc); } int cthd_cdev_order_parse::parse_new_cdev(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { thd_log_info("node type: Element, name: %s value: %s\n", cur_node->name, tmp_value); cdev_order_list.push_back(tmp_value); xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_cdev_order_parse::parse(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { if (!strcmp((const char*) cur_node->name, "CoolingDeviceOrder")) { parse_new_cdev(cur_node->children, doc); } } } return THD_SUCCESS; } int cthd_cdev_order_parse::get_order_list(std::vector<std::string> &list) { list = cdev_order_list; return 0; } 07070100000032000081A400000000000000000000000166853FAC00000592000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_order_parser.h/* * thd_cdev_order_parser.h: Specify cdev order * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_ORDER_PARSE_H #define THD_CDEV_ORDER_PARSE_H #include <string> #include <vector> #include <libxml/parser.h> #include <libxml/tree.h> #include "thermald.h" class cthd_cdev_order_parse { private: xmlDoc *doc; xmlNode *root_element; std::string filename; std::vector<std::string> cdev_order_list; int parse(xmlNode * a_node, xmlDoc *doc); int parse_new_cdev(xmlNode * a_node, xmlDoc *doc); public: cthd_cdev_order_parse(); int parser_init(); void parser_deinit(); int start_parse(); int get_order_list(std::vector<std::string> &list); }; #endif 07070100000033000081A400000000000000000000000166853FAC0000410C000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_rapl.cpp/* * cthd_cdev_rapl.cpp: thermal cooling class implementation * using RAPL * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_rapl.h" #include "thd_engine.h" /* This uses Intel RAPL driver to cool the system. RAPL driver show * mas thermal spec power in max_state. Each state can compensate * rapl_power_dec_percent, from the max state. * */ void cthd_sysfs_cdev_rapl::set_curr_state(int state, int control) { int new_state = state, ret; if (bios_locked) { if (state <= inc_dec_val) curr_state = min_state; else curr_state = max_state; return; } // If request to set a state which less than max_state i.e. lowest rapl power limit // then limit to the max_state. if (state < max_state) new_state = max_state; // If the state is more or equal to min_state, means that more than the // max rapl power limit, restore the power limit to min_state or // whatever the power on limit. Also make the rapl limit enforcement // to disabled, also restore power on time window. if (new_state >= min_state) { if (power_on_constraint_0_pwr) new_state = power_on_constraint_0_pwr; else new_state = min_state; curr_state = min_state; // If disabled during power on, disable if (!power_on_enable_status) rapl_update_enable_status(0); rapl_update_time_window(power_on_constraint_0_time_window); constrained = false; } else if (control) { if (!constrained) { // If it is the first time to activate this device, set the enabled flag // and set the time window. if (pl0_min_window) rapl_update_time_window(pl0_min_window); else rapl_update_time_window(def_rapl_time_window); // Set enable flag only if it was disabled if (!power_on_enable_status) rapl_update_enable_status(1); constrained = true; } } thd_log_info("set cdev state index %d state %d wr:%d\n", index, state, new_state); ret = rapl_update_pl1(new_state); if (ret < 0) { curr_state = (state == 0) ? 0 : max_state; if (ret == -ENODATA) { thd_log_info("powercap RAPL is BIOS locked, cannot update\n"); bios_locked = true; } } curr_state = new_state; } void cthd_sysfs_cdev_rapl::set_curr_state_raw(int state, int arg) { set_curr_state(state, arg); } // Return the last state or power set during set_curr_state int cthd_sysfs_cdev_rapl::get_curr_state() { return curr_state; } // Return the current power, using this the controller can choose the next state int cthd_sysfs_cdev_rapl::get_curr_state(bool read_again) { if (dynamic_phy_max_enable) { thd_engine->rapl_power_meter.rapl_start_measure_power(); return thd_engine->rapl_power_meter.rapl_action_get_power(PACKAGE); } return curr_state; } int cthd_sysfs_cdev_rapl::get_max_state() { return max_state; } int cthd_sysfs_cdev_rapl::rapl_sysfs_valid() { std::stringstream temp_str; int found_long_term = 0; int i; // The primary control is powercap rapl long_term control // If absent we can't use rapl cooling device for (i = 0; i < rapl_no_time_windows; ++i) { temp_str.str(std::string()); temp_str << "constraint_" << i << "_name"; if (cdev_sysfs.exists(temp_str.str())) { std::string type_str; cdev_sysfs.read(temp_str.str(), type_str); if (type_str == "long_term") { constraint_index = i; found_long_term = 1; } if (type_str == "short_term") { pl2_index = i; } } } if (!found_long_term) { thd_log_info("powercap RAPL no long term time window\n"); return THD_ERROR; } temp_str.str(std::string()); temp_str << "constraint_" << constraint_index << "_power_limit_uw"; if (!cdev_sysfs.exists(temp_str.str())) { thd_log_debug("powercap RAPL no power limit uw %s \n", temp_str.str().c_str()); return THD_ERROR; } temp_str.str(std::string()); temp_str << "constraint_" << constraint_index << "_time_window_us"; if (!cdev_sysfs.exists(temp_str.str())) { thd_log_info("powercap RAPL no time_window_us %s \n", temp_str.str().c_str()); return THD_ERROR; } return 0; } int cthd_sysfs_cdev_rapl::rapl_read_pl1_max() { std::stringstream temp_power_str; int current_pl1_max; temp_power_str << "constraint_" << constraint_index << "_max_power_uw"; if (cdev_sysfs.read(temp_power_str.str(), ¤t_pl1_max) > 0) { return current_pl1_max; } return THD_ERROR; } int cthd_sysfs_cdev_rapl::rapl_read_pl1() { std::stringstream temp_power_str; int current_pl1; temp_power_str << "constraint_" << constraint_index << "_power_limit_uw"; if (cdev_sysfs.read(temp_power_str.str(), ¤t_pl1) > 0) { return current_pl1; } return THD_ERROR; } int cthd_sysfs_cdev_rapl::rapl_update_pl1(int pl1) { std::stringstream temp_power_str; int ret; temp_power_str << "constraint_" << constraint_index << "_power_limit_uw"; ret = cdev_sysfs.write(temp_power_str.str(), pl1); if (ret <= 0) { thd_log_info( "pkg_power: powercap RAPL max power limit failed to write %d \n", pl1); return ret; } return THD_SUCCESS; } int cthd_sysfs_cdev_rapl::rapl_read_pl2() { std::stringstream temp_power_str; int current_pl2; temp_power_str << "constraint_" << pl2_index << "_power_limit_uw"; if (cdev_sysfs.read(temp_power_str.str(), ¤t_pl2) > 0) { return current_pl2; } return THD_ERROR; } int cthd_sysfs_cdev_rapl::rapl_update_pl2(int pl2) { std::stringstream temp_power_str; int ret; if (pl2_index == -1) { thd_log_warn("Asked to set PL2 but couldn't find a PL2 device\n"); return THD_ERROR; } temp_power_str << "constraint_" << pl2_index << "_power_limit_uw"; ret = cdev_sysfs.write(temp_power_str.str(), pl2); if (ret <= 0) { thd_log_info( "pkg_power: powercap RAPL max power limit failed to write PL2 %d \n", pl2); return ret; } return THD_SUCCESS; } int cthd_sysfs_cdev_rapl::rapl_read_time_window() { std::stringstream temp_time_str; int tm_window; temp_time_str << "constraint_" << constraint_index << "_time_window_us"; if (cdev_sysfs.read(temp_time_str.str(), &tm_window) > 0) { return tm_window; } return THD_ERROR; } int cthd_sysfs_cdev_rapl::rapl_update_time_window(int time_window) { std::stringstream temp_time_str; temp_time_str << "constraint_" << constraint_index << "_time_window_us"; if (cdev_sysfs.write(temp_time_str.str(), time_window) <= 0) { thd_log_info( "pkg_power: powercap RAPL time window failed to write %d \n", time_window); return THD_ERROR; } return THD_SUCCESS; } int cthd_sysfs_cdev_rapl::rapl_update_pl2_time_window(int time_window) { std::stringstream temp_time_str; temp_time_str << "constraint_" << pl2_index << "_time_window_us"; if (cdev_sysfs.write(temp_time_str.str(), time_window) <= 0) { thd_log_info( "pkg_power: powercap RAPL time window failed to write %d \n", time_window); return THD_ERROR; } return THD_SUCCESS; } int cthd_sysfs_cdev_rapl::rapl_update_enable_status(int enable) { std::stringstream temp_str; temp_str << "enabled"; if (cdev_sysfs.write(temp_str.str(), enable) <= 0) { thd_log_info( "pkg_power: powercap RAPL enable failed to write %d \n", enable); return THD_ERROR; } return THD_SUCCESS; } int cthd_sysfs_cdev_rapl::rapl_read_enable_status() { std::stringstream temp_str; int enable; temp_str << "enabled"; if (cdev_sysfs.read(temp_str.str(), &enable) > 0) { return enable; } return THD_ERROR; } void cthd_sysfs_cdev_rapl::set_tcc(int tcc) { csys_fs sysfs("/sys/bus/pci/devices/0000:00:04.0/"); if (!sysfs.exists("tcc_offset_degree_celsius")) return; sysfs.write("tcc_offset_degree_celsius", tcc); } void cthd_sysfs_cdev_rapl::set_adaptive_target(struct adaptive_target target) { int argument = std::stoi(target.argument, NULL); if (target.code == "PL1MAX") { int pl1_rapl; min_state = pl0_max_pwr = argument * 1000; pl1_rapl = rapl_read_pl1(); if (curr_state > pl1_rapl) set_curr_state(pl1_rapl, 1); if (curr_state > min_state) set_curr_state(min_state, 1); } else if (target.code == "PL1MIN") { max_state = pl0_min_pwr = argument * 1000; if (curr_state < max_state) set_curr_state(max_state, 1); } else if (target.code == "PL1STEP") { pl0_step_pwr = argument * 1000; set_inc_value(-pl0_step_pwr * 2); set_dec_value(-pl0_step_pwr); } else if (target.code == "PL1TimeWindow") { pl0_min_window = argument * 1000; } else if (target.code == "PL1PowerLimit") { set_curr_state(argument * 1000, 1); } else if (target.code == "PL2PowerLimit") { rapl_update_pl2(argument * 1000); } else if (target.code == "TccOffset") { set_tcc(argument); } } int cthd_sysfs_cdev_rapl::update() { int constraint_phy_max; bool ppcc = false; std::string domain_name; if (rapl_sysfs_valid()) return THD_ERROR; ppcc = read_ppcc_power_limits(); if (ppcc) { // This is a DPTF compatible platform, which defined // maximum and minimum power limits. We can trust this to be something sane. phy_max = pl0_max_pwr; // We want to be aggressive controlling temperature but lazy during // removing of controls set_inc_value(-pl0_step_pwr * 2); set_dec_value(-pl0_step_pwr); min_state = pl0_max_pwr; max_state = pl0_min_pwr; rapl_update_pl1(pl0_max_pwr); if (pl0_max_window > pl0_min_window) rapl_update_time_window(pl0_max_window); // To be efficient to control from the current power instead of PPCC max. thd_engine->rapl_power_meter.rapl_start_measure_power(); dynamic_phy_max_enable = true; //set_debounce_interval(1); // Some system has PL2 limit as 0, then try to set PL2 limit also if (!rapl_read_pl2()) { thd_log_info("PL2 power limit is 0, will conditionally enable\n"); if (pl1_max_pwr) { thd_log_info("PL2 limits are updated to %d %d\n", pl1_max_pwr, pl1_max_window); rapl_update_pl2(pl1_max_pwr); rapl_update_pl2_time_window(pl1_max_window); rapl_update_enable_status(1); } } else { // By default enable the rapl device to enforce any power limits rapl_update_enable_status(1); } } else { // This is not a DPTF platform // Read the max power from the powercap rapl sysfs // If not present, we can't use rapl to cool. phy_max = rapl_read_pl1_max(); // Check if there is any sane max power limit set if (phy_max < 0 || phy_max > rapl_max_sane_phy_max) { int ret = cdev_sysfs.read("name", domain_name); if (!ret) thd_log_info("%s:powercap RAPL invalid max power limit range \n", domain_name.c_str()); thd_log_info("Calculate dynamically phy_max \n"); power_on_constraint_0_pwr = rapl_read_pl1(); thd_log_debug("power_on_constraint_0_pwr %d\n", power_on_constraint_0_pwr); power_on_constraint_0_time_window = rapl_read_time_window(); thd_log_debug("power_on_constraint_0_time_window %d\n", power_on_constraint_0_time_window); phy_max = max_state = 0; curr_state = min_state = rapl_max_sane_phy_max; thd_engine->rapl_power_meter.rapl_start_measure_power(); set_inc_dec_value(-rapl_min_default_step); dynamic_phy_max_enable = true; return THD_SUCCESS; } constraint_phy_max = rapl_read_pl1(); // If the constraint_0_max_power_uw < constraint_0_power_limit_uw // Use constraint_0_power_limit_uw as the phy_max and min_state if (constraint_phy_max > phy_max) { thd_log_info( "Default constraint power limit is more than max power %d:%d\n", constraint_phy_max, phy_max); phy_max = constraint_phy_max; } thd_log_info("powercap RAPL max power limit range %d \n", phy_max); set_inc_dec_value(-phy_max * (float) rapl_power_dec_percent / 100); min_state = phy_max; max_state = min_state - (float) min_state * rapl_low_limit_percent / 100; } power_on_constraint_0_time_window = rapl_read_time_window(); power_on_enable_status = rapl_read_enable_status(); thd_log_debug("power_on_enable_status: %d\n", power_on_enable_status); thd_log_debug("power_on_constraint_0_time_window: %d\n", power_on_constraint_0_time_window); thd_log_debug("RAPL max limit %d increment: %d\n", max_state, inc_dec_val); set_pid_param(-1000, 100, 10); curr_state = min_state; return THD_SUCCESS; } bool cthd_sysfs_cdev_rapl::read_ppcc_power_limits() { csys_fs sys_fs; ppcc_t *ppcc; ppcc = thd_engine->get_ppcc_param(device_name); if (ppcc) { int def_max_power; thd_log_info("Reading PPCC from the thermal-conf.xml\n"); pl0_max_pwr = ppcc->power_limit_max * 1000; pl0_min_pwr = ppcc->power_limit_min * 1000; pl0_min_window = ppcc->time_wind_min * 1000; pl0_max_window = ppcc->time_wind_max * 1000; pl0_step_pwr = ppcc->step_size * 1000; pl1_valid = ppcc->limit_1_valid; if (pl1_valid) { pl1_max_pwr = ppcc->power_limit_1_max * 1000; pl1_min_pwr = ppcc->power_limit_1_min * 1000; pl1_min_window = ppcc->time_wind_1_min * 1000; pl1_max_window = ppcc->time_wind_1_max * 1000; pl1_step_pwr = ppcc->step_1_size * 1000; } if (pl0_max_pwr <= pl0_min_pwr) { thd_log_info("Invalid limits: ppcc limits max:%u min:%u min_win:%u step:%u\n", pl0_max_pwr, pl0_min_pwr, pl0_min_window, pl0_step_pwr); return false; } thd_log_info("ppcc limits max:%u min:%u min_win:%u step:%u\n", pl0_max_pwr, pl0_min_pwr, pl0_min_window, pl0_step_pwr); int policy_matched; policy_matched = thd_engine->search_idsp("63BE270F-1C11-48FD-A6F7-3AF253FF3E2D"); if (policy_matched != THD_SUCCESS) policy_matched = thd_engine->search_idsp("9E04115A-AE87-4D1C-9500-0F3E340BFE75"); if (policy_matched == THD_SUCCESS) { thd_log_info("IDSP policy matched, so trusting PPCC limits\n"); return true; } def_max_power = rapl_read_pl1_max(); if (def_max_power > pl0_max_pwr) thd_log_warn("ppcc limits is less than def PL1 max power :%d check thermal-conf.xml.auto\n", def_max_power); return true; } std::string domain_name; // Since this base class is also used by DRAM rapl, avoid reading PPCC as // there are no power limits defined by DPTF based systems for any other // domain other than package-0 cdev_sysfs.read("name", domain_name); if (domain_name != "package-0") return false; if (sys_fs.exists("/sys/bus/pci/devices/0000:00:04.0/power_limits/")) sys_fs.update_path("/sys/bus/pci/devices/0000:00:04.0/power_limits/"); else if (sys_fs.exists("/sys/bus/pci/devices/0000:00:0b.0/power_limits/")) sys_fs.update_path("/sys/bus/pci/devices/0000:00:0b.0/power_limits/"); else if (sys_fs.exists( "/sys/bus/platform/devices/INT3401:00/power_limits/")) sys_fs.update_path( "/sys/bus/platform/devices/INT3401:00/power_limits/"); else return false; if (sys_fs.exists("power_limit_0_max_uw")) { if (sys_fs.read("power_limit_0_max_uw", &pl0_max_pwr) <= 0) return false; } if (sys_fs.exists("power_limit_0_min_uw")) { if (sys_fs.read("power_limit_0_min_uw", &pl0_min_pwr) <= 0) return false; } if (sys_fs.exists("power_limit_0_tmin_us")) { if (sys_fs.read("power_limit_0_tmin_us", &pl0_min_window) <= 0) return false; } if (sys_fs.exists("power_limit_0_tmax_us")) { if (sys_fs.read("power_limit_0_tmax_us", &pl0_max_window) <= 0) return false; } if (sys_fs.exists("power_limit_0_step_uw")) { if (sys_fs.read("power_limit_0_step_uw", &pl0_step_pwr) <= 0) return false; } if (pl0_max_pwr && pl0_min_pwr && pl0_min_window && pl0_step_pwr && pl0_max_window) { int def_max_power; if (pl0_max_pwr <= pl0_min_pwr) { thd_log_info("Invalid limits: ppcc limits max:%u min:%u min_win:%u step:%u\n", pl0_max_pwr, pl0_min_pwr, pl0_min_window, pl0_step_pwr); return false; } thd_log_info("ppcc limits max:%u min:%u min_win:%u step:%u\n", pl0_max_pwr, pl0_min_pwr, pl0_min_window, pl0_step_pwr); def_max_power = rapl_read_pl1_max(); if (def_max_power > pl0_max_pwr) { thd_log_info("ppcc limits is less than def PL1 max power :%d, so ignore\n", def_max_power); return false; } return true; } return false; } void cthd_sysfs_cdev_rapl::thd_cdev_set_min_state_param(int arg) { min_state = curr_state = arg; } 07070100000034000081A400000000000000000000000166853FAC00000EBA000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_rapl.h/* * cthd_cdev_rapl.h: thermal cooling class interface * using RAPL * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_RAPL_H_ #define THD_CDEV_RAPL_H_ #include "thd_cdev.h" #include "thd_sys_fs.h" class cthd_sysfs_cdev_rapl: public cthd_cdev { protected: int phy_max; int package_id; int constraint_index; int pl2_index; bool dynamic_phy_max_enable; int pl0_max_pwr; int pl0_min_pwr; int pl0_min_window; int pl0_max_window; int pl0_step_pwr; int pl1_max_pwr; int pl1_min_pwr; int pl1_min_window; int pl1_max_window; int pl1_step_pwr; int pl1_valid; bool bios_locked; bool constrained; int power_on_constraint_0_pwr; int power_on_constraint_0_time_window; int power_on_enable_status; std::string device_name; virtual bool read_ppcc_power_limits(); private: int rapl_sysfs_valid(); int rapl_read_pl1(); int rapl_read_pl1_max(); int rapl_update_pl1(int pl1); int rapl_read_pl2(); int rapl_update_pl2(int pl2); int rapl_read_time_window(); int rapl_update_time_window(int time_window); int rapl_update_pl2_time_window(int time_window); int rapl_read_enable_status(); public: static const int rapl_no_time_windows = 6; static const long def_rapl_time_window = 1000000; // micro seconds static const int rapl_min_default_step = 500000; //0.5W static const int rapl_max_sane_phy_max = 100000000; // Some sane very high value in uW static const int rapl_low_limit_percent = 50; static const int rapl_power_dec_percent = 5; cthd_sysfs_cdev_rapl(unsigned int _index, int package) : cthd_cdev(_index, "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:0/"), phy_max( 0), package_id(package), constraint_index( 0), pl2_index(-1), dynamic_phy_max_enable( false), pl0_max_pwr(0), pl0_min_pwr(0), pl0_min_window( 0), pl0_max_window(0), pl0_step_pwr( 0), bios_locked(false), constrained( false), power_on_constraint_0_pwr(0), power_on_constraint_0_time_window( 0), power_on_enable_status(0), device_name("TCPU.D0") { } cthd_sysfs_cdev_rapl(unsigned int _index, int package, std::string contol_path) : cthd_cdev(_index, contol_path), phy_max(0), package_id( package), constraint_index( 0), pl2_index( -1), dynamic_phy_max_enable(false), pl0_max_pwr( 0), pl0_min_pwr( 0), pl0_min_window(0), pl0_max_window(0), pl0_step_pwr(0), bios_locked( false), constrained( false), power_on_constraint_0_pwr(0), power_on_constraint_0_time_window( 0), power_on_enable_status(0), device_name("TCPU.D0") { } virtual void set_curr_state(int state, int arg); virtual int get_curr_state(); virtual int get_curr_state(bool read_again); virtual int get_max_state(); virtual int update(); virtual void set_curr_state_raw(int state, int arg); void set_tcc(int tcc); void set_adaptive_target(struct adaptive_target target); void thd_cdev_set_min_state_param(int arg); int get_phy_max_state() { return phy_max; } int rapl_update_enable_status(int enable); }; #endif /* THD_CDEV_RAPL_H_ */ 07070100000035000081A400000000000000000000000166853FAC000006F5000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_rapl_dram.cpp/* * cthd_cdev_rapl_dram.cpp: thermal cooling class implementation * using RAPL DRAM * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_cdev_rapl_dram.h" #include "thd_engine.h" int cthd_sysfs_cdev_rapl_dram::update() { DIR *dir; struct dirent *entry; std::string base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:0/"; bool found = false; std::string path_name; dir = opendir(base.c_str()); if (!dir) return THD_ERROR; while ((entry = readdir(dir)) != NULL) { std::string temp_str; temp_str = base + entry->d_name + "/" + "name"; csys_fs name_sysfs(temp_str.c_str()); if (!name_sysfs.exists()) { continue; } std::string name; if (name_sysfs.read("", name) < 0) { continue; } thd_log_info("name = %s\n", name.c_str()); if (name == "dram") { found = true; path_name = base + entry->d_name + "/"; break; } } closedir(dir); if (!found) return THD_ERROR; cdev_sysfs.update_path(path_name); return cthd_sysfs_cdev_rapl::update(); } 07070100000036000081A400000000000000000000000166853FAC000004E0000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_rapl_dram.h/* * cthd_cdev_rapl_dram.h: thermal cooling class interface * using RAPL DRAM * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_RAPL_DRAM_H_ #define THD_CDEV_RAPL_DRAM_H_ #include "thd_cdev_rapl.h" class cthd_sysfs_cdev_rapl_dram: public cthd_sysfs_cdev_rapl { private: public: cthd_sysfs_cdev_rapl_dram(unsigned int _index, int _package) : cthd_sysfs_cdev_rapl(_index, _package) { device_name = "TMEM.D0"; } virtual int update(); }; #endif /* THD_CDEV_RAPL_DRAM_H_ */ 07070100000037000081A400000000000000000000000166853FAC00000D77000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_therm_sys_fs.cpp/* * cthd_sysfs_cdev.cpp: thermal cooling class implementation * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_cdev_therm_sys_fs.h" #include "thd_engine.h" /* This uses ACPI style thermal sysfs interface to set states. * It expects, max_state. curr_state in thermal sysfs and uses * these sysfs-files to control. * */ int cthd_sysfs_cdev::update() { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else curr_state = 0; std::stringstream tc_max_state_dev; tc_max_state_dev << "cooling_device" << index << "/max_state"; if (cdev_sysfs.exists(tc_max_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_max_state_dev.str(), state_str); std::istringstream(state_str) >> max_state; } else max_state = 0; std::stringstream tc_type_dev; tc_type_dev << "cooling_device" << index << "/type"; if (cdev_sysfs.exists(tc_type_dev.str())) { cdev_sysfs.read(tc_type_dev.str(), type_str); if (type_str.size()) { // They essentially change same ACPI object, so reading their // state from sysfs after a change to any processor will cause // double compensation if (type_str == "Processor") read_back = false; } } thd_log_debug("cooling dev %d:%d:%d:%s\n", index, curr_state, max_state, type_str.c_str()); return THD_SUCCESS; } int cthd_sysfs_cdev::get_max_state() { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/max_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> max_state; } else max_state = 0; return max_state; } void cthd_sysfs_cdev::set_curr_state(int state, int arg) { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::stringstream state_str; state_str << state; thd_log_debug("set cdev state index %d state %d\n", index, state); cdev_sysfs.write(tc_state_dev.str(), state_str.str()); curr_state = state; } else curr_state = 0; } int cthd_sysfs_cdev::get_curr_state() { if (!read_back) { return curr_state; } std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else curr_state = 0; return curr_state; } 07070100000038000081A400000000000000000000000166853FAC0000051F000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cdev_therm_sys_fs.h/* * cthd_sysfs_cdev.cpp: thermal cooling class interface * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_THERM_SYS_FS_H_ #define THD_CDEV_THERM_SYS_FS_H_ #include "thd_cdev.h" class cthd_sysfs_cdev: public cthd_cdev { protected: public: cthd_sysfs_cdev(unsigned int _index, std::string control_path) : cthd_cdev(_index, control_path) { } virtual void set_curr_state(int state, int arg); virtual int get_curr_state(); virtual int get_max_state(); virtual int update(); }; #endif /* THD_CDEV_THERM_SYS_FS_H_ */ 07070100000039000081A400000000000000000000000166853FAC000003B1000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_common.h/* thd_msr.cpp: thermal engine common definitions * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_COMMON_H_ #define THD_COMMON_H_ #include "thermald.h" #endif /* THD_COMMON_H_ */ 0707010000003A000081A400000000000000000000000166853FAC000026CD000000000000000000000000000000000000004400000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cpu_default_binding.cpp/* * thd_default_binding.cpp: Default binding of thermal zones * implementation file * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* * It will search for unbounded zones (There is valid passive trips and temp * reading is available but there is no cooling driver attached to it via XML * config or default hardcoded config. * In this case if CPU is causing this temp to go up then it will use RAPL and * power clamp to cool. If it fails for three times then this zone is added * to black list, so that this method will never be tried again, * The way this file implements using existing mechanism via a two pseudo * cooling devices "gates" called start and exit. The start gate checks the * current cpu load, if yes then it opens gate by changing it to max state. * Similarly if end gate state is reached means that the RAPL and power clamp * failed to control temperature of this zone. */ #include "thd_zone_therm_sys_fs.h" #include "thd_cpu_default_binding.h" class cthd_gating_cdev: public cthd_cdev { private: class cthd_cpu_default_binding *def_bind_ref; cpu_zone_binding_t *bind_zone; bool start; public: static const int max_state = 0x01; cthd_gating_cdev(int _id, cthd_cpu_default_binding *_def_bind_ref, cpu_zone_binding_t *_bind_zone, bool _start) : cthd_cdev(_id, ""), def_bind_ref(_def_bind_ref), bind_zone( _bind_zone), start(_start) { } void set_curr_state(int state, int arg); int get_max_state(); int update(); int get_curr_state(); }; int cthd_gating_cdev::get_curr_state() { return curr_state; } void cthd_gating_cdev::set_curr_state(int state, int arg) { if (!start && state) { if (!bind_zone->zone) return; thd_log_info("CPU def binding exit for %s\n", bind_zone->zone->get_zone_type().c_str()); bind_zone->zone->zone_reset(); cpu_zone_stat_t stats; if (def_bind_ref->read_zone_stat(bind_zone->zone->get_zone_type(), &stats) == THD_SUCCESS) { ++stats.failures; def_bind_ref->update_zone_stat(bind_zone->zone->get_zone_type(), stats.failures); if (stats.failures > 3) { // This zone can't be controlled by this binding thd_log_info("CPU def binding is set to inactive for %s\n", bind_zone->zone->get_zone_type().c_str()); bind_zone->zone->set_zone_inactive(); } } else { def_bind_ref->update_zone_stat(bind_zone->zone->get_zone_type(), 0); } } else if (start && state) { if (def_bind_ref->check_cpu_load()) { thd_log_info("Turn on the gate \n"); curr_state = max_state; } else { thd_log_info("Not CPU specific increase \n"); curr_state = 0; } } else { curr_state = 0; } thd_log_info( "cthd_gating_cdev::set_curr_state[start:%d state:%d. curr_state:%d\n", start, state, curr_state); } int cthd_gating_cdev::get_max_state() { return max_state; } int cthd_gating_cdev::update() { thd_log_info("cthd_gating_cdev::update\n"); return 0; } int cthd_cpu_default_binding::read_zone_stat(std::string zone_name, cpu_zone_stat_t *stat) { std::ifstream filein; std::stringstream filename; filename << TDRUNDIR << "/" << "cpu_def_zone_bind.out"; filein.open(filename.str().c_str(), std::ios::in | std::ios::binary); if (!filein) return THD_ERROR; while (filein) { filein.read((char *) stat, sizeof(*stat)); thd_log_info("read_zone_stat name:%s f:%d\n", stat->zone_name, stat->failures); if (filein && zone_name == stat->zone_name) return THD_SUCCESS; } filein.close(); return THD_ERROR; } void cthd_cpu_default_binding::update_zone_stat(std::string zone_name, int fail_cnt) { std::ifstream filein; std::streampos current = 0; cpu_zone_stat_t obj; bool found = false; std::stringstream filename; filename << TDRUNDIR << "/" << "cpu_def_zone_bind.out"; filein.open(filename.str().c_str(), std::ios::in | std::ios::binary); if (filein.is_open()) { while (!filein.eof()) { current = filein.tellg(); filein.read((char *) &obj, sizeof(obj)); if (filein && zone_name == obj.zone_name) { found = true; break; } } } filein.close(); std::fstream fileout(filename.str().c_str(), std::ios::out | std::ios::in | std::ios::binary); if (!fileout.is_open()) { std::ofstream file; file.open(filename.str().c_str()); if (file.is_open()) { file.close(); fileout.open(filename.str().c_str(), std::ios::in | std::ios::out | std::ios::binary); } else { thd_log_info("Can't create cpu_def_zone_bind.out\n"); } } if (found) { fileout.seekp(current); } else fileout.seekp(0, std::ios::end); strncpy(obj.zone_name, zone_name.c_str(), 50); obj.failures = fail_cnt; fileout.write((char *) &obj, sizeof(obj)); fileout.close(); } bool cthd_cpu_default_binding::check_cpu_load() { unsigned int max_power; unsigned int min_power; unsigned int power; power = thd_engine->rapl_power_meter.rapl_action_get_power(PACKAGE, &max_power, &min_power); if (cpu_package_max_power != 0) max_power = cpu_package_max_power; thd_log_info("cthd_gating_cdev power :%u %u %u\n", power, min_power, max_power); if ((max_power - min_power) < def_starting_power_differential) { return false; } if (power > (max_power * 60 / 100)) { thd_log_info("Significant cpu load \n"); return true; } return false; } bool cthd_cpu_default_binding::blacklist_match(std::string name) { int i = 0; const char *blacklist_zones[] = { "cpu", "acpitz", "Surface", "pkg-temp-0", "x86_pkg_temp", "soc_dts0", "soc_dts1", "B0D4", "B0DB", "" }; while (blacklist_zones[i][0] != '\0') { if (name == blacklist_zones[i]) return true; ++i; } cpu_zone_stat_t stats; if (read_zone_stat(name, &stats) == THD_SUCCESS) { if (stats.failures > 3) { // This zone can't be controlled by this binding thd_log_info(" zone %s in blacklist\n", name.c_str()); return true; } } return false; } void cthd_cpu_default_binding::do_default_binding( std::vector<cthd_cdev *> &cdevs) { int count = 0; int id = 0x1000; cthd_cdev *cdev_rapl; cthd_cdev *cdev_powerclamp; cdev_rapl = thd_engine->search_cdev("rapl_controller"); cdev_powerclamp = thd_engine->search_cdev("intel_powerclamp"); if (!cdev_rapl && !cdev_powerclamp) { thd_log_info( "cthd_cpu_default_binding::do_default_binding: No relevant cpu cdevs\n"); return; } for (unsigned int i = 0; i < thd_engine->get_zone_count(); ++i) { cthd_zone *zone = thd_engine->get_zone(i); if (!zone) { continue; } if (blacklist_match(zone->get_zone_type())) { continue; } if (!zone->zone_cdev_binded()) { cpu_zone_binding_t *cdev_binding_info; cdev_binding_info = new cpu_zone_binding_t; cdev_binding_info->zone_name = zone->get_zone_type(); cdev_binding_info->zone = zone; cdev_binding_info->cdev_gate_entry = new cthd_gating_cdev(id++, this, cdev_binding_info, true); if (cdev_binding_info->cdev_gate_entry) { cdev_binding_info->cdev_gate_entry->set_cdev_type( cdev_binding_info->zone_name + "_" + "cpu_gate_entry"); } else { thd_log_info("do_default_binding failed \n"); cdev_binding_info->cdev_gate_entry = NULL; delete cdev_binding_info; continue; } cdev_binding_info->cdev_gate_exit = new cthd_gating_cdev(id++, this, cdev_binding_info, false); if (cdev_binding_info->cdev_gate_exit) { cdev_binding_info->cdev_gate_exit->set_cdev_type( cdev_binding_info->zone_name + "_" + "cpu_gate_exit"); } else { thd_log_info("do_default_binding failed \n"); delete cdev_binding_info->cdev_gate_entry; cdev_binding_info->cdev_gate_entry = NULL; delete cdev_binding_info; return; } thd_log_info("unbound zone %s\n", zone->get_zone_type().c_str()); int status = zone->bind_cooling_device(PASSIVE, 0, cdev_binding_info->cdev_gate_entry, 0, def_gating_cdev_sampling_period); if (status == THD_ERROR) { thd_log_info("unbound zone: Bind attempt failed\n"); delete cdev_binding_info->cdev_gate_exit; cdev_binding_info->cdev_gate_exit = NULL; delete cdev_binding_info->cdev_gate_entry; cdev_binding_info->cdev_gate_entry = NULL; delete cdev_binding_info; continue; } if (cdev_rapl) { zone->bind_cooling_device(PASSIVE, 0, cdev_rapl, 20); } if (cdev_powerclamp) { zone->bind_cooling_device(PASSIVE, 0, cdev_powerclamp, 20); } status = zone->bind_cooling_device(PASSIVE, 0, cdev_binding_info->cdev_gate_exit, 0, def_gating_cdev_sampling_period); if (status == THD_ERROR) { thd_log_info("unbound zone: Bind attempt failed\n"); delete cdev_binding_info->cdev_gate_exit; cdev_binding_info->cdev_gate_exit = NULL; delete cdev_binding_info->cdev_gate_entry; cdev_binding_info->cdev_gate_entry = NULL; delete cdev_binding_info; continue; } thd_log_info("unbound zone %s\n", zone->get_zone_type().c_str()); count++; zone->set_zone_active(); cdev_list.push_back(cdev_binding_info); } } if (count) { thd_engine->rapl_power_meter.rapl_start_measure_power(); cpu_package_max_power = thd_engine->rapl_power_meter.rapl_action_get_max_power(PACKAGE); thd_log_info("do_default_binding max power CPU package :%u\n", cpu_package_max_power); } } 0707010000003B000081A400000000000000000000000166853FAC0000086D000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_cpu_default_binding.h/* * thd_default_binding.h: Default binding of thermal zones * interface file * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_DEFAULT_BINDING_H_ #define THD_DEFAULT_BINDING_H_ #include "thd_engine.h" #include "thd_cdev.h" typedef struct { char zone_name[50 + 1]; int failures; } cpu_zone_stat_t; typedef struct { std::string zone_name; cthd_cdev *cdev_gate_entry; cthd_cdev *cdev_gate_exit; cthd_zone *zone; } cpu_zone_binding_t; class cthd_cpu_default_binding { private: int thd_read_default_thermal_zones(); unsigned int cpu_package_max_power; bool blacklist_match(std::string name); public: static const int def_gating_cdev_sampling_period = 30; static const unsigned int def_starting_power_differential = 4000000; std::vector<cpu_zone_binding_t*> cdev_list; cthd_cpu_default_binding() : cpu_package_max_power(0) { } ~ cthd_cpu_default_binding() { for (unsigned int i = 0; i < cdev_list.size(); ++i) { cpu_zone_binding_t *cdev_binding_info = cdev_list[i]; delete cdev_binding_info->cdev_gate_exit; delete cdev_binding_info->cdev_gate_entry; delete cdev_binding_info; } cdev_list.clear(); } void do_default_binding(std::vector<cthd_cdev *> &cdevs); bool check_cpu_load(); int read_zone_stat(std::string zone_name, cpu_zone_stat_t *stat); void update_zone_stat(std::string zone_name, int fail_cnt); }; #endif /* THD_DEFAULT_BINDING_H_ */ 0707010000003C000081A400000000000000000000000166853FAC000086FF000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_dbus_interface.cpp/* * thd_dbus_interface.cpp: Thermal Daemon dbus interface * * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_default.h" #include "thd_sensor.h" #include "thd_zone.h" #include "thd_trip_point.h" #include <gio/gio.h> #include <glib.h> #include <glib/gprintf.h> #include <glib-object.h> struct _PrefObject { GObject parent; }; #define PREF_TYPE_OBJECT (pref_object_get_type()) G_DECLARE_FINAL_TYPE(PrefObject, pref_object, PREF, OBJECT, GObject) #define MAX_DBUS_REPLY_STR_LEN 100 G_DEFINE_TYPE(PrefObject, pref_object, G_TYPE_OBJECT) gboolean thd_dbus_interface_terminate(PrefObject *obj, GError **error); gboolean thd_dbus_interface_reinit(PrefObject *obj, GError **error); gboolean thd_dbus_interface_set_current_preference(PrefObject *obj, gchar *pref, GError **error); gboolean thd_dbus_interface_get_current_preference(PrefObject *obj, gchar **pref_out, GError **error); gboolean thd_dbus_interface_set_user_max_temperature(PrefObject *obj, gchar *zone_name, unsigned temperature, GError **error); gboolean thd_dbus_interface_set_user_passive_temperature(PrefObject *obj, gchar *zone_name, unsigned int temperature, GError **error); gboolean thd_dbus_interface_add_sensor(PrefObject *obj, gchar *sensor, gchar *path, GError **error); gboolean thd_dbus_interface_get_sensor_information(PrefObject *obj, gint index, gchar **sensor_out, gchar **path, gint *temp, GError **error); gboolean thd_dbus_interface_add_zone_passive(PrefObject *obj, gchar *zone_name, gint trip_temp, gchar *sensor_name, gchar *cdev_name, GError **error); gboolean thd_dbus_interface_set_zone_status(PrefObject *obj, gchar *zone_name, int status, GError **error); gboolean thd_dbus_interface_get_zone_status(PrefObject *obj, gchar *zone_name, int *status, GError **error); gboolean thd_dbus_interface_delete_zone(PrefObject *obj, gchar *zone_name, GError **error); gboolean thd_dbus_interface_add_virtual_sensor(PrefObject *obj, gchar *name, gchar *dep_sensor, double slope, double intercept, GError **error); gboolean thd_dbus_interface_add_cooling_device(PrefObject *obj, gchar *cdev_name, gchar *path, gint min_state, gint max_state, gint step, GError **error); gboolean thd_dbus_interface_update_cooling_device(PrefObject *obj, gchar *cdev_name, gchar *path, gint min_state, gint max_state, gint step, GError **error); gboolean thd_dbus_interface_get_sensor_count(PrefObject *obj, int *status, GError **error); gboolean thd_dbus_interface_get_sensor_temperature(PrefObject *obj, int index, unsigned int *temperature, GError **error); gboolean thd_dbus_interface_get_zone_count(PrefObject *obj, int *status, GError **error); gboolean thd_dbus_interface_get_zone_information(PrefObject *obj, gint index, gchar **zone_out, gint *sensor_count, gint *trip_count, gint *bound, GError **error); gboolean thd_dbus_interface_get_zone_sensor_at_index(PrefObject *obj, gint zone_index, gint sensor_index, gchar **sensor_out, GError **error); gboolean thd_dbus_interface_get_zone_trip_at_index(PrefObject *obj, gint zone_index, gint trip_index, int *temp, int *trip_type, int *sensor_id, int *cdev_size, GArray **cdev_ids, GError **error); gboolean thd_dbus_interface_get_cdev_count(PrefObject *obj, int *status, GError **error); gboolean thd_dbus_interface_get_cdev_information(PrefObject *obj, gint index, gchar **cdev_out, gint *min_state, gint *max_state, gint *curr_state, GError **error); // To be implemented gboolean thd_dbus_interface_add_trip_point(PrefObject *obj, gchar *name, GError **error) { return FALSE; } gboolean thd_dbus_interface_delete_trip_point(PrefObject *obj, gchar *name, GError **error) { return FALSE; } gboolean thd_dbus_interface_disable_cooling_device(PrefObject *obj, gchar *name, GError **error) { return FALSE; } // This is a generated file, which expects the above prototypes #ifndef GDBUS #include "thd_dbus_interface.h" #endif // DBUS Related functions // Dbus object initialization static void pref_object_init(PrefObject *obj) { g_assert(obj != NULL); } // Dbus object class initialization static void pref_object_class_init(PrefObjectClass *_class) { g_assert(_class != NULL); #ifndef GDBUS dbus_g_object_type_install_info(PREF_TYPE_OBJECT, &dbus_glib_thd_dbus_interface_object_info); #endif } // Callback function called to inform a sent value via dbus gboolean thd_dbus_interface_set_current_preference(PrefObject *obj, gchar *pref, GError **error) { int ret; thd_log_debug("thd_dbus_interface_set_current_preference %s\n", (char*) pref); g_assert(obj != NULL); cthd_preference thd_pref; ret = thd_pref.set_preference((char*) pref); thd_engine->send_message(PREF_CHANGED, 0, NULL); return ret; } // Callback function called to get value via dbus gboolean thd_dbus_interface_get_current_preference(PrefObject *obj, gchar **pref_out, GError **error) { thd_log_debug("thd_dbus_interface_get_current_preference\n"); g_assert(obj != NULL); gchar *value_out; static char *pref_str; pref_str = g_new(char, MAX_DBUS_REPLY_STR_LEN); if (!pref_str) return FALSE; cthd_preference thd_pref; value_out = (gchar*) thd_pref.get_preference_cstr(); if (!value_out) { g_free(pref_str); return FALSE; } strncpy(pref_str, value_out, MAX_DBUS_REPLY_STR_LEN); free(value_out); thd_log_debug("thd_dbus_interface_get_current_preference out :%s\n", pref_str); *pref_out = pref_str; return TRUE; } gboolean (*thd_dbus_exit_callback)(void); gboolean thd_dbus_interface_terminate(PrefObject *obj, GError **error) { thd_engine->thd_engine_terminate(); if (thd_dbus_exit_callback) thd_dbus_exit_callback(); return TRUE; } gboolean thd_dbus_interface_reinit(PrefObject *obj, GError **error) { bool exclusive_control = false; if (thd_engine->get_control_mode() == EXCLUSIVE) exclusive_control = true; std::string config_file = thd_engine->get_config_file(); const char *conf_file = NULL; if (!config_file.empty()) conf_file = config_file.c_str(); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; sleep(2); if (thd_engine_create_default_engine(true, exclusive_control, conf_file) != THD_SUCCESS) { return FALSE; } return TRUE; } gboolean thd_dbus_interface_set_user_max_temperature(PrefObject *obj, gchar *zone_name, unsigned int temperature, GError **error) { thd_log_debug("thd_dbus_interface_set_user_set_point %s:%d\n", zone_name, temperature); g_assert(obj != NULL); int ret = thd_engine->user_set_max_temp(zone_name, temperature); if (ret == THD_SUCCESS) thd_engine->send_message(PREF_CHANGED, 0, NULL); else return FALSE; return TRUE; } gboolean thd_dbus_interface_set_user_passive_temperature(PrefObject *obj, gchar *zone_name, unsigned int temperature, GError **error) { thd_log_debug("thd_dbus_interface_set_user_passive_temperature %s:%u\n", zone_name, temperature); g_assert(obj != NULL); int ret = thd_engine->user_set_psv_temp(zone_name, temperature); if (ret == THD_SUCCESS) thd_engine->send_message(PREF_CHANGED, 0, NULL); else return FALSE; return TRUE; } gboolean thd_dbus_interface_add_sensor(PrefObject *obj, gchar *sensor, gchar *path, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_add_sensor %s:%s\n", (char*) sensor, (char *) path); ret = thd_engine->user_add_sensor(sensor, path); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } // Adjust parameters for the following gboolean thd_dbus_interface_add_virtual_sensor(PrefObject *obj, gchar *name, gchar *dep_sensor, double slope, double intercept, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_add_sensor %s:%s\n", (char*) name, (char *) dep_sensor); ret = thd_engine->user_add_virtual_sensor(name, dep_sensor, slope, intercept); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_get_sensor_information(PrefObject *obj, gint index, gchar **sensor_out, gchar **path, gint *temp, GError **error) { char *sensor_str; char *path_str; thd_log_debug("thd_dbus_interface_get_sensor_information %d\n", index); cthd_sensor *sensor = thd_engine->user_get_sensor(index); if (!sensor) return FALSE; sensor_str = g_new(char, MAX_DBUS_REPLY_STR_LEN + 1); if (!sensor_str) return FALSE; path_str = g_new(char, MAX_DBUS_REPLY_STR_LEN + 1); if (!path_str) { g_free(sensor_str); return FALSE; } strncpy(sensor_str, sensor->get_sensor_type().c_str(), MAX_DBUS_REPLY_STR_LEN); sensor_str[MAX_DBUS_REPLY_STR_LEN] = '\0'; strncpy(path_str, sensor->get_sensor_path().c_str(), MAX_DBUS_REPLY_STR_LEN); path_str[MAX_DBUS_REPLY_STR_LEN] = '\0'; *temp = (gint) sensor->read_temperature(); *sensor_out = sensor_str; *path = path_str; return TRUE; } gboolean thd_dbus_interface_get_sensor_count(PrefObject *obj, int *count, GError **error) { *count = thd_engine->get_sensor_count(); return TRUE; } gboolean thd_dbus_interface_get_zone_count(PrefObject *obj, int *count, GError **error) { *count = thd_engine->get_zone_count(); return TRUE; } gboolean thd_dbus_interface_get_zone_information(PrefObject *obj, gint index, gchar **zone_out, gint *sensor_count, gint *trip_count, gint *bound, GError **error) { char *zone_str; thd_log_debug("thd_dbus_interface_get_zone_information %d\n", index); cthd_zone *zone = thd_engine->user_get_zone(index); if (!zone) return FALSE; zone_str = g_new(char, MAX_DBUS_REPLY_STR_LEN + 1); if (!zone_str) return FALSE; strncpy(zone_str, zone->get_zone_type().c_str(), MAX_DBUS_REPLY_STR_LEN); zone_str[MAX_DBUS_REPLY_STR_LEN] = '\0'; *zone_out = zone_str; *sensor_count = zone->get_sensor_count(); *trip_count = zone->get_trip_count(); *bound = (gint) zone->zone_active_status(); return TRUE; } gboolean thd_dbus_interface_get_zone_sensor_at_index(PrefObject *obj, gint zone_index, gint sensor_index, gchar **sensor_out, GError **error) { char *sensor_str; thd_log_debug("thd_dbus_interface_get_zone_sensor_at_index %d\n", zone_index); cthd_zone *zone = thd_engine->user_get_zone(zone_index); if (!zone) return FALSE; cthd_sensor *sensor = zone->get_sensor_at_index(sensor_index); if (!sensor) return FALSE; sensor_str = g_new(char, MAX_DBUS_REPLY_STR_LEN + 1); if (!sensor_str) return FALSE; strncpy(sensor_str, sensor->get_sensor_type().c_str(), MAX_DBUS_REPLY_STR_LEN); sensor_str[MAX_DBUS_REPLY_STR_LEN] = '\0'; *sensor_out = sensor_str; return TRUE; } gboolean thd_dbus_interface_get_zone_trip_at_index(PrefObject *obj, gint zone_index, gint trip_index, int *temp, int *trip_type, int *sensor_id, int *cdev_size, GArray **cdev_ids, GError **error) { thd_log_debug("thd_dbus_interface_get_zone_sensor_at_index %d\n", zone_index); cthd_zone *zone = thd_engine->user_get_zone(zone_index); if (!zone) return FALSE; cthd_trip_point *trip = zone->get_trip_at_index(trip_index); if (!trip) return FALSE; *temp = trip->get_trip_temp(); *trip_type = trip->get_trip_type(); *sensor_id = trip->get_sensor_id(); *cdev_size = trip->get_cdev_count(); GArray *garray; garray = g_array_new(FALSE, FALSE, sizeof(gint)); for (int i = 0; i < *cdev_size; i++) { trip_pt_cdev_t cdev_trip; int index; cdev_trip = trip->get_cdev_at_index(i); index = cdev_trip.cdev->thd_cdev_get_index(); g_array_prepend_val(garray, index); } *cdev_ids = garray; return TRUE; } gboolean thd_dbus_interface_get_cdev_count(PrefObject *obj, int *count, GError **error) { *count = thd_engine->get_cdev_count(); return TRUE; } gboolean thd_dbus_interface_get_cdev_information(PrefObject *obj, gint index, gchar **cdev_out, gint *min_state, gint *max_state, gint *curr_state, GError **error) { char *cdev_str; thd_log_debug("thd_dbus_interface_get_cdev_information %d\n", index); cthd_cdev *cdev = thd_engine->user_get_cdev(index); if (!cdev) return FALSE; cdev_str = g_new(char, MAX_DBUS_REPLY_STR_LEN + 1); if (!cdev_str) return FALSE; strncpy(cdev_str, cdev->get_cdev_type().c_str(), MAX_DBUS_REPLY_STR_LEN); cdev_str[MAX_DBUS_REPLY_STR_LEN] = '\0'; *cdev_out = cdev_str; *min_state = cdev->get_min_state(); *max_state = cdev->get_max_state(); *curr_state = cdev->get_curr_state(); return TRUE; } gboolean thd_dbus_interface_add_zone_passive(PrefObject *obj, gchar *zone_name, gint trip_temp, gchar *sensor_name, gchar *cdev_name, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_add_zone_passive %s\n", (char*) zone_name); ret = thd_engine->user_add_zone(zone_name, trip_temp, sensor_name, cdev_name); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_set_zone_status(PrefObject *obj, gchar *zone_name, int status, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_set_zone_status %s\n", (char*) zone_name); ret = thd_engine->user_set_zone_status(zone_name, status); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_get_zone_status(PrefObject *obj, gchar *zone_name, int *status, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_set_zone_status %s\n", (char*) zone_name); ret = thd_engine->user_get_zone_status(zone_name, status); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_delete_zone(PrefObject *obj, gchar *zone_name, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_delete_zone %s\n", (char*) zone_name); ret = thd_engine->user_delete_zone(zone_name); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_add_cooling_device(PrefObject *obj, gchar *cdev_name, gchar *path, gint min_state, gint max_state, gint step, GError **error) { int ret; g_assert(obj != NULL); thd_log_debug("thd_dbus_interface_add_cooling_device %s\n", (char*) cdev_name); // Using a device in /etc is a security issue if ((strlen(path) >= strlen("/etc")) && !strncmp(path, "/etc", strlen("/etc"))) return FALSE; ret = thd_engine->user_add_cdev(cdev_name, path, min_state, max_state, step); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } gboolean thd_dbus_interface_update_cooling_device(PrefObject *obj, gchar *cdev_name, gchar *path, gint min_state, gint max_state, gint step, GError **error) { g_assert(obj != NULL); // Using a device in /etc is a security issue if ((strlen(path) >= strlen("/etc")) && !strncmp(path, "/etc", strlen("/etc"))) return FALSE; return thd_dbus_interface_add_cooling_device(obj, cdev_name, path, min_state, max_state, step, error); } gboolean thd_dbus_interface_get_sensor_temperature(PrefObject *obj, int index, unsigned int *temperature, GError **error) { int ret; ret = thd_engine->get_sensor_temperature(index, temperature); if (ret == THD_SUCCESS) return TRUE; else return FALSE; } #ifdef GDBUS #pragma GCC diagnostic push static GDBusInterfaceVTable interface_vtable; extern gint own_id; static GDBusNodeInfo * thd_dbus_load_introspection(const gchar *filename, GError **error) { g_autoptr(GBytes) data = NULL; g_autofree gchar *path = NULL; path = g_build_filename("/org/freedesktop/thermald", filename, NULL); data = g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, error); if (data == NULL) return NULL; return g_dbus_node_info_new_for_xml((gchar *)g_bytes_get_data(data, NULL), error); } static void thd_dbus_handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { PrefObject *obj = PREF_OBJECT(user_data); g_autoptr(GError) error = NULL; thd_log_debug("Dbus method called %s %s.\n", interface_name, method_name); if (g_strcmp0(method_name, "AddCoolingDevice") == 0) { g_autofree gchar *cdev_name = NULL; g_autofree gchar *path = NULL; gint min_state; gint max_state; gint step; g_variant_get(parameters, "(ssiii)", &cdev_name, &path, &min_state, &max_state, &step); thd_dbus_interface_add_cooling_device(obj, cdev_name, path, min_state, max_state, step, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "AddSensor") == 0) { g_autofree gchar *sensor_name = NULL; g_autofree gchar *path = NULL; g_variant_get(parameters, "(ss)", &sensor_name, &path); thd_dbus_interface_add_sensor(obj, sensor_name, path, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "AddTripPoint") == 0) { g_autofree gchar *zone_name = NULL; guint trip_point_temp; g_autofree gchar *trip_point_sensor = NULL; g_autofree gchar *trip_point_cdev = NULL; g_variant_get(parameters, "(suss)", &zone_name, &trip_point_temp, &trip_point_sensor, &trip_point_cdev); thd_dbus_interface_add_trip_point(obj, zone_name, &error); if(error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "AddVirtualSensor") == 0) { g_autofree gchar *sensor_name = NULL; g_autofree gchar *dep_sensor = NULL; gdouble slope; gdouble intercept; g_variant_get(parameters, "(ssdd)", &sensor_name, &dep_sensor, &slope, &intercept); thd_dbus_interface_add_virtual_sensor(obj, sensor_name, dep_sensor, slope, intercept, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "AddZonePassive") == 0) { g_autofree gchar *zone_name = NULL; gint trip_point_temp; g_autofree gchar *trip_point_sensor = NULL; g_autofree gchar *trip_point_cdev = NULL; g_variant_get(parameters, "(siss)", &zone_name, &trip_point_temp, &trip_point_sensor, &trip_point_cdev); thd_dbus_interface_add_zone_passive(obj, zone_name, trip_point_temp, trip_point_sensor, trip_point_cdev, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "DeleteTripPoint") == 0) { g_autofree gchar *zone_name = NULL; guint trip_point_temp; g_variant_get(parameters, "(su)", &zone_name, &trip_point_temp); thd_dbus_interface_delete_trip_point(obj, zone_name, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "DeleteZone") == 0) { g_autofree gchar *zone_name = NULL; g_variant_get(parameters, "(&s)", &zone_name); thd_dbus_interface_delete_zone(obj, zone_name, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "DisableCoolingDevice") == 0) { g_autofree gchar *cdev_name = NULL; g_variant_get(parameters, "(s)", &cdev_name); thd_dbus_interface_disable_cooling_device(obj, cdev_name, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "GetCdevCount") == 0) { gint count; thd_dbus_interface_get_cdev_count(obj, &count, &error); if (error) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value (invocation, g_variant_new("(u)", count)); return; } if (g_strcmp0(method_name, "GetCdevInformation") == 0) { gint ret; gint index; g_autofree gchar *cdev_out = NULL; gint min_state; gint max_state; gint curr_state; g_variant_get(parameters, "(u)", &index); ret = thd_dbus_interface_get_cdev_information(obj, index, &cdev_out, &min_state, &max_state, &curr_state, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(siii)", cdev_out, min_state, max_state, curr_state)); return; } if (g_strcmp0(method_name, "GetCurrentPreference") == 0) { gboolean ret; g_autofree gchar *cur_pref = NULL; ret = thd_dbus_interface_get_current_preference(obj, &cur_pref, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", cur_pref)); return; } if (g_strcmp0(method_name, "GetSensorCount") == 0) { gboolean ret; gint count; ret = thd_dbus_interface_get_sensor_count(obj, &count, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", count)); return; } if (g_strcmp0(method_name, "GetSensorInformation") == 0) { gboolean ret; gint index; g_autofree gchar *sensor_out = NULL; g_autofree gchar *path = NULL; gint temp; g_variant_get(parameters, "(u)", &index); ret = thd_dbus_interface_get_sensor_information(obj, index, &sensor_out, &path, &temp, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(ssi)", sensor_out, path, temp)); return; } if (g_strcmp0(method_name, "GetSensorTemperature") == 0) { gboolean ret; gint index; guint temperature; g_variant_get(parameters, "(u)", &index); ret = thd_dbus_interface_get_sensor_temperature(obj, index, &temperature, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", temperature)); return; } if (g_strcmp0(method_name, "GetZoneCount") == 0) { gboolean ret; gint count; ret = thd_dbus_interface_get_zone_count(obj, &count, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", count)); return; } if (g_strcmp0(method_name, "GetZoneInformation") == 0) { gboolean ret; gint index; g_autofree gchar *zone_out = NULL; gint sensor_count; gint trip_count; gint bound; g_variant_get(parameters, "(u)", &index); ret = thd_dbus_interface_get_zone_information(obj, index, &zone_out, &sensor_count, &trip_count, &bound, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(siii)", zone_out, sensor_count, trip_count, bound)); return; } if (g_strcmp0(method_name, "GetZoneSensorAtIndex") == 0) { gboolean ret; gint zone_index; gint sensor_index; g_autofree gchar *sensor_out = NULL; g_variant_get(parameters, "(uu)", &zone_index, &sensor_index); ret = thd_dbus_interface_get_zone_sensor_at_index(obj, zone_index, sensor_index, &sensor_out, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", sensor_out)); return; } if (g_strcmp0(method_name, "GetZoneStatus") == 0) { gboolean ret; g_autofree gchar *zone_name = NULL; gint status; g_variant_get(parameters, "(s)", &zone_name); ret = thd_dbus_interface_get_zone_status(obj, zone_name, &status, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", status)); return; } if (g_strcmp0(method_name, "GetZoneTripAtIndex") == 0) { gboolean ret; gint zone_index; gint trip_index; gint temp; gint trip_type; gint sensor_id; gint cdev_size; g_autoptr(GArray) cdev_ids = NULL; g_autoptr(GVariantBuilder) builder = NULL; GVariant **tmp; GVariant *array = NULL; g_variant_get(parameters, "(uu)", &zone_index, &trip_index); ret = thd_dbus_interface_get_zone_trip_at_index(obj, zone_index, trip_index, &temp, &trip_type, &sensor_id, &cdev_size, &cdev_ids, &error); if (error || !ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } builder = g_variant_builder_new(G_VARIANT_TYPE("(iiiiai)")); g_variant_builder_add_value(builder, g_variant_new_int32(temp)); g_variant_builder_add_value(builder, g_variant_new_int32(trip_type)); g_variant_builder_add_value(builder, g_variant_new_int32(sensor_id)); g_variant_builder_add_value(builder, g_variant_new_int32(cdev_size)); tmp = (GVariant **) g_malloc0(cdev_size * sizeof(GVariant *)); for (int i = 0; i < cdev_size; i++) { tmp[i] = g_variant_new_int32(g_array_index(cdev_ids, gint, i)); } array = g_variant_new_array(G_VARIANT_TYPE_INT32, tmp, cdev_size); g_variant_builder_add_value(builder, array); g_dbus_method_invocation_return_value(invocation, g_variant_builder_end(builder)); g_free(tmp); return; } if (g_strcmp0(method_name, "Reinit") == 0) { thd_dbus_interface_reinit(obj, &error); g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "SetCurrentPreference") == 0) { gboolean ret; g_autofree gchar *pref = NULL; g_variant_get(parameters, "(s)", &pref); ret = thd_dbus_interface_set_current_preference(obj, pref, &error); if (!ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "SetUserMaxTemperature") == 0) { gboolean ret; g_autofree gchar *zone_name = NULL; guint user_set_point_in_milli_degree_celsius; g_variant_get(parameters, "(su)", &zone_name, &user_set_point_in_milli_degree_celsius); ret = thd_dbus_interface_set_user_max_temperature(obj, zone_name, user_set_point_in_milli_degree_celsius, &error); if (!ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } return; } if (g_strcmp0(method_name, "SetUserPassiveTemperature") == 0) { gboolean ret; g_autofree gchar *zone_name = NULL; guint user_set_point_in_milli_degree_celsius; g_variant_get(parameters, "(su)", &zone_name, &user_set_point_in_milli_degree_celsius); ret = thd_dbus_interface_set_user_passive_temperature(obj, zone_name, user_set_point_in_milli_degree_celsius, &error); if (!ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "SetZoneStatus") == 0) { gboolean ret; g_autofree gchar *zone_name = NULL; gint status; g_variant_get(parameters, "(si)", &zone_name, &status); ret = thd_dbus_interface_set_zone_status(obj, zone_name, status, &error); if (!ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } if (g_strcmp0(method_name, "Terminate") == 0) { g_dbus_method_invocation_return_value(invocation, NULL); thd_dbus_interface_terminate(obj, &error); return; } if (g_strcmp0(method_name, "UpdateCoolingDevice") == 0) { gboolean ret; g_autofree gchar *cdev_name = NULL; g_autofree gchar *path = NULL; gint min_state; gint max_state; gint step; g_variant_get(parameters, "(ssiii)", &cdev_name, &path, &min_state, &max_state, &step); ret = thd_dbus_interface_update_cooling_device(obj, cdev_name, path, min_state, max_state, step, &error); if (!ret) { g_dbus_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); return; } g_set_error(&error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "no such method %s", method_name); g_dbus_method_invocation_return_gerror(invocation, error); } static GVariant * thd_dbus_handle_get_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { return NULL; } static gboolean thd_dbus_handle_set_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data) { return TRUE; } static void thd_dbus_on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { guint registration_id; GDBusProxy *proxy_id = NULL; GError *error = NULL; GDBusNodeInfo *introspection_data = NULL; if (user_data == NULL) { thd_log_error("user_data is NULL\n"); return; } introspection_data = thd_dbus_load_introspection("src/thd_dbus_interface.xml", &error); if (error != NULL) { thd_log_error("Couldn't create introspection data: %s:\n", error->message); return; } registration_id = g_dbus_connection_register_object(connection, "/org/freedesktop/thermald", introspection_data->interfaces[0], &interface_vtable, user_data, NULL, &error); proxy_id = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", NULL, &error); g_assert(registration_id > 0); g_assert(proxy_id != NULL); } static void thd_dbus_on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { } static void thd_dbus_on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data) { g_warning("Lost the name %s\n", name); exit(1); } // Set up Dbus server with GDBus int thd_dbus_server_init(gboolean (*exit_handler)(void)) { PrefObject *value_obj; value_obj = PREF_OBJECT(g_object_new(PREF_TYPE_OBJECT, NULL)); if (value_obj == NULL) { thd_log_error("Failed to create one Value instance:\n"); return THD_FATAL_ERROR; } thd_dbus_exit_callback = exit_handler; interface_vtable.method_call = thd_dbus_handle_method_call; interface_vtable.get_property = thd_dbus_handle_get_property; interface_vtable.set_property = thd_dbus_handle_set_property; own_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, "org.freedesktop.thermald", G_BUS_NAME_OWNER_FLAGS_REPLACE, thd_dbus_on_bus_acquired, thd_dbus_on_name_acquired, thd_dbus_on_name_lost, g_object_ref(value_obj), NULL); return THD_SUCCESS; } #pragma GCC diagnostic pop #else // Setup dbus server int thd_dbus_server_init(gboolean (*exit_handler)(void)) { DBusGConnection *bus; DBusGProxy *bus_proxy; GError *error = NULL; guint result; PrefObject *value_obj; thd_dbus_exit_callback = exit_handler; bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (error != NULL) { thd_log_error("Couldn't connect to session bus: %s:\n", error->message); return THD_FATAL_ERROR; } // Get a bus proxy instance bus_proxy = dbus_g_proxy_new_for_name(bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); if (bus_proxy == NULL) { thd_log_error("Failed to get a proxy for D-Bus:\n"); return THD_FATAL_ERROR; } thd_log_debug("Registering the well-known name (%s)\n", THD_SERVICE_NAME); // register the well-known name if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error, G_TYPE_STRING, THD_SERVICE_NAME, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID)) { thd_log_error("D-Bus.RequestName RPC failed: %s\n", error->message); return THD_FATAL_ERROR; } thd_log_debug("RequestName returned %d.\n", result); if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { thd_log_error("Failed to get the primary well-known name:\n"); return THD_FATAL_ERROR; } value_obj = (PrefObject*) g_object_new(PREF_TYPE_OBJECT, NULL); if (value_obj == NULL) { thd_log_error("Failed to create one Value instance:\n"); return THD_FATAL_ERROR; } thd_log_debug("Registering it on the D-Bus.\n"); dbus_g_connection_register_g_object(bus, THD_SERVICE_OBJECT_PATH, G_OBJECT(value_obj)); return THD_SUCCESS; } #endif 0707010000003D000081A400000000000000000000000166853FAC0000177C000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_dbus_interface.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN" "http://standards.freedesktop.org/dbus/1.0/introspect.dtd"> <node> <interface name="org.freedesktop.thermald"> <!-- SetCurrentPreference: @new_pref: a string representing the selected preference The new preference can be one of: <itemizedlist> <listitem> <para> PERFORMANCE: performance driven policy (ACPI: ACTIVE) </para> </listitem> <listitem> <para> ENERGY_CONSERVE: Energy conservation "Quiet" mode (ACPI: PASSIVE) </para> </listitem> <listitem> <para> DISABLE: Disable thermal daemon </para> </listitem> </itemizedlist> --> <method name="SetCurrentPreference"> <arg type="s" name="new_pref" direction="in"/> </method> <method name="SetUserMaxTemperature"> <arg type="s" name="zone_name" direction="in"/> <arg type="u" name="user_set_point_in_milli_degree_celsius" direction="in"/> </method> <method name="SetUserPassiveTemperature"> <arg type="s" name="zone_name" direction="in"/> <arg type="u" name="user_passive_control_in_milli_degree_celsius" direction="in"/> </method> <!-- GetCurrentPreference: Returns the current preference --> <method name="GetCurrentPreference"> <arg type="s" name="cur_pref" direction="out"/> </method> <method name="AddSensor"> <arg type="s" name="sensor_name" direction="in"/> <arg type="s" name="path" direction="in"/> </method> <method name="AddVirtualSensor"> <arg type="s" name="sensor_name" direction="in"/> <arg type="s" name="dep_sensor_name" direction="in"/> <arg type="d" name="slope" direction="in"/> <arg type="d" name="intercept" direction="in"/> </method> <method name="GetSensorCount"> <arg type="u" name="count" direction="out"/> </method> <method name="GetSensorInformation"> <arg type="u" name="index" direction="in"/> <arg type="s" name="sensor" direction="out"/> <arg type="s" name="path" direction="out"/> <arg type="i" name="temp" direction="out"/> </method> <method name="GetSensorTemperature"> <arg type="u" name="index" direction="in"/> <arg type="u" name="temperature" direction="out"/> </method> <method name="GetZoneCount"> <arg type="u" name="count" direction="out"/> </method> <method name="GetZoneInformation"> <arg type="u" name="index" direction="in"/> <arg type="s" name="zone" direction="out"/> <arg type="i" name="sensor_count" direction="out"/> <arg type="i" name="trip_count" direction="out"/> <arg type="i" name="bound" direction="out"/> </method> <method name="GetZoneSensorAtIndex"> <arg type="u" name="zone_index" direction="in"/> <arg type="u" name="sensor_index" direction="in"/> <arg type="s" name="sensor_type" direction="out"/> </method> <method name="GetZoneTripAtIndex"> <arg type="u" name="zone_index" direction="in"/> <arg type="u" name="trip_index" direction="in"/> <arg type="i" name="temp" direction="out"/> <arg type="i" name="trip_type" direction="out"/> <arg type="i" name="sensor_id" direction="out"/> <arg type="i" name="cdev_size" direction="out"/> <arg type="ai" name="cdev_ids" direction="out"/> </method> <method name="GetCdevCount"> <arg type="u" name="count" direction="out"/> </method> <method name="GetCdevInformation"> <arg type="u" name="index" direction="in"/> <arg type="s" name="type" direction="out"/> <arg type="i" name="min_state" direction="out"/> <arg type="i" name="max_state" direction="out"/> <arg type="i" name="current_state" direction="out"/> </method> <method name="AddZonePassive"> <arg type="s" name="zone_name" direction="in"/> <arg type="u" name="trip_point_temp" direction="in"/> <arg type="s" name="trip_point_sensor" direction="in"/> <arg type="s" name="trip_point_cdev" direction="in"/> </method> <method name="DeleteZone"> <arg type="s" name="zone_name" direction="in"/> </method> <method name="SetZoneStatus"> <arg type="s" name="zone_name" direction="in"/> <arg type="i" name="enable" direction="in"/> </method> <method name="GetZoneStatus"> <arg type="s" name="zone_name" direction="in"/> <arg type="i" name="enable" direction="out"/> </method> <method name="AddTripPoint"> <arg type="s" name="zone_name" direction="in"/> <arg type="u" name="trip_point_temp" direction="in"/> <arg type="s" name="trip_point_sensor" direction="in"/> <arg type="s" name="trip_point_cdev" direction="in"/> </method> <method name="DeleteTripPoint"> <arg type="s" name="zone_name" direction="in"/> <arg type="u" name="trip_point_temp" direction="in"/> </method> <method name="AddCoolingDevice"> <arg type="s" name="cdev_name" direction="in"/> <arg type="s" name="optional_path" direction="in"/> <arg type="i" name="min_state" direction="in"/> <arg type="i" name="max_state" direction="in"/> <arg type="i" name="inc_step" direction="in"/> </method> <method name="UpdateCoolingDevice"> <arg type="s" name="cdev_name" direction="in"/> <arg type="s" name="optional_path" direction="in"/> <arg type="i" name="min_state" direction="in"/> <arg type="i" name="max_state" direction="in"/> <arg type="i" name="inc_step" direction="in"/> </method> <method name="DisableCoolingDevice"> <arg type="s" name="cdev_name" direction="in"/> </method> <method name="Reinit"> </method> <method name="Terminate"> </method> </interface> </node> 0707010000003E000081A400000000000000000000000166853FAC0000815D000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine.cpp/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* This class acts as the parent class of all thermal engines. Main functions are: * - Initialization * - Read cooling devices and thermal zones(sensors), which can be overridden in child * - Starts a poll loop, All the thermal processing happens in this thread's context * - Message processing loop * - If either a poll interval is expired or notified via netlink, it schedules a * a change notification on the associated cthd_zone to read and process. */ #include <dirent.h> #include <errno.h> #include <sys/types.h> #include <sys/utsname.h> #include <cpuid.h> #include <locale> #include "thd_engine.h" #include "thd_cdev_therm_sys_fs.h" #include "thd_zone_therm_sys_fs.h" #include "thd_zone_dynamic.h" #include "thd_cdev_gen_sysfs.h" #include "thd_int3400.h" static void *cthd_engine_thread(void *arg); cthd_engine::cthd_engine(std::string _uuid) : current_cdev_index(0), current_zone_index(0), current_sensor_index(0), parse_thermal_zone_success( false), parse_thermal_cdev_success(false), uuid(_uuid), parser_disabled( false), adaptive_mode(false), poll_timeout_msec(-1), wakeup_fd( -1), uevent_fd(-1), control_mode(COMPLEMENTRY), write_pipe_fd( 0), preference(0), status(true), thz_last_uevent_time(0), thz_last_temp_ind_time( 0), thz_last_update_event_time(0), terminate(false), genuine_intel( 0), has_invariant_tsc(0), has_aperf(0), proc_list_matched( false), poll_interval_sec(0), poll_sensor_mask(0), fast_poll_sensor_mask( 0), saved_poll_interval(0), poll_fd_cnt(0), rt_kernel(false), parser_init_done( false) { thd_engine = pthread_t(); thd_attr = pthread_attr_t(); pthread_mutex_init(&thd_engine_mutex, NULL); memset(poll_fds, 0, sizeof(poll_fds)); memset(last_cpu_update, 0, sizeof(last_cpu_update)); } cthd_engine::~cthd_engine() { unsigned int i; if (parser_init_done) parser.parser_deinit(); for (i = 0; i < sensors.size(); ++i) { delete sensors[i]; } sensors.clear(); for (i = 0; i < zones.size(); ++i) { delete zones[i]; } zones.clear(); for (i = 0; i < cdevs.size(); ++i) { delete cdevs[i]; } cdevs.clear(); } void cthd_engine::thd_engine_thread() { unsigned int i; int n; time_t tm; int poll_timeout_sec = poll_timeout_msec / 1000; thd_log_info("thd_engine_thread begin\n"); for (;;) { if (terminate) break; n = poll(poll_fds, poll_fd_cnt, poll_timeout_msec); thd_log_debug("poll exit %d polls_fd event %d %d\n", n, poll_fds[0].revents, poll_fds[1].revents); if (n < 0) { thd_log_warn("Write to pipe failed \n"); continue; } time(&tm); rapl_power_meter.rapl_measure_power(); if (n == 0 || (tm - thz_last_temp_ind_time) >= poll_timeout_sec) { if (!status) { thd_log_msg("Thermal Daemon is disabled \n"); continue; } pthread_mutex_lock(&thd_engine_mutex); // Polling mode enabled. Trigger a temp change message for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->zone_temperature_notification(0, 0); } pthread_mutex_unlock(&thd_engine_mutex); thz_last_temp_ind_time = tm; } if (uevent_fd >= 0 && (poll_fds[uevent_fd].revents & POLLIN)) { // Kobj uevent if (kobj_uevent.check_for_event()) { time_t tm; time(&tm); thd_log_debug("kobj uevent for thermal\n"); if ((tm - thz_last_uevent_time) >= thz_notify_debounce_interval) { pthread_mutex_lock(&thd_engine_mutex); for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->zone_temperature_notification(0, 0); } pthread_mutex_unlock(&thd_engine_mutex); } else { thd_log_debug("IGNORE THZ kevent\n"); } thz_last_uevent_time = tm; } } if (wakeup_fd >= 0 && (poll_fds[wakeup_fd].revents & POLLIN)) { message_capsul_t msg; thd_log_debug("wakeup fd event\n"); int result = read(poll_fds[wakeup_fd].fd, &msg, sizeof(message_capsul_t)); if (result < 0) { thd_log_warn("read on wakeup fd failed\n"); poll_fds[wakeup_fd].revents = 0; continue; } if (proc_message(&msg) < 0) { thd_log_debug("Terminating thread..\n"); } } if ((tm - thz_last_update_event_time) >= thd_poll_interval) { pthread_mutex_lock(&thd_engine_mutex); update_engine_state(); pthread_mutex_unlock(&thd_engine_mutex); thz_last_update_event_time = tm; } workarounds(); } thd_log_debug("thd_engine_thread_end\n"); } bool cthd_engine::set_preference(const int pref) { return true; } int cthd_engine::thd_engine_init(bool ignore_cpuid_check, bool adaptive) { int ret; adaptive_mode = adaptive; if (ignore_cpuid_check) { thd_log_debug("Ignore CPU ID check for MSRs \n"); proc_list_matched = true; } else { check_cpu_id(); if (!proc_list_matched) { if ((parser_init() == THD_SUCCESS) && parser.platform_matched()) { thd_log_msg("Unsupported cpu model, using thermal-conf.xml only \n"); } else { thd_log_msg("Unsupported cpu model, use thermal-conf.xml file or run with --ignore-cpuid-check \n"); return THD_ERROR; } } } ret = read_thermal_sensors(); if (ret != THD_SUCCESS) { thd_log_error("Thermal sysfs Error in reading sensors\n"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } ret = read_cooling_devices(); if (ret != THD_SUCCESS) { thd_log_error("Thermal sysfs Error in reading cooling devs\n"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } ret = read_thermal_zones(); if (ret != THD_SUCCESS) { thd_log_error("No thermal sensors found\n"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } return THD_SUCCESS; } int cthd_engine::thd_engine_start() { int ret; int wake_fds[2]; check_for_rt_kernel(); // Pipe is used for communication between two processes ret = pipe(wake_fds); if (ret) { thd_log_error("Thermal sysfs: pipe creation failed %d:\n", ret); return THD_FATAL_ERROR; } if (fcntl(wake_fds[0], F_SETFL, O_NONBLOCK) < 0) { thd_log_error("Cannot set non-blocking on pipe: %s\n", strerror(errno)); return THD_FATAL_ERROR; } if (fcntl(wake_fds[1], F_SETFL, O_NONBLOCK) < 0) { thd_log_error("Cannot set non-blocking on pipe: %s\n", strerror(errno)); return THD_FATAL_ERROR; } write_pipe_fd = wake_fds[1]; memset(poll_fds, 0, sizeof(poll_fds)); wakeup_fd = poll_fd_cnt; poll_fds[wakeup_fd].fd = wake_fds[0]; poll_fds[wakeup_fd].events = POLLIN; poll_fds[wakeup_fd].revents = 0; poll_fd_cnt++; poll_timeout_msec = -1; if (poll_interval_sec) { thd_log_msg("Polling mode is enabled: %d\n", poll_interval_sec); poll_timeout_msec = poll_interval_sec * 1000; } if (parser.platform_matched()) { parser.set_default_preference(); int poll_secs = parser.get_polling_interval(); if (poll_secs) { thd_log_info("Poll interval is defined in XML config %d seconds\n", poll_secs); poll_interval_sec = poll_secs; poll_timeout_msec = poll_secs * 1000; } } // Check if polling is disabled and sensors don't support // async mode, in that enable force polling if (!poll_interval_sec) { unsigned int i; for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; if (!zone->zone_active_status()) continue; if (!zone->check_sensor_async_status()) { thd_log_msg( "Polling will be enabled as some sensors are not capable to notify asynchronously\n"); poll_timeout_msec = def_poll_interval; break; } } if (i == zones.size()) { thd_log_info("Proceed without polling mode!\n"); } uevent_fd = poll_fd_cnt; poll_fds[uevent_fd].fd = kobj_uevent.kobj_uevent_open(); if (poll_fds[uevent_fd].fd < 0) { thd_log_warn("Invalid kobj_uevent handle\n"); uevent_fd = -1; goto skip_kobj; } thd_log_info("FD = %d\n", poll_fds[uevent_fd].fd); kobj_uevent.register_dev_path( (char *) "/devices/virtual/thermal/thermal_zone"); poll_fds[uevent_fd].events = POLLIN; poll_fds[uevent_fd].revents = 0; poll_fd_cnt++; } skip_kobj: #ifndef DISABLE_PTHREAD // Create thread pthread_attr_init(&thd_attr); pthread_attr_setdetachstate(&thd_attr, PTHREAD_CREATE_DETACHED); ret = pthread_create(&thd_engine, &thd_attr, cthd_engine_thread, (void*) this); #else { pid_t childpid; if((childpid = fork()) == - 1) { perror("fork"); exit(EXIT_FAILURE); } if(childpid == 0) { /* Child process closes up input side of pipe */ close(wake_fds[1]); cthd_engine_thread((void*)this); } else { /* Parent process closes up output side of pipe */ close(wake_fds[0]); } } #endif thd_pref.refresh(); preference = thd_pref.get_preference(); thd_log_info("Current user preference is %d\n", preference); if (control_mode == EXCLUSIVE) { thd_log_info("Control is taken over from kernel\n"); takeover_thermal_control(); } return ret; } int cthd_engine::thd_engine_stop() { return THD_SUCCESS; } static void *cthd_engine_thread(void *arg) { cthd_engine *obj = (cthd_engine*) arg; obj->thd_engine_thread(); return NULL; } void cthd_engine::send_message(message_name_t msg_id, int size, unsigned char *msg) { message_capsul_t msg_cap; memset(&msg_cap, 0, sizeof(message_capsul_t)); msg_cap.msg_id = msg_id; msg_cap.msg_size = (size > MAX_MSG_SIZE) ? MAX_MSG_SIZE : size; if (msg) memcpy(msg_cap.msg, msg, msg_cap.msg_size); int result = write(write_pipe_fd, &msg_cap, sizeof(message_capsul_t)); if (result < 0) thd_log_warn("Write to pipe failed \n"); } void cthd_engine::process_pref_change() { int new_pref; thd_pref.refresh(); new_pref = thd_pref.get_preference(); if (new_pref == PREF_DISABLED) { status = false; return; } status = true; if (preference != new_pref) { thd_log_msg("Preference changed \n"); } preference = new_pref; for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->update_zone_preference(); } if (control_mode == EXCLUSIVE) { thd_log_info("Control is taken over from kernel\n"); takeover_thermal_control(); } } void cthd_engine::thd_engine_terminate() { send_message(TERMINATE, 0, NULL); sleep(1); process_terminate(); } int cthd_engine::thd_engine_set_user_max_temp(const char *zone_type, const char *user_set_point) { std::string str(user_set_point); cthd_zone *zone; thd_log_debug("thd_engine_set_user_set_point %s\n", user_set_point); std::locale loc; if (std::isdigit(str[0], loc) == 0) { thd_log_warn("thd_engine_set_user_set_point Invalid set point\n"); return THD_ERROR; } zone = get_zone(zone_type); if (!zone) { thd_log_warn("thd_engine_set_user_set_point Invalid zone\n"); return THD_ERROR; } return zone->update_max_temperature(atoi(user_set_point)); } int cthd_engine::thd_engine_set_user_psv_temp(const char *zone_type, const char *user_set_point) { std::string str(user_set_point); cthd_zone *zone; thd_log_debug("thd_engine_set_user_psv_temp %s\n", user_set_point); std::locale loc; if (std::isdigit(str[0], loc) == 0) { thd_log_warn("thd_engine_set_user_psv_temp Invalid set point\n"); return THD_ERROR; } zone = get_zone(zone_type); if (!zone) { thd_log_warn("thd_engine_set_user_psv_temp Invalid zone\n"); return THD_ERROR; } return zone->update_psv_temperature(atoi(user_set_point)); } void cthd_engine::thermal_zone_change(message_capsul_t *msg) { thermal_zone_notify_t *pmsg = (thermal_zone_notify_t*) msg->msg; for (unsigned i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; if (zone->zone_active_status()) zone->zone_temperature_notification(pmsg->type, pmsg->data); else { thd_log_debug("zone is not active\n"); } } } void cthd_engine::poll_enable_disable(bool status, message_capsul_t *msg) { unsigned int *sensor_id = (unsigned int*) msg->msg; if (status) { poll_sensor_mask |= (1 << (*sensor_id)); poll_timeout_msec = def_poll_interval; thd_log_debug("thd_engine polling enabled via %u \n", *sensor_id); } else { poll_sensor_mask &= ~(1 << (*sensor_id)); if (!poll_sensor_mask) { poll_timeout_msec = -1; thd_log_debug("thd_engine polling last disabled via %u \n", *sensor_id); } } } void cthd_engine::fast_poll_enable_disable(bool status, message_capsul_t *msg) { unsigned int *sensor_id = (unsigned int*) msg->msg; if (status) { fast_poll_sensor_mask |= (1 << (*sensor_id)); if (!saved_poll_interval) saved_poll_interval = poll_timeout_msec; poll_timeout_msec = 1000; thd_log_debug("thd_engine fast polling enabled via %u \n", *sensor_id); } else { fast_poll_sensor_mask &= ~(1 << (*sensor_id)); if (!fast_poll_sensor_mask) { if (saved_poll_interval) poll_timeout_msec = saved_poll_interval; thd_log_debug("thd_engine polling last disabled via %u \n", *sensor_id); } } } int cthd_engine::proc_message(message_capsul_t *msg) { int ret = 0; thd_log_debug("Received message %d\n", msg->msg_id); switch (msg->msg_id) { case WAKEUP: break; case TERMINATE: thd_log_msg("Terminating ...\n"); ret = -1; terminate = true; break; case PREF_CHANGED: process_pref_change(); break; case THERMAL_ZONE_NOTIFY: if (!status) { thd_log_msg("Thermal Daemon is disabled \n"); break; } thermal_zone_change(msg); break; case RELOAD_ZONES: thd_engine_reload_zones(); break; case POLL_ENABLE: if (!poll_interval_sec) { poll_enable_disable(true, msg); } break; case POLL_DISABLE: if (!poll_interval_sec) { poll_enable_disable(false, msg); } break; case FAST_POLL_ENABLE: fast_poll_enable_disable(true, msg); break; case FAST_POLL_DISABLE: fast_poll_enable_disable(false, msg); break; default: break; } return ret; } cthd_cdev *cthd_engine::thd_get_cdev_at_index(int index) { for (int i = 0; i < (int) cdevs.size(); ++i) { if (cdevs[i]->thd_cdev_get_index() == index) return cdevs[i]; } return NULL; } void cthd_engine::takeover_thermal_control() { csys_fs sysfs("/sys/class/thermal/"); DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; cthd_INT3400 int3400(uuid); thd_log_info("Taking over thermal control \n"); int3400.set_default_uuid(); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); std::stringstream policy; std::string curr_policy; std::stringstream type; std::string thermal_type; std::stringstream mode; policy << "thermal_zone" << i << "/policy"; if (sysfs.exists(policy.str().c_str())) { int ret; ret = sysfs.read(policy.str(), curr_policy); if (ret >= 0) { zone_preferences.push_back(curr_policy); sysfs.write(policy.str(), "user_space"); } } type << "thermal_zone" << i << "/type"; if (sysfs.exists(type.str().c_str())) { sysfs.read(type.str(), thermal_type); thd_log_info("Thermal zone of type %s\n", thermal_type.c_str()); if (thermal_type == "INT3400") { mode << "thermal_zone" << i << "/mode"; sysfs.write(mode.str(), "enabled"); } } } } closedir(dir); } } void cthd_engine::giveup_thermal_control() { if (control_mode != EXCLUSIVE) return; if (zone_preferences.size() == 0) return; thd_log_info("Giving up thermal control \n"); csys_fs sysfs("/sys/class/thermal/"); DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; int cnt = 0; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); std::stringstream policy; std::stringstream type; std::string thermal_type; std::stringstream mode; policy << "thermal_zone" << i << "/policy"; if (sysfs.exists(policy.str().c_str())) { sysfs.write(policy.str(), zone_preferences[cnt++]); } type << "thermal_zone" << i << "/type"; if (sysfs.exists(type.str().c_str())) { sysfs.read(type.str(), thermal_type); if (thermal_type == "INT3400") { mode << "thermal_zone" << i << "/mode"; sysfs.write(mode.str(), "disabled"); } } } } closedir(dir); } } void cthd_engine::process_terminate() { thd_log_msg("terminating on user request ..\n"); giveup_thermal_control(); } void cthd_engine::thd_engine_poll_enable(int sensor_id) { send_message(POLL_ENABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_poll_disable(int sensor_id) { send_message(POLL_DISABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_fast_poll_enable(int sensor_id) { send_message(FAST_POLL_ENABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_fast_poll_disable(int sensor_id) { send_message(FAST_POLL_DISABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_reload_zones() { thd_log_msg(" Reloading zones\n"); for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; delete zone; } zones.clear(); int ret = read_thermal_zones(); if (ret != THD_SUCCESS) { thd_log_error("No thermal sensors found\n"); // This is a fatal error and daemon will exit return; } } // Add any tested platform ids in this table #ifndef ANDROID static supported_ids_t id_table[] = { { 6, 0x2a }, // Sandybridge { 6, 0x3a }, // IvyBridge { 6, 0x3c }, // Haswell { 6, 0x45 }, // Haswell ULT { 6, 0x46 }, // Haswell ULT { 6, 0x3d }, // Broadwell { 6, 0x47 }, // Broadwell-GT3E { 6, 0x37 }, // Valleyview BYT { 6, 0x4c }, // Brasewell { 6, 0x4e }, // skylake { 6, 0x5e }, // skylake { 6, 0x5c }, // Broxton { 6, 0x7a }, // Gemini Lake { 6, 0x8e }, // kabylake { 6, 0x9e }, // kabylake { 6, 0x66 }, // Cannonlake { 6, 0x7e }, // Icelake { 6, 0x8c }, // Tigerlake_L { 6, 0x8d }, // Tigerlake { 6, 0xa5 }, // Cometlake { 6, 0xa6 }, // Cometlake_L { 6, 0xa7 }, // Rocketlake { 6, 0x9c }, // Jasper Lake { 6, 0x97 }, // Alderlake { 6, 0x9a }, // Alderlake { 6, 0xb7 }, // Raptorlake { 6, 0xba }, // Raptorlake { 6, 0xbe }, // Alderlake N { 6, 0xbf }, // Raptorlake S { 6, 0xaa }, // Mateor Lake L { 6, 0xbd }, // Lunar Lake M { 0, 0 } // Last Invalid entry }; std::vector<std::string> blocklist_paths { /* Some Lenovo machines have in-firmware thermal management, * avoid having two entities trying to manage things. * We may want to change this to dytc_perfmode once that is * widely available. */ "/sys/devices/platform/thinkpad_acpi/dytc_lapmode", }; #endif int cthd_engine::check_cpu_id() { #ifndef ANDROID // Copied from turbostat program unsigned int ebx, ecx, edx, max_level; unsigned int fms, family, model, stepping; genuine_intel = 0; int i = 0; bool valid = false; proc_list_matched = false; ebx = ecx = edx = 0; __cpuid(0, max_level, ebx, ecx, edx); if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e) genuine_intel = 1; if (genuine_intel == 0) { // Simply return without further capability check return THD_SUCCESS; } __cpuid(1, fms, ebx, ecx, edx); family = (fms >> 8) & 0xf; model = (fms >> 4) & 0xf; stepping = fms & 0xf; if (family == 6 || family == 0xf) model += ((fms >> 16) & 0xf) << 4; thd_log_msg( "%u CPUID levels; family:model:stepping 0x%x:%x:%x (%u:%u:%u)\n", max_level, family, model, stepping, family, model, stepping); while (id_table[i].family) { if (id_table[i].family == family && id_table[i].model == model) { proc_list_matched = true; valid = true; break; } i++; } if (!valid) { thd_log_msg(" Need Linux PowerCap sysfs \n"); } for (std::string path : blocklist_paths) { struct stat s; if (!stat(path.c_str(), &s)) { proc_list_matched = false; thd_log_warn("[%s] present: Thermald can't run on this platform\n", path.c_str()); break; } } #endif return THD_SUCCESS; } void cthd_engine::thd_read_default_thermal_sensors() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; int max_index = 0; if ((dir = opendir("/sys/class/thermal/thermal_zone1/")) == NULL) { thd_log_info("Waiting for thermal sysfs to be ready\n"); sleep(2); } else { closedir(dir); } thd_log_debug("thd_read_default_thermal_sensors \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); if (i > max_index) max_index = i; cthd_sensor *sensor = new cthd_sensor(i, base_path + entry->d_name + "/", ""); if (sensor->sensor_update() != THD_SUCCESS) { delete sensor; continue; } sensors.push_back(sensor); } } closedir(dir); } if (sensors.size()) current_sensor_index = max_index + 1; thd_log_info("thd_read_default_thermal_sensors loaded %zu sensors \n", sensors.size()); } void cthd_engine::thd_read_default_thermal_zones() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; int max_index = 0; thd_log_debug("thd_read_default_thermal_zones \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); if (i > max_index) max_index = i; cthd_sysfs_zone *zone = new cthd_sysfs_zone(i, "/sys/class/thermal/thermal_zone"); if (zone->zone_update() != THD_SUCCESS) { delete zone; continue; } if (control_mode == EXCLUSIVE) zone->set_zone_active(); zones.push_back(zone); } } closedir(dir); } if (zones.size()) current_zone_index = max_index + 1; thd_log_info("thd_read_default_thermal_zones loaded %zu zones \n", zones.size()); } void cthd_engine::thd_read_default_cooling_devices() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; int max_index = 0; thd_log_debug("thd_read_default_cooling devices \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "cooling_device", strlen("cooling_device"))) { int i; i = atoi(entry->d_name + strlen("cooling_device")); if (i > max_index) max_index = i; cthd_sysfs_cdev *cdev = new cthd_sysfs_cdev(i, "/sys/class/thermal/"); if (cdev->update() != THD_SUCCESS) { delete cdev; continue; } cdevs.push_back(cdev); } } closedir(dir); } if (cdevs.size()) current_cdev_index = max_index + 1; thd_log_info("thd_read_default_cooling devices loaded %zu cdevs \n", cdevs.size()); } ppcc_t* cthd_engine::get_ppcc_param(std::string name) { return parser.get_ppcc_param(name); } cthd_zone* cthd_engine::search_zone(std::string name) { cthd_zone *zone; for (unsigned int i = 0; i < zones.size(); ++i) { zone = zones[i]; if (!zone) continue; if (zone->get_zone_type() == name) return zone; if (!name.compare(0, 4, "pch_")) { if (!zone->get_zone_type().compare(0, 4, "pch_")) { thd_log_info("Matching partial zone %s %s\n", name.c_str(), zone->get_zone_type().c_str()); return zone; } } } return NULL; } cthd_cdev* cthd_engine::search_cdev(std::string name) { cthd_cdev *cdev; for (unsigned int i = 0; i < cdevs.size(); ++i) { cdev = cdevs[i]; if (!cdev) continue; if (cdev->get_cdev_type() == name) return cdev; if (cdev->get_cdev_alias() == name) return cdev; } return NULL; } cthd_sensor* cthd_engine::search_sensor(std::string name) { cthd_sensor *sensor; for (unsigned int i = 0; i < sensors.size(); ++i) { sensor = sensors[i]; if (!sensor) continue; if (sensor->get_sensor_type() == name) return sensor; if (!name.compare(0, 4, "pch_")) { if (!sensor->get_sensor_type().compare(0, 4, "pch_")) { thd_log_info("Matching partial %s %s\n", name.c_str(), sensor->get_sensor_type().c_str()); return sensor; } } } return NULL; } cthd_sensor* cthd_engine::get_sensor(int index) { if (index >= 0 && index < (int) sensors.size()) return sensors[index]; else return NULL; } int cthd_engine::get_sensor_temperature(int index, unsigned int *temperature) { if (index >= 0 && index < (int) sensors.size()) { *temperature = sensors[index]->read_temperature(); return THD_SUCCESS; } else return THD_ERROR; } cthd_zone* cthd_engine::get_zone(int index) { if (index == -1) return NULL; if (index >= 0 && index < (int) zones.size()) return zones[index]; else return NULL; } cthd_zone* cthd_engine::get_zone(std::string type) { cthd_zone *zone; for (unsigned int i = 0; i < zones.size(); ++i) { zone = zones[i]; if (zone->get_zone_type() == type) return zone; } return NULL; } // Code copied from // https://web.archive.org/web/20130822155153/https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO#Runtime_detection_of_an_RT-PREEMPT_Kernel void cthd_engine::check_for_rt_kernel() { struct utsname _uname; char *crit1 = NULL; int crit2 = 0; FILE *fd; uname(&_uname); crit1 = strcasestr(_uname.version, "PREEMPT RT"); if ((fd = fopen("/sys/kernel/realtime", "r")) != NULL) { int flag; crit2 = ((fscanf(fd, "%d", &flag) == 1) && (flag == 1)); fclose(fd); } if (crit1 && crit2) rt_kernel = true; else rt_kernel = false; thd_log_info("Running on a %s kernel\n", rt_kernel ? "PREEMPT RT" : "vanilla"); } int cthd_engine::user_add_sensor(std::string name, std::string path) { cthd_sensor *sensor; pthread_mutex_lock(&thd_engine_mutex); for (unsigned int i = 0; i < sensors.size(); ++i) { if (sensors[i]->get_sensor_type() == name) { sensor = sensors[i]; sensor->update_path(path); pthread_mutex_unlock(&thd_engine_mutex); return THD_SUCCESS; } } sensor = new cthd_sensor(current_sensor_index, path, name, SENSOR_TYPE_RAW); if (sensor->sensor_update() != THD_SUCCESS) { delete sensor; pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } sensors.push_back(sensor); ++current_sensor_index; pthread_mutex_unlock(&thd_engine_mutex); send_message(WAKEUP, 0, 0); return THD_SUCCESS; } int cthd_engine::user_add_virtual_sensor(std::string name, std::string dep_sensor, double slope, double intercept) { cthd_sensor *sensor; int ret; pthread_mutex_lock(&thd_engine_mutex); for (unsigned int i = 0; i < sensors.size(); ++i) { if (sensors[i]->get_sensor_type() == name) { sensor = sensors[i]; if (sensor->is_virtual()) { cthd_sensor_virtual *virt_sensor = (cthd_sensor_virtual *) sensor; ret = virt_sensor->sensor_update_param(dep_sensor, slope, intercept); } else { pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } pthread_mutex_unlock(&thd_engine_mutex); return ret; } } cthd_sensor_virtual *virt_sensor; virt_sensor = new cthd_sensor_virtual(current_sensor_index, name, dep_sensor, slope, intercept); if (virt_sensor->sensor_update() != THD_SUCCESS) { delete virt_sensor; pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } sensors.push_back(virt_sensor); ++current_sensor_index; pthread_mutex_unlock(&thd_engine_mutex); send_message(WAKEUP, 0, 0); return THD_SUCCESS; } cthd_sensor *cthd_engine::user_get_sensor(unsigned int index) { if (index < sensors.size()) return sensors[index]; else return NULL; } cthd_zone *cthd_engine::user_get_zone(unsigned int index) { if (index < zones.size()) return zones[index]; else return NULL; } cthd_cdev *cthd_engine::user_get_cdev(unsigned int index) { if (index < cdevs.size()) return cdevs[index]; else return NULL; } int cthd_engine::user_set_psv_temp(std::string name, unsigned int temp) { cthd_zone *zone; int ret; pthread_mutex_lock(&thd_engine_mutex); zone = get_zone(name); if (!zone) { pthread_mutex_unlock(&thd_engine_mutex); thd_log_warn("user_set_psv_temp\n"); return THD_ERROR; } thd_log_info("Setting psv %u\n", temp); ret = zone->update_psv_temperature(temp); pthread_mutex_unlock(&thd_engine_mutex); return ret; } int cthd_engine::user_set_max_temp(std::string name, unsigned int temp) { cthd_zone *zone; int ret; pthread_mutex_lock(&thd_engine_mutex); zone = get_zone(name); if (!zone) { pthread_mutex_unlock(&thd_engine_mutex); thd_log_warn("user_set_max_temp\n"); return THD_ERROR; } thd_log_info("Setting max %u\n", temp); ret = zone->update_max_temperature(temp); pthread_mutex_unlock(&thd_engine_mutex); return ret; } int cthd_engine::user_add_zone(std::string zone_name, unsigned int trip_temp, std::string sensor_name, std::string cdev_name) { int ret = THD_SUCCESS; cthd_zone_dynamic *zone = new cthd_zone_dynamic(current_zone_index, zone_name, trip_temp, PASSIVE, sensor_name, cdev_name); if (!zone) { return THD_ERROR; } if (zone->zone_update() == THD_SUCCESS) { pthread_mutex_lock(&thd_engine_mutex); zones.push_back(zone); pthread_mutex_unlock(&thd_engine_mutex); zone->set_zone_active(); ++current_zone_index; } else { delete zone; return THD_ERROR; } for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); } return ret; } int cthd_engine::user_set_zone_status(std::string name, int status) { cthd_zone *zone; pthread_mutex_lock(&thd_engine_mutex); zone = get_zone(name); if (!zone) { pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } thd_log_info("Zone Set status %d\n", status); if (status) zone->set_zone_active(); else zone->set_zone_inactive(); pthread_mutex_unlock(&thd_engine_mutex); return THD_SUCCESS; } int cthd_engine::user_get_zone_status(std::string name, int *status) { cthd_zone *zone; pthread_mutex_lock(&thd_engine_mutex); zone = get_zone(name); if (!zone) { pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } if (zone->zone_active_status()) *status = 1; else *status = 0; pthread_mutex_unlock(&thd_engine_mutex); return THD_SUCCESS; } int cthd_engine::user_delete_zone(std::string name) { pthread_mutex_lock(&thd_engine_mutex); for (unsigned int i = 0; i < zones.size(); ++i) { if (zones[i]->get_zone_type() == name) { delete zones[i]; zones.erase(zones.begin() + i); break; } } pthread_mutex_unlock(&thd_engine_mutex); for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); } return THD_SUCCESS; } int cthd_engine::user_add_cdev(std::string cdev_name, std::string cdev_path, int min_state, int max_state, int step) { cthd_cdev *cdev; pthread_mutex_lock(&thd_engine_mutex); // Check if there is existing cdev with this name and path cdev = search_cdev(cdev_name); if (!cdev) { cthd_gen_sysfs_cdev *cdev_sysfs; cdev_sysfs = new cthd_gen_sysfs_cdev(current_cdev_index, cdev_path); if (!cdev_sysfs) { pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } cdev_sysfs->set_cdev_type(cdev_name); if (cdev_sysfs->update() != THD_SUCCESS) { delete cdev_sysfs; pthread_mutex_unlock(&thd_engine_mutex); return THD_ERROR; } cdevs.push_back(cdev_sysfs); ++current_cdev_index; cdev = cdev_sysfs; } cdev->set_min_state(min_state); cdev->set_max_state(max_state); cdev->set_inc_dec_value(step); pthread_mutex_unlock(&thd_engine_mutex); for (unsigned int i = 0; i < cdevs.size(); ++i) { cdevs[i]->cdev_dump(); } return THD_SUCCESS; } int cthd_engine::parser_init() { if (parser_disabled) return THD_ERROR; if (parser_init_done) return THD_SUCCESS; if (parser.parser_init(get_config_file()) == THD_SUCCESS) { if (parser.start_parse() == THD_SUCCESS) { parser.dump_thermal_conf(); parser_init_done = true; return THD_SUCCESS; } } return THD_ERROR; } void cthd_engine::parser_deinit() { if (parser_init_done) { parser.parser_deinit(); parser_init_done = false; } } int cthd_engine::debug_mode_on(void) { static const char *debug_mode = TDRUNDIR "/debug_mode"; struct stat s; if (stat(debug_mode, &s)) return 0; return 1; } 0707010000003F000081A400000000000000000000000166853FAC00001D11000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine.h/* * thd_engine.h: thermal engine class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ENGINE_H_ #define THD_ENGINE_H_ #include <pthread.h> #include <poll.h> #include <time.h> #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_sensor.h" #include "thd_sensor_virtual.h" #include "thd_zone.h" #include "thd_cdev.h" #include "thd_parse.h" #include "thd_kobj_uevent.h" #include "thd_rapl_power_meter.h" #define MAX_MSG_SIZE 512 #define THD_NUM_OF_POLL_FDS 10 typedef enum { WAKEUP, TERMINATE, PREF_CHANGED, THERMAL_ZONE_NOTIFY, RELOAD_ZONES, POLL_ENABLE, POLL_DISABLE, FAST_POLL_ENABLE, FAST_POLL_DISABLE, } message_name_t; // This defines whether the thermal control is entirely done by // this daemon or it just complements, what is done in kernel typedef enum { COMPLEMENTRY, EXCLUSIVE, } control_mode_t; typedef struct { message_name_t msg_id; int msg_size; unsigned long msg[MAX_MSG_SIZE]; } message_capsul_t; typedef struct { unsigned int family; unsigned int model; } supported_ids_t; class cthd_engine { protected: std::vector<cthd_zone *> zones; std::vector<cthd_sensor *> sensors; std::vector<cthd_cdev *> cdevs; int current_cdev_index; int current_zone_index; int current_sensor_index; bool parse_thermal_zone_success; bool parse_thermal_cdev_success; std::string uuid; bool parser_disabled; bool adaptive_mode; private: int poll_timeout_msec; int wakeup_fd; int uevent_fd; control_mode_t control_mode; int write_pipe_fd; int preference; bool status; time_t thz_last_uevent_time; time_t thz_last_temp_ind_time; time_t thz_last_update_event_time; bool terminate; int genuine_intel; int has_invariant_tsc; int has_aperf; bool proc_list_matched; int poll_interval_sec; cthd_preference thd_pref; unsigned int poll_sensor_mask; unsigned int fast_poll_sensor_mask; int saved_poll_interval; std::string config_file; pthread_t thd_engine; pthread_attr_t thd_attr; pthread_mutex_t thd_engine_mutex; std::vector<std::string> zone_preferences; static const int thz_notify_debounce_interval = 3; struct pollfd poll_fds[THD_NUM_OF_POLL_FDS]; int poll_fd_cnt; bool rt_kernel; cthd_kobj_uevent kobj_uevent; bool parser_init_done; int proc_message(message_capsul_t *msg); void process_pref_change(); void thermal_zone_change(message_capsul_t *msg); void process_terminate(); void check_for_rt_kernel(); public: static const int max_thermal_zones = 10; static const int max_cool_devs = 50; static const int def_poll_interval = 4000; static const int soft_cdev_start_index = 100; cthd_parse parser; cthd_rapl_power_meter rapl_power_meter; cthd_engine(std::string _uuid); virtual ~cthd_engine(); void set_control_mode(control_mode_t mode) { control_mode = mode; } control_mode_t get_control_mode() { return control_mode; } void thd_engine_thread(); virtual int thd_engine_init(bool ignore_cpuid_check, bool adaptive = false); virtual int thd_engine_start(); int thd_engine_stop(); int check_cpu_id(); bool set_preference(const int pref); void thd_engine_terminate(); void thd_engine_calibrate(); int thd_engine_set_user_max_temp(const char *zone_type, const char *user_set_point); int thd_engine_set_user_psv_temp(const char *zone_type, const char *user_set_point); void poll_enable_disable(bool status, message_capsul_t *msg); void fast_poll_enable_disable(bool status, message_capsul_t *msg); cthd_cdev *thd_get_cdev_at_index(int index); void send_message(message_name_t msg_id, int size, unsigned char *msg); void takeover_thermal_control(); void giveup_thermal_control(); void thd_engine_poll_enable(int sensor_id); void thd_engine_poll_disable(int sensor_id); void thd_engine_fast_poll_enable(int sensor_id); void thd_engine_fast_poll_disable(int sensor_id); void thd_read_default_thermal_sensors(); void thd_read_default_thermal_zones(); void thd_read_default_cooling_devices(); virtual void update_engine_state() {}; virtual int read_thermal_sensors() { return 0; } ; virtual int read_thermal_zones() { return 0; } ; virtual int read_cooling_devices() { return 0; } ; int use_custom_zones() { return parse_thermal_zone_success; } int use_custom_cdevs() { return parse_thermal_cdev_success; } static const int max_cpu_count = 64; time_t last_cpu_update[max_cpu_count]; virtual bool apply_cpu_operation(int cpu) { return false; } int get_poll_timeout_ms() { return poll_timeout_msec; } int get_poll_timeout_sec() { return poll_timeout_msec / 1000; } void thd_engine_reload_zones(); bool processor_id_match() { return proc_list_matched; } int get_poll_interval() { return poll_interval_sec; } void set_poll_interval(int val) { poll_interval_sec = val; } int get_preference() { return preference; } void set_config_file(std::string conf_file) { config_file = conf_file; } std::string get_config_file() { return config_file; } virtual ppcc_t *get_ppcc_param(std::string name); virtual int search_idsp(std::string name) { return THD_ERROR; } cthd_zone *search_zone(std::string name); cthd_cdev *search_cdev(std::string name); cthd_sensor *search_sensor(std::string name); cthd_sensor *get_sensor(int index); cthd_zone *get_zone(int index); cthd_zone *get_zone(std::string type); int get_sensor_temperature(int index, unsigned int *temperature); unsigned int get_sensor_count() { return sensors.size(); } unsigned int get_zone_count() { return zones.size(); } unsigned int get_cdev_count() { return cdevs.size(); } void add_zone(cthd_zone *zone) { zones.push_back(zone); } bool rt_kernel_status() { return rt_kernel; } virtual void workarounds() { } // User/External messages int user_add_sensor(std::string name, std::string path); cthd_sensor *user_get_sensor(unsigned int index); cthd_zone *user_get_zone(unsigned int index); int user_add_virtual_sensor(std::string name, std::string dep_sensor, double slope, double intercept); int user_set_psv_temp(std::string name, unsigned int temp); int user_set_max_temp(std::string name, unsigned int temp); int user_add_zone(std::string zone_name, unsigned int trip_temp, std::string sensor_name, std::string cdev_name); int user_set_zone_status(std::string name, int status); int user_get_zone_status(std::string name, int *status); int user_delete_zone(std::string name); int user_add_cdev(std::string cdev_name, std::string cdev_path, int min_state, int max_state, int step); cthd_cdev *user_get_cdev(unsigned int index); int parser_init(); void parser_deinit(); int debug_mode_on(void); }; #endif /* THD_ENGINE_H_ */ 07070100000040000081A400000000000000000000000166853FAC00004F7E000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine_adaptive.cpp/* * cthd_engine_adaptive.cpp: Adaptive thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Copyright 2020 Google LLC * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name Matthew Garrett <mjg59@google.com> * */ #include <arpa/inet.h> #include <dirent.h> #include <errno.h> #include <inttypes.h> #include <linux/input.h> #include <sys/types.h> #include "thd_engine_adaptive.h" int cthd_engine_adaptive::install_passive(struct psv *psv) { std::string psv_zone; size_t pos = psv->target.find_last_of("."); if (pos == std::string::npos) psv_zone = psv->target; else psv_zone = psv->target.substr(pos + 1); while (psv_zone.back() == '_') { psv_zone.pop_back(); } cthd_zone *zone = search_zone(psv_zone); if (!zone) { if (!psv_zone.compare(0, 4, "B0D4")) { psv_zone = "TCPU"; zone = search_zone(psv_zone); } if (!zone) { if (!psv_zone.compare(0, 4, "TCPU")) { psv_zone = "B0D4"; zone = search_zone(psv_zone); } if (!zone) { thd_log_warn("Unable to find a zone for %s\n", psv_zone.c_str()); return THD_ERROR; } } } std::string psv_cdev; pos = psv->source.find_last_of("."); if (pos == std::string::npos) psv_cdev = psv->source; else psv_cdev = psv->source.substr(pos + 1); while (psv_cdev.back() == '_') { psv_cdev.pop_back(); } cthd_cdev *cdev = search_cdev(psv_cdev); if (!cdev) { if (!psv_cdev.compare(0, 4, "TCPU")) { psv_cdev = "B0D4"; cdev = search_cdev(psv_cdev); } if (!cdev) { thd_log_warn("Unable to find a cooling device for %s\n", psv_cdev.c_str()); return THD_ERROR; } } cthd_sensor *sensor = search_sensor(psv_zone); if (!sensor) { thd_log_warn("Unable to find a sensor for %s\n", psv_zone.c_str()); return THD_ERROR; } int temp = DECI_KELVIN_TO_CELSIUS(psv->temp) * 1000; int target_state = 0; if (psv->limit.length()) { if (!strncasecmp(psv->limit.c_str(), "MAX", 3)) { target_state = TRIP_PT_INVALID_TARGET_STATE; } else if (!strncasecmp(psv->limit.c_str(), "MIN", 3)) { target_state = 0; } else { std::istringstream buffer(psv->limit); buffer >> target_state; target_state *= 1000; } } cthd_trip_point trip_pt(zone->get_trip_count(), PASSIVE, temp, 0, zone->get_zone_index(), sensor->get_index(), SEQUENTIAL); trip_pt.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence, psv->sample_period / 10, target_state ? 1 : 0, target_state, NULL, 0, 0, 0); zone->add_trip(trip_pt, 1); zone->zone_cdev_set_binded(); zone->set_zone_active(); return 0; } void cthd_engine_adaptive::set_trip(std::string target, std::string argument) { std::string psv_zone; float float_temp = stof(argument, NULL); int temp = (int) (float_temp * 1000); size_t pos = target.find_last_of("."); if (pos == std::string::npos) psv_zone = target; else psv_zone = target.substr(pos + 1); while (psv_zone.back() == '_') { psv_zone.pop_back(); } cthd_zone *zone = search_zone(psv_zone); if (!zone) { thd_log_warn("Unable to find a zone for %s\n", psv_zone.c_str()); return; } int index = 0; cthd_trip_point *trip = zone->get_trip_at_index(index); while (trip != NULL) { if (trip->get_trip_type() == PASSIVE) { trip->update_trip_temp(temp); return; } index++; trip = zone->get_trip_at_index(index); } thd_log_warn("Unable to find a passive trippoint for %s\n", target.c_str()); } void cthd_engine_adaptive::psvt_consolidate() { /* Once all tables are installed, we need to consolidate since * thermald has different implementation. * If there is only entry of type MAX, then simply use thermald default at temperature + 1 * If there is a next trip after MAX for a target, then choose a temperature limit in the middle */ for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; unsigned int count = zone->get_trip_count(); // Special case for handling a single trip which has a defined // target state. Add another trip + 1C so that control is applied // till max state // Count is 2, because there is a default poll trip if (count == 2) { cthd_trip_point *trip = zone->get_trip_at_index(0); int target_state; thd_log_info("Single trip with a target state\n"); if (trip->is_target_valid(target_state) == THD_SUCCESS) { cthd_trip_point trip_pt(1, PASSIVE, trip->get_trip_temp() + 1000, 0, zone->get_zone_index(), trip->get_sensor_id(), SEQUENTIAL); trip_pt.thd_trip_point_add_cdev(*trip->get_first_cdev(), cthd_trip_point::default_influence); zone->add_trip(trip_pt, 1); continue; } } for (unsigned int j = 0; j < count; ++j) { cthd_trip_point *trip = zone->get_trip_at_index(j); int target_state; thd_log_debug("check trip zone:%d:%d\n", i, j); if (trip->is_target_valid(target_state) == THD_SUCCESS) { if (target_state == TRIP_PT_INVALID_TARGET_STATE) { if (j == count - 1) { // This is the last "MAX" trip // So make the target state invalid and temperature + 1 C trip->set_first_target_invalid(); trip->update_trip_temp(trip->get_trip_temp() + 1000); } else { // This is not the last trip. So something after this // if the next one has the same source and target cthd_trip_point *next_trip = zone->get_trip_at_index( j + 1); // Sinc this is not the last trip in this zone, we don't check // exception, next trip will be valid cthd_cdev *cdev = next_trip->get_first_cdev(); if (!cdev) { // Something wrong make the current target invalid trip->set_first_target_invalid(); trip->update_trip_temp( trip->get_trip_temp() + 1000); continue; } int next_target_state; if (trip->get_sensor_id() == next_trip->get_sensor_id() && trip->get_first_cdev() == next_trip->get_first_cdev() && next_trip->is_target_valid(next_target_state) == THD_SUCCESS) { // Same source and target and the target state of next is not of type MAX int state = cdev->get_min_state(); target_state = (state + next_target_state) / 2; trip->set_first_target(target_state); trip->update_trip_temp( (next_trip->get_trip_temp() + trip->get_trip_temp()) / 2); } else { // It has different source and target so // So make the target state invalid and temperature + 1 C trip->set_first_target_invalid(); trip->update_trip_temp( trip->get_trip_temp() + 1000); } } } } } } } #define DEFAULT_SAMPLE_TIME_SEC 5 int cthd_engine_adaptive::install_itmt(struct itmt_entry *itmt_entry) { std::string itmt_zone; size_t pos = itmt_entry->target.find_last_of("."); if (pos == std::string::npos) itmt_zone = itmt_entry->target; else itmt_zone = itmt_entry->target.substr(pos + 1); while (itmt_zone.back() == '_') { itmt_zone.pop_back(); } cthd_zone *zone = search_zone(itmt_zone); if (!zone) { if (!itmt_zone.compare(0, 4, "B0D4")) { itmt_zone = "TCPU"; zone = search_zone(itmt_zone); } if (!zone) { if (!itmt_zone.compare(0, 4, "TCPU")) { itmt_zone = "B0D4"; zone = search_zone(itmt_zone); } if (!zone) { thd_log_warn("Unable to find a zone for %s\n", itmt_zone.c_str()); return THD_ERROR; } } } cthd_cdev *cdev = search_cdev("rapl_controller_mmio"); if (!cdev) { return THD_ERROR; } cthd_sensor *sensor = search_sensor(itmt_zone); if (!sensor) { thd_log_warn("Unable to find a sensor for %s\n", itmt_zone.c_str()); return THD_ERROR; } int temp = (itmt_entry->trip_point - 2732) * 100; int _min_state = 0, _max_state = 0; if (itmt_entry->pl1_max.length()) { if (!strncasecmp(itmt_entry->pl1_max.c_str(), "MAX", 3)) { _max_state = TRIP_PT_INVALID_TARGET_STATE; } else if (!strncasecmp(itmt_entry->pl1_max.c_str(), "MIN", 3)) { _max_state = 0; } else { std::istringstream buffer(itmt_entry->pl1_max); buffer >> _max_state; _max_state *= 1000; } } if (itmt_entry->pl1_min.length()) { if (!strncasecmp(itmt_entry->pl1_min.c_str(), "MAX", 3)) { _min_state = TRIP_PT_INVALID_TARGET_STATE; } else if (!strncasecmp(itmt_entry->pl1_min.c_str(), "MIN", 3)) { _min_state = 0; } else { std::istringstream buffer(itmt_entry->pl1_min); buffer >> _min_state; _min_state *= 1000; } } cthd_trip_point trip_pt(zone->get_trip_count(), PASSIVE, temp, 0, zone->get_zone_index(), sensor->get_index(), SEQUENTIAL); /* * Why the min = max and max=min in the below * thd_trip_point_add_cdev? * * Thermald min_state is where no cooling is active * Thermald max_state is where max cooling is applied * * If you check one ITMT table entry: * target:\_SB_.PC00.LPCB.ECDV.CHRG trip_temp:45 pl1_min:28000 pl1.max:MAX * * This means that when exceeding 45 set the PL1 to 28W, * Below 45 PL1 is set to maximum (full power) * * That means that untrottled case is PL1_MAX * Throttled case is PL1_MIN * Which is opposite of the argument order in the * thd_trip_point_add_cdev() */ trip_pt.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence, DEFAULT_SAMPLE_TIME_SEC, 0, 0, NULL, 1, _max_state, _min_state); zone->add_trip(trip_pt, 1); zone->zone_cdev_set_binded(); zone->set_zone_active(); return 0; } int cthd_engine_adaptive::set_itmt_target(struct adaptive_target target) { struct itmt *itmt; thd_log_info("set_int3400 ITMT target %s\n", target.argument.c_str()); itmt = gddv.find_itmt(target.argument); if (!itmt) { return THD_ERROR; } if (!int3400_installed) { for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *_zone = zones[i]; // This is only for debug to plot power, so keep if (_zone->get_zone_type() == "rapl_pkg_power") continue; _zone->zone_reset(1); _zone->trip_delete_all(); if (_zone->zone_active_status()) _zone->set_zone_inactive(); } } for (int i = 0; i < (int) itmt->itmt_entries.size(); i++) { install_itmt(&itmt->itmt_entries[i]); } return THD_SUCCESS; } void cthd_engine_adaptive::set_int3400_target(struct adaptive_target target) { if (target.code == "ITMT") { if (set_itmt_target(target) == THD_SUCCESS) { int3400_installed = 1; } } if (target.code == "PSVT") { struct psvt *psvt; thd_log_info("set_int3400 target %s\n", target.argument.c_str()); psvt = gddv.find_psvt(target.argument); if (!psvt) { return; } if (!int3400_installed) { for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *_zone = zones[i]; // This is only for debug to plot power, so keep if (_zone->get_zone_type() == "rapl_pkg_power") continue; _zone->zone_reset(1); _zone->trip_delete_all(); if (_zone->zone_active_status()) _zone->set_zone_inactive(); } } for (int i = 0; i < (int) psvt->psvs.size(); i++) { install_passive(&psvt->psvs[i]); } int3400_installed = 1; } psvt_consolidate(); thd_log_info("\n\n ZONE DUMP BEGIN\n"); int new_zone_count = 0; for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); if (zones[i]->zone_active_status()) ++new_zone_count; } thd_log_info("\n\n ZONE DUMP END\n"); if (!new_zone_count) { thd_log_warn("Adaptive policy couldn't create any zones\n"); thd_log_warn("Possibly some sensors in the PSVT are missing\n"); thd_log_warn("Restart in non adaptive mode via systemd\n"); csys_fs sysfs("/tmp/ignore_adaptive"); sysfs.create(); exit(EXIT_FAILURE); } if (target.code == "PSV") { set_trip(target.participant, target.argument); } } void cthd_engine_adaptive::install_passive_default() { thd_log_info("IETM_D0 processed\n"); for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *_zone = zones[i]; _zone->zone_reset(1); _zone->trip_delete_all(); if (_zone->zone_active_status()) _zone->set_zone_inactive(); } struct psvt *psvt = gddv.find_def_psvt(); if (!psvt) return; std::vector<struct psv> psvs = psvt->psvs; thd_log_info("Name :%s\n", psvt->name.c_str()); for (unsigned int j = 0; j < psvs.size(); ++j) { install_passive(&psvs[j]); } psvt_consolidate(); thd_log_info("\n\n ZONE DUMP BEGIN\n"); for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); } thd_log_info("\n\n ZONE DUMP END\n"); } void cthd_engine_adaptive::execute_target(struct adaptive_target &target) { cthd_cdev *cdev; std::string name; int argument; thd_log_info("Target Name:%s\n", target.name.c_str()); size_t pos = target.participant.find_last_of("."); if (pos == std::string::npos) name = target.participant; else name = target.participant.substr(pos + 1); cdev = search_cdev(name); if (!cdev) { if (!name.compare(0, 4, "TCPU")) { name = "B0D4"; cdev = search_cdev(name); } } thd_log_info("looking for cdev %s\n", name.c_str()); if (!cdev) { thd_log_info("cdev %s not found\n", name.c_str()); if (target.participant == int3400_path) { set_int3400_target(target); return; } } if (target.code == "PSVT") { thd_log_info("PSVT...\n"); set_int3400_target(target); return; } try { argument = std::stoi(target.argument, NULL); } catch (...) { thd_log_info("Invalid target target:%s %s\n", target.code.c_str(), target.argument.c_str()); return; } thd_log_info("target:%s %d\n", target.code.c_str(), argument); if (cdev) cdev->set_adaptive_target(target); } void cthd_engine_adaptive::exec_fallback_target(int target) { thd_log_debug("exec_fallback_target %d\n", target); int3400_installed = 0; for (int i = 0; i < (int) gddv.targets.size(); i++) { if (gddv.targets[i].target_id != (uint64_t) target) continue; execute_target(gddv.targets[i]); } } // Called every polling interval void cthd_engine_adaptive::update_engine_state() { int target = -1; // When This means that gddv doesn't have any conditions // no need to do any processing if (passive_def_only) return; if (fallback_id < 0) target = gddv.evaluate_conditions(); if (current_matched_target == target) { thd_log_debug("No change in target\n"); return; } current_matched_target = target; // No target matched // It is possible that the target which matched last time didn't match // because of conditions have changed. // So in that case we have to install the default target. // Return only when there is a fallback ID was identified during // start after installing that target if (target == -1) { if (fallback_id >= 0 && !policy_active) { exec_fallback_target(gddv.targets[fallback_id].target_id); policy_active = 1; return; } } int3400_installed = 0; if (target > 0) { for (int i = 0; i < (int) gddv.targets.size(); i++) { if (gddv.targets[i].target_id != (uint64_t) target) continue; execute_target(gddv.targets[i]); } policy_active = 1; } if (!int3400_installed) { thd_log_info("Adaptive target doesn't have PSVT or ITMT target\n"); install_passive_default(); } } int cthd_engine_adaptive::thd_engine_init(bool ignore_cpuid_check, bool adaptive) { csys_fs sysfs(""); char *buf; size_t size; int res; parser_disabled = true; force_mmio_rapl = true; if (!ignore_cpuid_check) { check_cpu_id(); if (!processor_id_match()) { thd_log_msg("Unsupported cpu model or platform\n"); exit(EXIT_SUCCESS); } } csys_fs _sysfs("/tmp/ignore_adaptive"); if (_sysfs.exists()) { return THD_ERROR; } if (sysfs.exists("/sys/bus/platform/devices/INT3400:00")) { int3400_base_path = "/sys/bus/platform/devices/INT3400:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1040:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1040:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1041:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1041:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC10A0:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC10A0:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1042:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1042:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1068:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1068:00/"; } else { return THD_ERROR; } if (sysfs.read(int3400_base_path + "firmware_node/path", int3400_path) < 0) { thd_log_debug("Unable to locate INT3400 firmware path\n"); return THD_ERROR; } size = sysfs.size(int3400_base_path + "data_vault"); if (size == 0) { thd_log_debug("Unable to open GDDV data vault\n"); return THD_ERROR; } buf = new char[size]; if (!buf) { thd_log_error("Unable to allocate memory for GDDV"); return THD_FATAL_ERROR; } if (sysfs.read(int3400_base_path + "data_vault", buf, size) < int(size)) { thd_log_debug("Unable to read GDDV data vault\n"); delete[] buf; return THD_FATAL_ERROR; } delete[] buf; res = gddv.gddv_init(); if (res != THD_SUCCESS) { return res; } if (!gddv.conditions.size()) { thd_log_info("No adaptive conditions present\n"); struct psvt *psvt = gddv.find_def_psvt(); if (psvt) { thd_log_info("IETM.D0 found\n"); passive_def_only = 1; } else { return THD_SUCCESS; } } /* Read the sensors/zones */ res = cthd_engine::thd_engine_init(ignore_cpuid_check, adaptive); if (res != THD_SUCCESS) return res; return THD_SUCCESS; } int cthd_engine_adaptive::thd_engine_start() { if (passive_def_only) { // This means there are no conditions present // This doesn't mean that there are conditions present but none matched. install_passive_default(); return cthd_engine::thd_engine_start(); } if (gddv.verify_conditions()) { // This means that gddv has conditions which thermald can't support // Doesn't mean that the conditions supported by thermald matches // but can't satisfy any conditions as they don't match. // That can be only found during execution of conditions. thd_log_info( "Some conditions are not supported, so check if any condition set can be matched\n"); int target = gddv.evaluate_conditions(); if (target == -1) { thd_log_info("Also unable to evaluate any conditions\n"); thd_log_info( "Falling back to use configuration with the highest power\n"); int i = gddv.find_agressive_target(); thd_log_info("target:%d\n", i); if (i >= 0) { thd_log_info("fallback id:%d\n", i); fallback_id = i; } else { struct psvt *psvt = gddv.find_def_psvt(); if (psvt) { thd_log_info("IETM.D0 found\n"); install_passive_default(); } else { gddv.gddv_free(); return THD_ERROR; } } } } set_control_mode(EXCLUSIVE); // Check if any conditions can be satisfied at this time // If not just install the default passive IETM.D0 table if (gddv.evaluate_conditions() == -1) install_passive_default(); thd_log_info("adaptive engine reached end\n"); return cthd_engine::thd_engine_start(); } int thd_engine_create_adaptive_engine(bool ignore_cpuid_check, bool test_mode) { thd_engine = new cthd_engine_adaptive(); thd_engine->set_poll_interval(thd_poll_interval); // Initialize thermald objects if (thd_engine->thd_engine_init(ignore_cpuid_check, true) != THD_SUCCESS) { thd_log_info("THD engine init failed\n"); return THD_ERROR; } if (thd_engine->thd_engine_start() != THD_SUCCESS) { thd_log_info("THD engine start failed\n"); if (test_mode) { thd_log_warn("This platform doesn't support adaptive mode\n"); thd_log_warn( "It is possible that manufacturer doesn't support DPTF tables or\n"); thd_log_warn( "didn't provide tables, which can be parsed in open source.\n"); exit(0); } return THD_ERROR; } return THD_SUCCESS; } 07070100000041000081A400000000000000000000000166853FAC00000B0C000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine_adaptive.h/* * cthd_engine_adaptive.cpp: Adaptive thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Copyright 2020 Google LLC * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name Matthew Garrett <mjg59@google.com> * */ #ifndef THD_ENGINE_ADAPTIVE_H_ #define THD_ENGINE_ADAPTIVE_H_ #ifndef ANDROID #include <libevdev/libevdev.h> #include <upower.h> #endif #include "thd_engine_default.h" #include "thd_cpu_default_binding.h" #include "thd_adaptive_types.h" class cthd_engine_adaptive: public cthd_engine_default { protected: int policy_active; int fallback_id; std::string int3400_path; std::string int3400_base_path; int passive_def_only; int passive_def_processed; int power_slider; int int3400_installed; int current_matched_target; int set_itmt_target(struct adaptive_target target); int install_passive(struct psv *psv); int install_itmt(struct itmt_entry *itmt_entry); void psvt_consolidate(); void set_trip(std::string device, std::string argument); void set_int3400_target(struct adaptive_target target); void exec_fallback_target(int target); void execute_target(struct adaptive_target &target); void install_passive_default(); public: #ifndef ANDROID cthd_engine_adaptive() : cthd_engine_default("63BE270F-1C11-48FD-A6F7-3AF253FF3E2D"), policy_active( 0), fallback_id(-1), int3400_path(""), int3400_base_path( ""), passive_def_only(0), passive_def_processed(0), power_slider(75), int3400_installed(0), current_matched_target(-1) { } #else cthd_engine_adaptive() : cthd_engine_default("63BE270F-1C11-48FD-A6F7-3AF253FF3E2D"), policy_active( 0), fallback_id(-1), int3400_base_path(""), passive_def_only( 0), passive_def_processed(0) { } #endif ~cthd_engine_adaptive() { } ppcc_t* get_ppcc_param(std::string name) { return gddv.get_ppcc_param(name); } int search_idsp(std::string name) { return gddv.search_idsp(name); } int thd_engine_init(bool ignore_cpuid_check, bool adaptive); int thd_engine_start(); void update_engine_state(); void update_power_slider(); }; int thd_engine_create_adaptive_engine(bool ignore_cpuid_check, bool test_mode); #endif /* THD_ENGINE_ADAPTIVE_H_ */ 07070100000042000081A400000000000000000000000166853FAC00006CBC000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine_default.cpp/* * cthd_engine_defualt.cpp: Default thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <cstring> #include <dirent.h> #include <errno.h> #include <sys/types.h> #include "thd_engine_default.h" #include "thd_zone_cpu.h" #include "thd_zone_generic.h" #include "thd_cdev_gen_sysfs.h" #include "thd_cdev_cpufreq.h" #include "thd_cdev_rapl.h" #include "thd_cdev_intel_pstate_driver.h" #include "thd_cdev_rapl_dram.h" #include "thd_sensor_virtual.h" #include "thd_cdev_backlight.h" #include "thd_int3400.h" #include "thd_sensor_kbl_amdgpu_thermal.h" #include "thd_sensor_kbl_amdgpu_power.h" #include "thd_cdev_kbl_amdgpu.h" #include "thd_zone_kbl_amdgpu.h" #include "thd_sensor_kbl_g_mcp.h" #include "thd_zone_kbl_g_mcp.h" #include "thd_cdev_kbl_amdgpu.h" #include "thd_zone_kbl_g_mcp.h" #include "thd_sensor_rapl_power.h" #include "thd_zone_rapl_power.h" // Default CPU cooling devices, which are not part of thermal sysfs // Since non trivial initialization is not supported, we init all fields even if they are not needed /* Some security scan handler can't parse, the following block and generate unnecessary errors. * hiding good ones. So init in old style compatible to C++ static cooling_dev_t cpu_def_cooling_devices[] = { { .status = true, .mask = CDEV_DEF_BIT_UNIT_VAL | CDEV_DEF_BIT_READ_BACK | CDEV_DEF_BIT_MIN_STATE | CDEV_DEF_BIT_STEP, .index = 0, .unit_val = ABSOULUTE_VALUE, .min_state = 0, .max_state = 0, .inc_dec_step = 5, .read_back = false, .auto_down_control = false, .type_string = "intel_powerclamp", .path_str = "", .debounce_interval = 4, .pid_enable = false, .pid = {0.0, 0.0, 0.0}}, }; */ static cooling_dev_t cpu_def_cooling_devices[] = { { true, CDEV_DEF_BIT_UNIT_VAL | CDEV_DEF_BIT_READ_BACK | CDEV_DEF_BIT_MIN_STATE | CDEV_DEF_BIT_STEP, 0, ABSOULUTE_VALUE, 0, 0, 5, false, false, "intel_powerclamp", "", 4, false, { 0.0, 0.0, 0.0 },"" }, { true, CDEV_DEF_BIT_UNIT_VAL | CDEV_DEF_BIT_READ_BACK | CDEV_DEF_BIT_MIN_STATE | CDEV_DEF_BIT_STEP, 0, ABSOULUTE_VALUE, 0, 100, 5, false, false, "LCD", "", 4, false, { 0.0, 0.0, 0.0 },"" } }; cthd_engine_default::~cthd_engine_default() { } int cthd_engine_default::read_thermal_sensors() { int index; DIR *dir; struct dirent *entry; cthd_sensor *sensor; const std::string base_path[] = { "/sys/devices/platform/", "/sys/class/hwmon/" }; int i; thd_read_default_thermal_sensors(); index = current_sensor_index; sensor = search_sensor("pkg-temp-0"); if (sensor) { // Force this to support async sensor->set_async_capable(true); } sensor = search_sensor("x86_pkg_temp"); if (sensor) { // Force this to support async sensor->set_async_capable(true); } sensor = search_sensor("soc_dts0"); if (sensor) { // Force this to support async sensor->set_async_capable(true); } // Default CPU temperature zone // Find path to read DTS temperature for (i = 0; i < 2; ++i) { if ((dir = opendir(base_path[i].c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "coretemp.", strlen("coretemp.")) || !strncmp(entry->d_name, "hwmon", strlen("hwmon"))) { // Check name std::string name_path = base_path[i] + entry->d_name + "/name"; csys_fs name_sysfs(name_path.c_str()); if (!name_sysfs.exists()) { thd_log_info("dts %s doesn't exist\n", name_path.c_str()); continue; } std::string name; if (name_sysfs.read("", name) < 0) { thd_log_info("dts name read failed for %s\n", name_path.c_str()); continue; } if (name != "coretemp") continue; std::string temp_dir_path = base_path[i] + entry->d_name + "/"; DIR *temp_dir = nullptr; struct dirent *temp_dir_entry = nullptr; int len_temp_dir_entry = 0; int len_input = strlen("_input"); if ((temp_dir = opendir(temp_dir_path.c_str())) != NULL) { while ((temp_dir_entry = readdir(temp_dir)) != NULL) { len_temp_dir_entry = strlen(temp_dir_entry->d_name); if ((len_temp_dir_entry >= len_input && !strcmp( temp_dir_entry->d_name + len_temp_dir_entry - len_input, "_input")) && (!strncmp(temp_dir_entry->d_name, "temp", strlen("temp")))) { cthd_sensor *sensor = new cthd_sensor(index, temp_dir_path + temp_dir_entry->d_name, "hwmon", SENSOR_TYPE_RAW); if (sensor->sensor_update() != THD_SUCCESS) { delete sensor; closedir(temp_dir); closedir(dir); return THD_ERROR; } sensors.push_back(sensor); ++index; } } closedir(temp_dir); } } } closedir(dir); } if (index != current_sensor_index) break; } if (index == current_sensor_index) { // No coretemp sysfs exist, try hwmon thd_log_warn("Thermal DTS: No coretemp sysfs found\n"); } cthd_sensor_kbl_amdgpu_thermal *amdgpu_thermal = new cthd_sensor_kbl_amdgpu_thermal(index); if (amdgpu_thermal->sensor_update() == THD_SUCCESS) { sensors.push_back(amdgpu_thermal); ++index; } else { delete amdgpu_thermal; } cthd_sensor_kbl_amdgpu_power *amdgpu_power = new cthd_sensor_kbl_amdgpu_power(index); if (amdgpu_power->sensor_update() == THD_SUCCESS) { sensors.push_back(amdgpu_power); ++index; } else { delete amdgpu_power; } cthd_sensor_kbl_g_mcp *mcp_power = new cthd_sensor_kbl_g_mcp(index); if (mcp_power->sensor_update() == THD_SUCCESS) { sensors.push_back(mcp_power); ++index; } else { delete mcp_power; } if (debug_mode_on()) { // Only used for debug power using ThermalMonitor cthd_sensor_rapl_power *rapl_power = new cthd_sensor_rapl_power(index); if (rapl_power->sensor_update() == THD_SUCCESS) { sensors.push_back(rapl_power); ++index; } else { delete rapl_power; } } current_sensor_index = index; // Add from XML sensor config if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.sensor_count(); ++i) { thermal_sensor_t *sensor_config = parser.get_sensor_dev_index(i); if (!sensor_config) continue; cthd_sensor *sensor = search_sensor(sensor_config->name); if (sensor) { if (sensor_config->mask & SENSOR_DEF_BIT_PATH) sensor->update_path(sensor_config->path); if (sensor_config->mask & SENSOR_DEF_BIT_ASYNC_CAPABLE) sensor->set_async_capable(sensor_config->async_capable); } else { cthd_sensor *sensor_new = NULL; if (sensor_config->virtual_sensor) { cthd_sensor_virtual *sensor_virt = new cthd_sensor_virtual( index, sensor_config->name, sensor_config->sensor_link.name, sensor_config->sensor_link.multiplier, sensor_config->sensor_link.offset); if (sensor_virt->sensor_update() != THD_SUCCESS) { delete sensor_virt; continue; } sensor_new = sensor_virt; } else { sensor_new = new cthd_sensor(index, sensor_config->path, sensor_config->name, SENSOR_TYPE_RAW); if (sensor_new->sensor_update() != THD_SUCCESS) { delete sensor_new; continue; } } if (sensor_new) { sensors.push_back(sensor_new); ++index; } } } } current_sensor_index = index; for (unsigned int i = 0; i < sensors.size(); ++i) { sensors[i]->sensor_dump(); } return THD_SUCCESS; } bool cthd_engine_default::add_int340x_processor_dev(void) { if (thd_ignore_default_control) return false; /* Specialized processor thermal device names */ cthd_zone *processor_thermal = NULL, *acpi_thermal = NULL; cthd_INT3400 int3400(uuid); unsigned int passive, new_passive = 0, critical = 0; if (int3400.match_supported_uuid() == THD_SUCCESS) { processor_thermal = search_zone("B0D4"); } if (!processor_thermal) processor_thermal = search_zone("B0DB"); if (!processor_thermal) processor_thermal = search_zone("TCPU"); if (processor_thermal) { /* Check If there is a valid passive trip */ for (unsigned int i = 0; i < processor_thermal->get_trip_count(); ++i) { cthd_trip_point *trip = processor_thermal->get_trip_at_index(i); if (trip && trip->get_trip_type() == PASSIVE && (passive = trip->get_trip_temp())) { /* Need to honor ACPI _CRT, otherwise the system could be shut down by Linux kernel */ acpi_thermal = search_zone("acpitz"); if (acpi_thermal) { for (unsigned int i = 0; i < acpi_thermal->get_trip_count(); ++i) { cthd_trip_point *crit = acpi_thermal->get_trip_at_index(i); if (crit && crit->get_trip_type() == CRITICAL) { critical = crit->get_trip_temp(); break; } } } if (critical && passive + 5 * 1000 >= critical) { new_passive = critical - 15 * 1000; trip->thd_trip_update_set_point(new_passive); } thd_log_info("Processor thermal device is present \n"); thd_log_info("It will act as CPU thermal zone !! \n"); thd_log_info("Processor thermal device passive Trip is %d\n", trip->get_trip_temp()); processor_thermal->set_zone_active(); cthd_cdev *cdev; cdev = search_cdev("rapl_controller"); if (cdev) { processor_thermal->bind_cooling_device(PASSIVE, 0, cdev, cthd_trip_point::default_influence); } cdev = search_cdev("intel_pstate"); if (cdev) { processor_thermal->bind_cooling_device(PASSIVE, 0, cdev, cthd_trip_point::default_influence); } cdev = search_cdev("intel_powerclamp"); if (cdev) { processor_thermal->bind_cooling_device(PASSIVE, 0, cdev, cthd_trip_point::default_influence); } cdev = search_cdev("Processor"); if (cdev) { processor_thermal->bind_cooling_device(PASSIVE, 0, cdev, cthd_trip_point::default_influence); } return true; } } } return false; } void cthd_engine_default::disable_cpu_zone(thermal_zone_t *zone_config) { if (parser.thermal_conf_auto()) { cthd_zone *cpu_zone = search_zone("cpu"); if (cpu_zone) cpu_zone->set_zone_inactive(); return; } cthd_zone *zone = search_zone(zone_config->type); if (!zone) return; if (!zone->zone_active_status()) return; for (unsigned int k = 0; k < zone_config->trip_pts.size(); ++k) { trip_point_t &trip_pt_config = zone_config->trip_pts[k]; cthd_sensor *sensor = search_sensor(trip_pt_config.sensor_type); if (sensor && sensor->get_sensor_type() == "B0D4") { thd_log_info( "B0D4 is defined in thermal-config so deactivating default cpu\n"); cthd_zone *cpu_zone = search_zone("cpu"); if (cpu_zone) cpu_zone->set_zone_inactive(); } } } int cthd_engine_default::read_thermal_zones() { int index; DIR *dir; struct dirent *entry; const std::string base_path[] = { "/sys/devices/platform/", "/sys/class/hwmon/" }; int i; thd_read_default_thermal_zones(); index = current_zone_index; bool valid_int340x = add_int340x_processor_dev(); if (!thd_ignore_default_control && !valid_int340x && !search_zone("cpu")) { bool cpu_zone_created = false; thd_log_info("zone cpu will be created \n"); // Default CPU temperature zone // Find path to read DTS temperature for (i = 0; i < 2; ++i) { if ((dir = opendir(base_path[i].c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "coretemp.", strlen("coretemp.")) || !strncmp(entry->d_name, "hwmon", strlen("hwmon"))) { std::string name_path = base_path[i] + entry->d_name + "/name"; csys_fs name_sysfs(name_path.c_str()); if (!name_sysfs.exists()) { thd_log_info("dts zone %s doesn't exist\n", name_path.c_str()); continue; } std::string name; if (name_sysfs.read("", name) < 0) { thd_log_info("dts zone name read failed for %s\n", name_path.c_str()); continue; } thd_log_info("%s->%s\n", name_path.c_str(), name.c_str()); if (name != "coretemp") continue; cthd_zone_cpu *zone = new cthd_zone_cpu(index, base_path[i] + entry->d_name + "/", atoi(entry->d_name + strlen("coretemp."))); if (zone->zone_update() == THD_SUCCESS) { zone->set_zone_active(); zones.push_back(zone); cpu_zone_created = true; ++index; } else { delete zone; } } } closedir(dir); } if (cpu_zone_created) break; } if (!cpu_zone_created) { thd_log_error( "Thermal DTS or hwmon: No Zones present Need to configure manually\n"); } } current_zone_index = index; // Add from XML thermal zone if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.zone_count(); ++i) { bool activate; thermal_zone_t *zone_config = parser.get_zone_dev_index(i); if (!zone_config) continue; thd_log_debug("Look for Zone [%s] \n", zone_config->type.c_str()); cthd_zone *zone = search_zone(zone_config->type); if (zone) { activate = false; thd_log_info("Zone already present %s \n", zone_config->type.c_str()); for (unsigned int k = 0; k < zone_config->trip_pts.size(); ++k) { trip_point_t &trip_pt_config = zone_config->trip_pts[k]; thd_log_debug( "Trip %d, temperature %d, Look for Search sensor %s\n", k, trip_pt_config.temperature, trip_pt_config.sensor_type.c_str()); cthd_sensor *sensor = search_sensor( trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type [%s]\n", trip_pt_config.sensor_type.c_str()); // This will update the trip temperature for the matching // trip type if (trip_pt_config.temperature) { cthd_trip_point trip_pt(zone->get_trip_count(), trip_pt_config.trip_pt_type, trip_pt_config.temperature, trip_pt_config.hyst, zone->get_zone_index(), -1, trip_pt_config.control_type); zone->update_trip_temp(trip_pt); } continue; } zone->bind_sensor(sensor); if (trip_pt_config.temperature) { cthd_trip_point trip_pt(zone->get_trip_count(), trip_pt_config.trip_pt_type, trip_pt_config.temperature, trip_pt_config.hyst, zone->get_zone_index(), sensor->get_index(), trip_pt_config.control_type); if (trip_pt_config.dependency.dependency) { trip_pt.set_dependency(trip_pt_config.dependency.cdev, trip_pt_config.dependency.state); } // bind cdev for (unsigned int j = 0; j < trip_pt_config.cdev_trips.size(); ++j) { cthd_cdev *cdev = search_cdev( trip_pt_config.cdev_trips[j].type); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, trip_pt_config.cdev_trips[j].influence, trip_pt_config.cdev_trips[j].sampling_period, trip_pt_config.cdev_trips[j].target_state_valid, trip_pt_config.cdev_trips[j].target_state, &trip_pt_config.cdev_trips[j].pid_param); zone->zone_cdev_set_binded(); activate = true; } } zone->add_trip(trip_pt); } else { thd_log_debug("Trip temp == 0 is in zone %s \n", zone_config->type.c_str()); // Try to find some existing non zero trips and associate the cdevs // This is the way from an XML config a generic cooling device // can be bound. For example from ACPI thermal relationships tables for (unsigned int j = 0; j < trip_pt_config.cdev_trips.size(); ++j) { cthd_cdev *cdev = search_cdev( trip_pt_config.cdev_trips[j].type); if (!cdev) { thd_log_info("cdev for type %s not found\n", trip_pt_config.cdev_trips[j].type.c_str()); } if (cdev) { if (zone->bind_cooling_device( trip_pt_config.trip_pt_type, 0, cdev, trip_pt_config.cdev_trips[j].influence, trip_pt_config.cdev_trips[j].sampling_period, trip_pt_config.cdev_trips[j].target_state_valid, trip_pt_config.cdev_trips[j].target_state) == THD_SUCCESS) { thd_log_debug( "bind %s to trip to sensor %s\n", cdev->get_cdev_type().c_str(), sensor->get_sensor_type().c_str()); activate = true; } else { thd_log_debug( "bind_cooling_device failed for cdev %s trip %s\n", cdev->get_cdev_type().c_str(), sensor->get_sensor_type().c_str()); } } } } } if (activate) { thd_log_debug("Activate zone %s\n", zone->get_zone_type().c_str()); zone->set_zone_active(); } } else { cthd_zone_generic *zone = new cthd_zone_generic(index, i, zone_config->type); if (zone->zone_update() == THD_SUCCESS) { zones.push_back(zone); ++index; zone->set_zone_active(); } else delete zone; } disable_cpu_zone(zone_config); } } current_zone_index = index; if (debug_mode_on()) { // Only used for debug power using ThermalMonitor cthd_zone_rapl_power *rapl_power = new cthd_zone_rapl_power(index); if (rapl_power->zone_update() == THD_SUCCESS) { rapl_power->set_zone_active(); zones.push_back(rapl_power); ++index; } else { delete rapl_power; } current_zone_index = index; } if (!zones.size()) { thd_log_info("No Thermal Zones found \n"); return THD_FATAL_ERROR; } #ifdef AUTO_DETECT_RELATIONSHIP def_binding.do_default_binding(cdevs); #endif thd_log_info("\n\n ZONE DUMP BEGIN\n"); for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); } thd_log_info("\n\n ZONE DUMP END\n"); return THD_SUCCESS; } int cthd_engine_default::add_replace_cdev(cooling_dev_t *config) { cthd_cdev *cdev; bool cdev_present = false; bool percent_unit = false; // Check if there is existing cdev with this name and path cdev = search_cdev(config->type_string); if (cdev) { cdev_present = true; // Also check for path, some device like FAN has multiple paths for same type_str std::string base_path = cdev->get_base_path(); if (config->path_str.size() && config->path_str != base_path) { cdev_present = false; } } if (!cdev_present) { // create new cdev = new cthd_gen_sysfs_cdev(current_cdev_index, config->path_str); if (!cdev) return THD_ERROR; cdev->set_cdev_type(config->type_string); if (cdev->update() != THD_SUCCESS) { delete cdev; return THD_ERROR; } cdevs.push_back(cdev); ++current_cdev_index; } if (config->mask & CDEV_DEF_BIT_UNIT_VAL) { if (config->unit_val == RELATIVE_PERCENTAGES) percent_unit = true; } if (config->mask & CDEV_DEF_BIT_AUTO_DOWN) cdev->set_down_adjust_control(config->auto_down_control); if (config->mask & CDEV_DEF_BIT_STEP) { if (percent_unit) cdev->set_inc_dec_value( cdev->get_curr_state() * config->inc_dec_step / 100); else cdev->set_inc_dec_value(config->inc_dec_step); } if (config->mask & CDEV_DEF_BIT_MIN_STATE) { if (percent_unit) cdev->thd_cdev_set_min_state_param( cdev->get_curr_state() * config->min_state / 100); else cdev->thd_cdev_set_min_state_param(config->min_state); } if (config->mask & CDEV_DEF_BIT_MAX_STATE) { if (percent_unit) cdev->thd_cdev_set_max_state_param( cdev->get_curr_state() * config->max_state / 100); else cdev->thd_cdev_set_max_state_param(config->max_state); } if (config->mask & CDEV_DEF_BIT_READ_BACK) cdev->thd_cdev_set_read_back_param(config->read_back); if (config->mask & CDEV_DEF_BIT_DEBOUNCE_VAL) cdev->set_debounce_interval(config->debounce_interval); if (config->mask & CDEV_DEF_BIT_PID_PARAMS) { cdev->enable_pid(); cdev->set_pid_param(config->pid.Kp, config->pid.Ki, config->pid.Kd); } if (config->mask & CDEV_DEF_BIT_WRITE_PREFIX) cdev->thd_cdev_set_write_prefix(config->write_prefix); return THD_SUCCESS; } int cthd_engine_default::read_cooling_devices() { int size; int i; // Read first all the default cooling devices added by kernel thd_read_default_cooling_devices(); // Add RAPL cooling device cthd_sysfs_cdev_rapl *rapl_dev = new cthd_sysfs_cdev_rapl( current_cdev_index, 0); rapl_dev->set_cdev_type("rapl_controller"); rapl_dev->set_cdev_alias("B0D4"); if (rapl_dev->update() == THD_SUCCESS) { cdevs.push_back(rapl_dev); ++current_cdev_index; } else { delete rapl_dev; rapl_dev = NULL; } // Add RAPL mmio cooling device if (!disable_active_power && (parser.thermal_matched_platform_index() >= 0 || force_mmio_rapl)) { cthd_sysfs_cdev_rapl *rapl_mmio_dev = new cthd_sysfs_cdev_rapl( current_cdev_index, 0, "/sys/devices/virtual/powercap/intel-rapl-mmio/intel-rapl-mmio:0/"); rapl_mmio_dev->set_cdev_type("rapl_controller_mmio"); if (rapl_mmio_dev->update() == THD_SUCCESS) { cdevs.push_back(rapl_mmio_dev); ++current_cdev_index; // Prefer MMIO access over MSR access for B0D4 if (rapl_dev) { struct adaptive_target target = {}; rapl_dev->set_cdev_alias(""); if (adaptive_mode) { thd_log_info("Disable rapl-msr interface and use rapl-mmio\n"); rapl_dev->rapl_update_enable_status(0); target.code = "PL1MAX"; target.argument = "200000"; rapl_dev->set_adaptive_target(target); } } rapl_mmio_dev->set_cdev_alias("B0D4"); } else { delete rapl_mmio_dev; } } // Add Intel P state driver as cdev cthd_intel_p_state_cdev *pstate_dev = new cthd_intel_p_state_cdev( current_cdev_index); pstate_dev->set_cdev_type("intel_pstate"); if (pstate_dev->update() == THD_SUCCESS) { cdevs.push_back(pstate_dev); ++current_cdev_index; } else delete pstate_dev; // Add statically defined cooling devices size = sizeof(cpu_def_cooling_devices) / sizeof(cooling_dev_t); for (i = 0; i < size; ++i) { add_replace_cdev(&cpu_def_cooling_devices[i]); } cthd_cdev_cpufreq *cpu_freq_dev = new cthd_cdev_cpufreq(current_cdev_index, -1); cpu_freq_dev->set_cdev_type("cpufreq"); if (cpu_freq_dev->update() == THD_SUCCESS) { cdevs.push_back(cpu_freq_dev); ++current_cdev_index; } else delete cpu_freq_dev; cthd_sysfs_cdev_rapl_dram *rapl_dram_dev = new cthd_sysfs_cdev_rapl_dram( current_cdev_index, 0); rapl_dram_dev->set_cdev_type("rapl_controller_dram"); if (rapl_dram_dev->update() == THD_SUCCESS) { cdevs.push_back(rapl_dram_dev); ++current_cdev_index; } else delete rapl_dram_dev; cthd_cdev *cdev = search_cdev("LCD"); if (!cdev) { cthd_cdev_backlight *backlight_dev = new cthd_cdev_backlight( current_cdev_index, 0); backlight_dev->set_cdev_type("LCD"); if (backlight_dev->update() == THD_SUCCESS) { cdevs.push_back(backlight_dev); ++current_cdev_index; } else delete backlight_dev; } cthd_cdev *cdev_amdgpu = search_cdev("amdgpu"); if (!cdev_amdgpu) { cthd_cdev_kgl_amdgpu *cdev_amdgpu = new cthd_cdev_kgl_amdgpu( current_cdev_index, 0); cdev_amdgpu->set_cdev_type("amdgpu"); if (cdev_amdgpu->update() == THD_SUCCESS) { cdevs.push_back(cdev_amdgpu); ++current_cdev_index; } else delete cdev_amdgpu; } // Add from XML cooling device config if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.cdev_count(); ++i) { cooling_dev_t *cdev_config = parser.get_cool_dev_index(i); if (!cdev_config) continue; add_replace_cdev(cdev_config); } } // Dump all cooling devices for (unsigned i = 0; i < cdevs.size(); ++i) { cdevs[i]->cdev_dump(); } return THD_SUCCESS; } // Thermal engine cthd_engine *thd_engine; int thd_engine_create_default_engine(bool ignore_cpuid_check, bool exclusive_control, const char *conf_file) { int res; thd_engine = new cthd_engine_default(); if (!thd_engine) return THD_ERROR; if (exclusive_control) thd_engine->set_control_mode(EXCLUSIVE); // Initialize thermald objects thd_engine->set_poll_interval(thd_poll_interval); if (conf_file) thd_engine->set_config_file(conf_file); res = thd_engine->thd_engine_init(ignore_cpuid_check); if (res != THD_SUCCESS) { if (res == THD_FATAL_ERROR) thd_log_error("THD engine init failed\n"); else thd_log_msg("THD engine init failed\n"); } res = thd_engine->thd_engine_start(); if (res != THD_SUCCESS) { if (res == THD_FATAL_ERROR) thd_log_error("THD engine start failed\n"); else thd_log_msg("THD engine start failed\n"); } return res; } void cthd_engine_default::workarounds() { // Every 30 seconds repeat if (!disable_active_power && !workaround_interval) { workaround_rapl_mmio_power(); workaround_tcc_offset(); workaround_interval = 7; } else { --workaround_interval; } } #ifndef ANDROID #include <cpuid.h> #include <sys/mman.h> #define BIT_ULL(nr) (1ULL << (nr)) #endif void cthd_engine_default::workaround_rapl_mmio_power(void) { if (!workaround_enabled) return; cthd_cdev *cdev = search_cdev("rapl_controller_mmio"); if (cdev) { /* RAPL MMIO is enabled and getting used. No need to disable */ return; } else { csys_fs _sysfs("/sys/devices/virtual/powercap/intel-rapl-mmio/intel-rapl-mmio:0/"); if (_sysfs.exists()) { std::stringstream temp_str; temp_str << "enabled"; if (_sysfs.write(temp_str.str(), 0) > 0) return; thd_log_debug("Failed to write to RAPL MMIO\n"); } } #ifndef ANDROID int map_fd; void *rapl_mem; unsigned char *rapl_pkg_pwr_addr; unsigned long long pkg_power_limit; unsigned int ebx, ecx, edx; unsigned int fms, family, model; ecx = edx = 0; __cpuid(1, fms, ebx, ecx, edx); family = (fms >> 8) & 0xf; model = (fms >> 4) & 0xf; if (family == 6 || family == 0xf) model += ((fms >> 16) & 0xf) << 4; // Apply for KabyLake only if (model != 0x8e && model != 0x9e) return; map_fd = open("/dev/mem", O_RDWR, 0); if (map_fd < 0) return; rapl_mem = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0xfed15000); if (!rapl_mem || rapl_mem == MAP_FAILED) { close(map_fd); return; } rapl_pkg_pwr_addr = ((unsigned char *)rapl_mem + 0x9a0); pkg_power_limit = *(unsigned long long *)rapl_pkg_pwr_addr; *(unsigned long long *)rapl_pkg_pwr_addr = pkg_power_limit & ~BIT_ULL(15); munmap(rapl_mem, 4096); close(map_fd); #endif } void cthd_engine_default::workaround_tcc_offset(void) { #ifndef ANDROID csys_fs sys_fs; int tcc; if (tcc_offset_checked && tcc_offset_low) return; if (parser.thermal_matched_platform_index() < 0) { tcc_offset_checked = 1; tcc_offset_low = 1; return; } if (sys_fs.exists("/sys/bus/pci/devices/0000:00:04.0/tcc_offset_degree_celsius")) { if (sys_fs.read("/sys/bus/pci/devices/0000:00:04.0/tcc_offset_degree_celsius", &tcc) <= 0) { tcc_offset_checked = 1; tcc_offset_low = 1; return; } if (tcc > 10) { int ret; ret = sys_fs.write("/sys/bus/pci/devices/0000:00:04.0/tcc_offset_degree_celsius", 5); if (ret < 0) tcc_offset_low = 1; // probably locked so retryA tcc_offset_checked = 1; } else { if (!tcc_offset_checked) tcc_offset_low = 1; tcc_offset_checked = 1; } } else { thd_log_info("Kernel update is required to update TCC\n"); tcc_offset_checked = 1; tcc_offset_low = 1; } #endif } 07070100000043000081A400000000000000000000000166853FAC00000982000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_engine_default.h/* * cthd_engine_defualt.cpp: Default thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ENGINE_DEFAULT_H_ #define THD_ENGINE_DEFAULT_H_ #include "thd_engine.h" #include "thd_cpu_default_binding.h" #include "thd_gddv.h" class cthd_engine_default: public cthd_engine { private: int add_replace_cdev(cooling_dev_t *config); bool add_int340x_processor_dev(void); void disable_cpu_zone(thermal_zone_t *zone_config); void workaround_rapl_mmio_power(void); void workaround_tcc_offset(void); cthd_cpu_default_binding def_binding; int workaround_interval; #ifndef ANDROID int tcc_offset_checked; int tcc_offset_low; #endif protected: bool force_mmio_rapl; public: static const int power_clamp_reduction_percent = 5; cthd_gddv gddv; #ifndef ANDROID cthd_engine_default() : cthd_engine("42A441D6-AE6A-462b-A84B-4A8CE79027D3"), workaround_interval(0), tcc_offset_checked(0), tcc_offset_low(0), force_mmio_rapl(false) { } cthd_engine_default(std::string _uuid) : cthd_engine(_uuid), workaround_interval(0), tcc_offset_checked(0), tcc_offset_low(0), force_mmio_rapl(false) { } #else cthd_engine_default() : cthd_engine("42A441D6-AE6A-462b-A84B-4A8CE79027D3"), workaround_interval(0), force_mmio_rapl(false) { } cthd_engine_default(std::string _uuid) : cthd_engine(_uuid), workaround_interval(0), force_mmio_rapl(false) { } #endif ~cthd_engine_default(); int read_thermal_zones(); int read_cooling_devices(); int read_thermal_sensors(); void workarounds(); }; int thd_engine_create_default_engine(bool ignore_cpuid_check, bool exclusive_control, const char *config_file); #endif /* THD_ENGINE_DEFAULT_H_ */ 07070100000044000081A400000000000000000000000166853FAC0000A672000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_gddv.cpp/* * cthd_gddv.cpp: Adaptive thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Copyright 2020 Google LLC * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name Matthew Garrett <mjg59@google.com> * */ #include <arpa/inet.h> #include <dirent.h> #include <errno.h> #include <inttypes.h> #include "thd_lzma_dec.h" #include <linux/input.h> #include <sys/types.h> #include "thd_gddv.h" /* From esif_lilb_datavault.h */ #define ESIFDV_NAME_LEN 32 // Max DataVault Name (Cache Name) Length (not including NULL) #define ESIFDV_DESC_LEN 64 // Max DataVault Description Length (not including NULL) #define SHA256_HASH_BYTES 32 struct header { uint16_t signature; uint16_t headersize; uint32_t version; union { /* Added in V1 */ struct { uint32_t flags; } v1; /* Added in V2 */ struct { uint32_t flags; char segmentid[ESIFDV_NAME_LEN]; char comment[ESIFDV_DESC_LEN]; uint8_t payload_hash[SHA256_HASH_BYTES]; uint32_t payload_size; uint32_t payload_class; } v2; }; } __attribute__ ((packed)); class _gddv_exception: public std::exception { virtual const char* what() const throw () { return "GDDV parsing failed"; } } gddv_exception; void cthd_gddv::destroy_dynamic_sources() { #ifndef ANDROID if (upower_client) g_clear_object(&upower_client); if (power_profiles_daemon) g_clear_object(&power_profiles_daemon); if (tablet_dev) { close(libevdev_get_fd(tablet_dev)); libevdev_free(tablet_dev); if (lid_dev == tablet_dev) lid_dev = NULL; tablet_dev = NULL; } if (lid_dev) { close(libevdev_get_fd(lid_dev)); libevdev_free(lid_dev); lid_dev = NULL; } #endif } cthd_gddv::~cthd_gddv() { } int cthd_gddv::get_type(char *object, int *offset) { return *(uint32_t*) (object + *offset); } uint64_t cthd_gddv::get_uint64(char *object, int *offset) { uint64_t value; int type = *(uint32_t*) (object + *offset); if (type != 4) { thd_log_warn("Found object of type %d, expecting 4\n", type); throw gddv_exception; } *offset += 4; value = *(uint64_t*) (object + *offset); *offset += 8; return value; } char* cthd_gddv::get_string(char *object, int *offset) { int type = *(uint32_t*) (object + *offset); uint64_t length; char *value; if (type != 8) { thd_log_warn("Found object of type %d, expecting 8\n", type); throw gddv_exception; } *offset += 4; length = *(uint64_t*) (object + *offset); *offset += 8; value = &object[*offset]; *offset += length; return value; } int cthd_gddv::merge_custom(struct custom_condition *custom, struct condition *condition) { condition->device = custom->participant; condition->condition = (enum adaptive_condition) custom->type; return 0; } int cthd_gddv::merge_appc() { for (int i = 0; i < (int) custom_conditions.size(); i++) { for (int j = 0; j < (int) conditions.size(); j++) { for (int k = 0; k < (int) conditions[j].size(); k++) { if (custom_conditions[i].condition == conditions[j][k].condition) { merge_custom(&custom_conditions[i], &conditions[j][k]); } } } } return 0; } int cthd_gddv::parse_appc(char *appc, int len) { int offset = 0; uint64_t version; if (appc[0] != 4) { thd_log_info("Found malformed APPC table, ignoring\n"); return 0; } version = get_uint64(appc, &offset); if (version != 1) { // Invalid APPC tables aren't fatal thd_log_info("Found unsupported or malformed APPC version %d\n", (int) version); return 0; } while (offset < len) { struct custom_condition condition; condition.condition = (enum adaptive_condition) get_uint64(appc, &offset); condition.name = get_string(appc, &offset); condition.participant = get_string(appc, &offset); condition.domain = get_uint64(appc, &offset); condition.type = get_uint64(appc, &offset); custom_conditions.push_back(condition); } return 0; } int cthd_gddv::parse_apat(char *apat, int len) { int offset = 0; uint64_t version = get_uint64(apat, &offset); if (version != 2) { thd_log_warn("Found unsupported APAT version %d\n", (int) version); throw gddv_exception; } while (offset < len) { struct adaptive_target target; target.target_id = get_uint64(apat, &offset); target.name = get_string(apat, &offset); target.participant = get_string(apat, &offset); target.domain = get_uint64(apat, &offset); target.code = get_string(apat, &offset); target.argument = get_string(apat, &offset); targets.push_back(target); } return 0; } void cthd_gddv::dump_apat() { thd_log_info("..apat dump begin.. \n"); for (unsigned int i = 0; i < targets.size(); ++i) { thd_log_info( "target_id:%" PRIu64 " name:%s participant:%s domain:%d code:%s argument:%s\n", targets[i].target_id, targets[i].name.c_str(), targets[i].participant.c_str(), (int)targets[i].domain, targets[i].code.c_str(), targets[i].argument.c_str()); } thd_log_info("apat dump end\n"); } int cthd_gddv::parse_apct(char *apct, int len) { int i; int offset = 0; uint64_t version = get_uint64(apct, &offset); if (version == 1) { while (offset < len) { std::vector<struct condition> condition_set; uint64_t target = get_uint64(apct, &offset); if (int(target) == -1) { thd_log_warn("Invalid APCT target\n"); throw gddv_exception; } for (i = 0; i < 10; i++) { struct condition condition; condition.condition = adaptive_condition(0); condition.device = ""; condition.comparison = adaptive_comparison(0); condition.argument = 0; condition.operation = adaptive_operation(0); condition.time_comparison = adaptive_comparison(0); condition.time = 0; condition.target = 0; condition.state = 0; condition.state_entry_time = 0; condition.target = target; condition.ignore_condition = 0; if (offset >= len) { thd_log_warn("Read off end of buffer in APCT parsing\n"); throw gddv_exception; } condition.condition = adaptive_condition( get_uint64(apct, &offset)); condition.comparison = adaptive_comparison( get_uint64(apct, &offset)); condition.argument = get_uint64(apct, &offset); if (i < 9) { condition.operation = adaptive_operation( get_uint64(apct, &offset)); if (condition.operation == FOR) { offset += 12; condition.time_comparison = adaptive_comparison( get_uint64(apct, &offset)); condition.time = get_uint64(apct, &offset); offset += 12; i++; } } condition_set.push_back(condition); } conditions.push_back(condition_set); } } else if (version == 2) { while (offset < len) { std::vector<struct condition> condition_set; uint64_t target = get_uint64(apct, &offset); if (int(target) == -1) { thd_log_warn("Invalid APCT target"); throw gddv_exception; } uint64_t count = get_uint64(apct, &offset); for (i = 0; i < int(count); i++) { struct condition condition = {}; condition.condition = adaptive_condition(0); condition.device = ""; condition.comparison = adaptive_comparison(0); condition.argument = 0; condition.operation = adaptive_operation(0); condition.time_comparison = adaptive_comparison(0); condition.time = 0; condition.target = 0; condition.state = 0; condition.state_entry_time = 0; condition.target = target; condition.ignore_condition = 0; if (offset >= len) { thd_log_warn("Read off end of buffer in parsing APCT\n"); throw gddv_exception; } condition.condition = adaptive_condition( get_uint64(apct, &offset)); condition.device = get_string(apct, &offset); offset += 12; condition.comparison = adaptive_comparison( get_uint64(apct, &offset)); condition.argument = get_uint64(apct, &offset); if (i < int(count - 1)) { condition.operation = adaptive_operation( get_uint64(apct, &offset)); if (condition.operation == FOR) { offset += 12; get_string(apct, &offset); offset += 12; condition.time_comparison = adaptive_comparison( get_uint64(apct, &offset)); condition.time = get_uint64(apct, &offset); offset += 12; i++; } } condition_set.push_back(condition); } conditions.push_back(condition_set); } } else { thd_log_warn("Unsupported APCT version %d\n", (int) version); throw gddv_exception; } return 0; } static const char *condition_names[] = { "Invalid", "Default", "Orientation", "Proximity", "Motion", "Dock", "Workload", "Cooling_mode", "Power_source", "Aggregate_power_percentage", "Lid_state", "Platform_type", "Platform_SKU", "Utilisation", "TDP", "Duty_cycle", "Power", "Temperature", "Display_orientation", "Oem0", "Oem1", "Oem2", "Oem3", "Oem4", "Oem5", "PMAX", "PSRC", "ARTG", "CTYP", "PROP", "Unk1", "Unk2", "Battery_state", "Battery_rate", "Battery_remaining", "Battery_voltage", "PBSS", "Battery_cycles", "Battery_last_full", "Power_personality", "Battery_design_capacity", "Screen_state", "AVOL", "ACUR", "AP01", "AP02", "AP10", "Time", "Temperature_without_hysteresis", "Mixed_reality", "User_presence", "RBHF", "VBNL", "CMPP", "Battery_percentage", "Battery_count", "Power_slider" }; static const char *comp_strs[] = { "INVALID", "ADAPTIVE_EQUAL", "ADAPTIVE_LESSER_OR_EQUAL", "ADAPTIVE_GREATER_OR_EQUAL" }; #define ARRAY_SIZE(array) \ (sizeof(array) / sizeof(array[0])) void cthd_gddv::dump_apct() { thd_log_info("..apct dump begin.. \n"); for (unsigned int i = 0; i < conditions.size(); ++i) { std::vector<struct condition> condition_set; thd_log_info("condition_set %d\n", i); condition_set = conditions[i]; for (unsigned int j = 0; j < condition_set.size(); ++j) { std::string cond_name, comp_str, op_str; if (condition_set[j].condition < ARRAY_SIZE(condition_names)) { cond_name = condition_names[condition_set[j].condition]; } else if (condition_set[j].condition >= 0x1000 && condition_set[j].condition < 0x10000) { std::stringstream msg; msg << "Oem" << (condition_set[j].condition - 0x1000 + 6); cond_name = msg.str(); } else { std::stringstream msg; msg << "UNKNOWN" << "( " << condition_set[j].condition << " )"; cond_name = msg.str(); } if (condition_set[j].comparison < ARRAY_SIZE(comp_strs)) { comp_str = comp_strs[condition_set[j].comparison]; } if (condition_set[j].operation == 1) { op_str = "AND"; } else if (condition_set[j].operation == 2) { op_str = "FOR"; } else { op_str = "INVALID"; } thd_log_info( "\ttarget:%d device:%s condition:%s comparison:%s argument:%d" " operation:%s time_comparison:%d time:%d" " stare:%d state_entry_time:%d \n", condition_set[j].target, condition_set[j].device.c_str(), cond_name.c_str(), comp_str.c_str(), condition_set[j].argument, op_str.c_str(), condition_set[j].time_comparison, condition_set[j].time, condition_set[j].state, condition_set[j].state_entry_time); } } thd_log_info("..apct dump end.. \n"); } ppcc_t* cthd_gddv::get_ppcc_param(std::string name) { if (name != "TCPU.D0") return NULL; for (int i = 0; i < (int) ppccs.size(); i++) { if (ppccs[i].name == name) return &ppccs[i]; } return NULL; } int cthd_gddv::parse_ppcc(char *name, char *buf, int len) { ppcc_t ppcc; ppcc.name = name; ppcc.power_limit_min = *(uint64_t*) (buf + 28); ppcc.power_limit_max = *(uint64_t*) (buf + 40); ppcc.time_wind_min = *(uint64_t*) (buf + 52); ppcc.time_wind_max = *(uint64_t*) (buf + 64); ppcc.step_size = *(uint64_t*) (buf + 76); ppcc.valid = 1; if (len < 156) return 0; thd_log_info("Processing ppcc limit 2, length %d\n", len); int start = 76 + 12; ppcc.power_limit_1_min = *(uint64_t*) (buf + start + 12); ppcc.power_limit_1_max = *(uint64_t*) (buf + start + 24); ppcc.time_wind_1_min = *(uint64_t*) (buf + start + 36); ppcc.time_wind_1_max = *(uint64_t*) (buf + start + 48); ppcc.step_1_size = *(uint64_t*) (buf + start + 60); if (ppcc.power_limit_1_max && ppcc.power_limit_1_min && ppcc.time_wind_1_min && ppcc.time_wind_1_max && ppcc.step_1_size) ppcc.limit_1_valid = 1; else ppcc.limit_1_valid = 0; ppccs.push_back(ppcc); return 0; } void cthd_gddv::dump_ppcc() { thd_log_info("..ppcc dump begin.. \n"); for (unsigned int i = 0; i < ppccs.size(); ++i) { thd_log_info( "Name:%s Limit:0 power_limit_max:%d power_limit_min:%d step_size:%d time_win_max:%d time_win_min:%d\n", ppccs[i].name.c_str(), ppccs[i].power_limit_max, ppccs[i].power_limit_min, ppccs[i].step_size, ppccs[i].time_wind_max, ppccs[i].time_wind_min); thd_log_info( "Name:%s Limit:1 power_limit_max:%d power_limit_min:%d step_size:%d time_win_max:%d time_win_min:%d\n", ppccs[i].name.c_str(), ppccs[i].power_limit_1_max, ppccs[i].power_limit_1_min, ppccs[i].step_1_size, ppccs[i].time_wind_1_max, ppccs[i].time_wind_1_min); } thd_log_info("ppcc dump end\n"); } int cthd_gddv::parse_psvt(char *name, char *buf, int len) { int offset = 0; int version = get_uint64(buf, &offset); struct psvt psvt; if (version > 2) { thd_log_warn("Found unsupported PSVT version %d\n", (int) version); throw gddv_exception; } if (name == NULL) psvt.name = "Default"; else psvt.name = name; while (offset < len) { struct psv psv; psv.source = get_string(buf, &offset); psv.target = get_string(buf, &offset); psv.priority = get_uint64(buf, &offset); psv.sample_period = get_uint64(buf, &offset); psv.temp = get_uint64(buf, &offset); psv.domain = get_uint64(buf, &offset); psv.control_knob = get_uint64(buf, &offset); if (get_type(buf, &offset) == 8) { psv.limit = get_string(buf, &offset); } else { uint64_t tmp = get_uint64(buf, &offset); psv.limit = std::to_string(tmp); } psv.step_size = get_uint64(buf, &offset); psv.limit_coeff = get_uint64(buf, &offset); psv.unlimit_coeff = get_uint64(buf, &offset); offset += 12; psvt.psvs.push_back(psv); } psvts.push_back(psvt); return 0; } void cthd_gddv::dump_psvt() { thd_log_info("..psvt dump begin.. \n"); for (unsigned int i = 0; i < psvts.size(); ++i) { std::vector<struct psv> psvs = psvts[i].psvs; thd_log_info("Name :%s\n", psvts[i].name.c_str()); for (unsigned int j = 0; j < psvs.size(); ++j) { thd_log_info( "\t source:%s target:%s priority:%d sample_period:%d temp:%d domain:%d control_knob:%d psv.limit:%s\n", psvs[j].source.c_str(), psvs[j].target.c_str(), psvs[j].priority, psvs[j].sample_period, DECI_KELVIN_TO_CELSIUS(psvs[j].temp), psvs[j].domain, psvs[j].control_knob, psvs[j].limit.c_str()); } } thd_log_info("psvt dump end\n"); } struct psvt* cthd_gddv::find_def_psvt() { for (unsigned int i = 0; i < psvts.size(); ++i) { if (psvts[i].name == "IETM.D0") { return &psvts[i]; } } return NULL; } int cthd_gddv::parse_itmt(char *name, char *buf, int len) { int offset = 0; int version = get_uint64(buf, &offset); struct itmt itmt; thd_log_debug(" ITMT version %d %s\n", (int) version, name); if (version > 2) { thd_log_info("Unsupported ITMT version\n"); return THD_ERROR; } if (name == NULL) itmt.name = "Default"; else itmt.name = name; while (offset < len) { struct itmt_entry itmt_entry; itmt_entry.target = get_string(buf, &offset); itmt_entry.trip_point = get_uint64(buf, &offset); itmt_entry.pl1_min = get_string(buf, &offset); itmt_entry.pl1_max = get_string(buf, &offset); itmt_entry.unused = get_string(buf, &offset); if (version == 2) { // Ref DPTF/Sources/Manager/DataManager.cpp DataManager::loadItmtTableObject() std::string dummy_str; unsigned long long dummy1,dummy2, dummy3; // There are three additional fields dummy1 = get_uint64(buf, &offset); dummy_str = get_string(buf, &offset); dummy2 = get_uint64(buf, &offset); dummy3 = get_uint64(buf, &offset); thd_log_debug("ignore dummy_str:%s %llu %llu %llu\n", dummy_str.c_str(), dummy1, dummy2, dummy3); } else { offset += 12; } itmt.itmt_entries.push_back(itmt_entry); } itmts.push_back(itmt); return 0; } void cthd_gddv::dump_itmt() { thd_log_info("..itmt dump begin.. \n"); for (unsigned int i = 0; i < itmts.size(); ++i) { std::vector<struct itmt_entry> itmt = itmts[i].itmt_entries; thd_log_info("Name :%s\n", itmts[i].name.c_str()); for (unsigned int j = 0; j < itmt.size(); ++j) { thd_log_info("\t target:%s trip_temp:%d pl1_min:%s pl1.max:%s\n", itmt[j].target.c_str(), DECI_KELVIN_TO_CELSIUS(itmt[j].trip_point), itmt[j].pl1_min.c_str(), itmt[j].pl1_max.c_str()); } } thd_log_info("itmt dump end\n"); } void cthd_gddv::parse_idsp(char *name, char *start, int length) { int len, i = 0; unsigned char *str = (unsigned char*) start; while (i < length) { char idsp[64]; std::string idsp_str; // The minimum length for an IDSP should be at least 28 // including headers and values if ((length - i) < 28) return; if (*str != 7) break; str += 4; // Get to Length field i += 4; len = *(int*) str; str += 8; // Get to actual contents i += 8; snprintf(idsp, sizeof(idsp), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", str[3], str[2], str[1], str[0], str[5], str[4], str[7], str[6], str[8], str[9], str[10], str[11], str[12], str[13], str[14], str[15]); idsp_str = idsp; std::transform(idsp_str.begin(), idsp_str.end(), idsp_str.begin(), ::toupper); idsps.push_back(idsp_str); str += len; i += len; } } void cthd_gddv::dump_idsps() { thd_log_info("..idsp dump begin.. \n"); for (unsigned int i = 0; i < idsps.size(); ++i) { thd_log_info("idsp :%s\n", idsps[i].c_str()); } thd_log_info("idsp dump end\n"); } int cthd_gddv::search_idsp(std::string name) { for (unsigned int i = 0; i < idsps.size(); ++i) { if (!idsps[i].compare(0, 36, name)) return THD_SUCCESS; } return THD_ERROR; } void cthd_gddv::parse_trip_point(char *name, char *type, char *val, int len) { struct trippoint trip; trip.name = name; trip.type_str = type; if (!trip.type_str.compare(0, 2, "_c")) trip.type = CRITICAL; else if (!trip.type_str.compare(0, 2, "_p")) trip.type = PASSIVE; else if (!trip.type_str.compare(0, 2, "_h")) trip.type = HOT; else if (!trip.type_str.compare(0, 2, "_a")) trip.type = ACTIVE; else trip.type = INVALID_TRIP_TYPE; trip.temp = DECI_KELVIN_TO_CELSIUS(*(int *)val); trippoints.push_back(trip); } void cthd_gddv::dump_trips() { thd_log_info("..trippoint dump begin.. \n"); for (unsigned int i = 0; i < trippoints.size(); ++i) { thd_log_info("name:%s type_str:%s type:%d temp:%d\n", trippoints[i].name.c_str(), trippoints[i].type_str.c_str(), trippoints[i].type, trippoints[i].temp); } thd_log_info("trippoint dump end\n"); } int cthd_gddv::get_trip_temp(std::string name, trip_point_type_t type) { std::string search_name = name + ".D0"; for (unsigned int i = 0; i < trippoints.size(); ++i) { if (!trippoints[i].name.compare(search_name) && trippoints[i].type == type) return trippoints[i].temp; } return THD_ERROR; } int cthd_gddv::parse_trt(char *buf, int len) { int offset = 0; thd_log_debug("TRT len:%d\n", len); if (len > 0) { thd_log_info( "_TRT not implemented. Report this for implementation with the thermald log using --loglevel=debug\n"); } while (offset < len) { struct trt_entry entry; entry.source = get_string(buf, &offset); entry.dest = get_string(buf, &offset); entry.priority = get_uint64(buf, &offset); entry.sample_rate = get_uint64(buf, &offset); entry.resd0 = get_uint64(buf, &offset); entry.resd1 = get_uint64(buf, &offset); entry.resd2 = get_uint64(buf, &offset); entry.resd3 = get_uint64(buf, &offset); thd_log_info("trt source:%s dest:%s prio:%d sample_rate:%d\n", entry.source.c_str(), entry.dest.c_str(), entry.priority, entry.sample_rate); } return THD_SUCCESS; } // From Common/esif_sdk_iface_esif.h: #define ESIF_SERVICE_CONFIG_COMPRESSED 0x40000000/* Payload is Compressed */ // From Common/esif_sdk.h #define ESIFHDR_VERSION(major, minor, revision) ((uint32_t)((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | ((revision) & 0xFFFF))) #define ESIFHDR_GET_MAJOR(version) ((uint32_t)(((version) >> 24) & 0xFF)) #define ESIFHDR_GET_MINOR(version) ((uint32_t)(((version) >> 16) & 0xFF)) #define ESIFHDR_GET_REVISION(version) ((uint32_t)((version) & 0xFFFF)) //From ESIF/Products/ESIF_LIB/Sources/esif_lib_datavault.c #define ESIFDV_HEADER_SIGNATURE 0x1FE5 #define ESIFDV_ITEM_KEYS_REV0_SIGNATURE 0xA0D8 int cthd_gddv::handle_compressed_gddv(char *buf, int size) { struct header *header = (struct header*) buf; uint64_t payload_output_size; uint64_t output_size; int res; unsigned char *decompressed; size_t destlen=0; payload_output_size = *(uint64_t*) (buf + header->headersize + 5); output_size = header->headersize + payload_output_size; decompressed = (unsigned char*) malloc(output_size); if (!decompressed) { thd_log_warn("Failed to allocate buffer for decompressed output\n"); throw gddv_exception; } res=lzma_decompress(NULL,&destlen, (const unsigned char*) (buf + header->headersize), size-header->headersize); thd_log_debug("decompress result =%d\n",res); res=lzma_decompress(( unsigned char*)(decompressed+ header->headersize), &destlen, (const unsigned char*) (buf + header->headersize), size-header->headersize); thd_log_debug("decompress result =%d\n",res); /* Copy and update header. * This will contain one or more nested repositories usually. */ memcpy (decompressed, buf, header->headersize); header = (struct header*) decompressed; header->v2.flags &= ~ESIF_SERVICE_CONFIG_COMPRESSED; header->v2.payload_size = payload_output_size; res = parse_gddv((char*) decompressed, output_size, NULL); free(decompressed); return res; } int cthd_gddv::parse_gddv_key(char *buf, int size, int *end_offset) { int offset = 0; uint32_t keyflags; uint32_t keylength; uint32_t valtype; uint32_t vallength; char *key; char *val; char *str; char *name = NULL; char *type = NULL; char *point = NULL; char *ns = NULL; memcpy(&keyflags, buf + offset, sizeof(keyflags)); offset += sizeof(keyflags); memcpy(&keylength, buf + offset, sizeof(keylength)); offset += sizeof(keylength); key = new char[keylength]; memcpy(key, buf + offset, keylength); offset += keylength; memcpy(&valtype, buf + offset, sizeof(valtype)); offset += sizeof(valtype); memcpy(&vallength, buf + offset, sizeof(vallength)); offset += sizeof(vallength); val = new char[vallength]; memcpy(val, buf + offset, vallength); offset += vallength; if (end_offset) *end_offset = offset; str = strtok(key, "/"); if (!str) { thd_log_debug("Ignoring key %s\n", key); delete[] (key); delete[] (val); /* Ignore */ return THD_SUCCESS; } if (strcmp(str, "participants") == 0) { name = strtok(NULL, "/"); type = strtok(NULL, "/"); point = strtok(NULL, "/"); } else if (strcmp(str, "shared") == 0) { ns = strtok(NULL, "/"); type = strtok(NULL, "/"); if (strcmp(ns, "tables") == 0) { point = strtok(NULL, "/"); } } if (name && type && strcmp(type, "ppcc") == 0) { parse_ppcc(name, val, vallength); } if (type && strcmp(type, "psvt") == 0) { if (point == NULL) parse_psvt(name, val, vallength); else parse_psvt(point, val, vallength); } if (type && strcmp(type, "appc") == 0) { parse_appc(val, vallength); } if (type && strcmp(type, "apct") == 0) { parse_apct(val, vallength); } if (type && strcmp(type, "apat") == 0) { parse_apat(val, vallength); } if (type && strcmp(type, "itmt") == 0) { if (point == NULL) parse_itmt(name, val, vallength); else parse_itmt(point, val, vallength); } if (name && type && strcmp(type, "idsp") == 0) { parse_idsp(name, val, vallength); } if (name && type && point && strcmp(type, "trippoint") == 0) { parse_trip_point(name, point, val, vallength); } if (type && strcmp(type, "_trt") == 0) { parse_trt(val, vallength); } delete[] (key); delete[] (val); return THD_SUCCESS; } int cthd_gddv::parse_gddv(char *buf, int size, int *end_offset) { int offset = 0; struct header *header; if (size < (int) sizeof(struct header)) return THD_ERROR; header = (struct header*) buf; if (header->signature != ESIFDV_HEADER_SIGNATURE) { thd_log_warn("Unexpected GDDV signature 0x%x\n", header->signature); throw gddv_exception; } if (ESIFHDR_GET_MAJOR(header->version) != 1 && ESIFHDR_GET_MAJOR(header->version) != 2) return THD_ERROR; offset = header->headersize; thd_log_debug("header version[%d] size[%d] header_size[%d] flags[%08X]\n", ESIFHDR_GET_MAJOR(header->version), size, header->headersize, header->v1.flags); if (ESIFHDR_GET_MAJOR(header->version) == 2) { char name[ESIFDV_NAME_LEN + 1] = { 0 }; char comment[ESIFDV_DESC_LEN + 1] = { 0 }; if (header->v2.flags & ESIF_SERVICE_CONFIG_COMPRESSED) { thd_log_debug("Uncompress GDDV payload\n"); return handle_compressed_gddv(buf, size); } strncpy(name, header->v2.segmentid, sizeof(name) - 1); strncpy(comment, header->v2.comment, sizeof(comment) - 1); thd_log_debug("DV name: %s\n", name); thd_log_debug("DV comment: %s\n", comment); thd_log_debug("Got payload of size %d (data length: %d)\n", size, header->v2.payload_size); size = header->v2.payload_size; } while ((offset + header->headersize) < size) { int res; int end_offset = 0; if (ESIFHDR_GET_MAJOR(header->version) == 2) { unsigned short signature; signature = *(unsigned short *) (buf + offset); if (signature == ESIFDV_ITEM_KEYS_REV0_SIGNATURE) { offset += sizeof(unsigned short); res = parse_gddv_key(buf + offset, size - offset, &end_offset); if (res != THD_SUCCESS) return res; offset += end_offset; } else if (signature == ESIFDV_HEADER_SIGNATURE) { thd_log_info("Got subobject in buf %p at %d\n", buf, offset); res = parse_gddv(buf + offset, size - offset, &end_offset); if (res != THD_SUCCESS) return res; /* Parse recursively */ offset += end_offset; thd_log_info("Subobject ended at %d of %d\n", offset, size); } else { thd_log_info("No known signature found 0x%04X\n", *(unsigned short *) (buf + offset)); return THD_ERROR; } } else { res = parse_gddv_key(buf + offset, size - offset, &end_offset); if (res != THD_SUCCESS) return res; offset += end_offset; } } if (end_offset) *end_offset = offset; return 0; } int cthd_gddv::verify_condition(struct condition condition) { const char *cond_name; if (condition.condition >= Oem0 && condition.condition <= Oem5) return 0; if (condition.condition >= adaptive_condition(0x1000) && condition.condition < adaptive_condition(0x10000)) return 0; if (condition.condition == Default) return 0; if (condition.condition == Temperature || condition.condition == Temperature_without_hysteresis || condition.condition == (adaptive_condition) 0) { return 0; } #ifndef ANDROID if (condition.condition == Lid_state && lid_dev != NULL) return 0; if (condition.condition == Power_source && upower_client != NULL) return 0; #endif if (condition.condition == Workload) return 0; if (condition.condition == Platform_type) return 0; if (condition.condition == Power_slider) return 0; if ( condition.condition >= ARRAY_SIZE(condition_names)) cond_name = "UNKNOWN"; else cond_name = condition_names[condition.condition]; thd_log_info("Unsupported condition %" PRIu64 " (%s)\n", condition.condition, cond_name); return THD_ERROR; } int cthd_gddv::verify_conditions() { int result = 0; for (int i = 0; i < (int) conditions.size(); i++) { for (int j = 0; j < (int) conditions[i].size(); j++) { if (verify_condition(conditions[i][j])) result = THD_ERROR; } } if (result != 0) thd_log_info("Unsupported conditions are present\n"); return result; } int cthd_gddv::compare_condition(struct condition condition, int value) { if (thd_engine && thd_engine->debug_mode_on()) { if (condition.condition < ARRAY_SIZE(condition_names)) { std::string cond_name, comp_str, op_str; cond_name = condition_names[condition.condition]; if (condition.comparison < ARRAY_SIZE(comp_strs)) { comp_str = comp_strs[condition.comparison]; thd_log_debug( "compare condition [%s] comparison [%s] value [%d]\n", cond_name.c_str(), comp_str.c_str(), value); } else { thd_log_debug( "compare condition [%s] comparison [%" PRIu64 "] value [%d]\n", cond_name.c_str(), condition.comparison, value); } } else { thd_log_debug("compare condition %" PRIu64 " value %d\n", condition.comparison, value); } } switch (condition.comparison) { case ADAPTIVE_EQUAL: if (value == condition.argument) return THD_SUCCESS; else return THD_ERROR; break; case ADAPTIVE_LESSER_OR_EQUAL: if (value <= condition.argument) return THD_SUCCESS; else return THD_ERROR; break; case ADAPTIVE_GREATER_OR_EQUAL: if (value >= condition.argument) return THD_SUCCESS; else return THD_ERROR; break; default: return THD_ERROR; } } int cthd_gddv::compare_time(struct condition condition) { int elapsed = time(NULL) - condition.state_entry_time; switch (condition.time_comparison) { case ADAPTIVE_EQUAL: if (elapsed == condition.time) return THD_SUCCESS; else return THD_ERROR; break; case ADAPTIVE_LESSER_OR_EQUAL: if (elapsed <= condition.time) return THD_SUCCESS; else return THD_ERROR; break; case ADAPTIVE_GREATER_OR_EQUAL: if (elapsed >= condition.time) return THD_SUCCESS; else return THD_ERROR; break; default: return THD_ERROR; } } int cthd_gddv::evaluate_oem_condition(struct condition condition) { csys_fs sysfs(int3400_base_path.c_str()); int oem_condition = -1; if (condition.condition >= Oem0 && condition.condition <= Oem5) oem_condition = (int) condition.condition - Oem0; else if (condition.condition >= (adaptive_condition) 0x1000 && condition.condition < (adaptive_condition) 0x10000) oem_condition = (int) condition.condition - 0x1000 + 6; if (oem_condition != -1) { std::string filename = "odvp" + std::to_string(oem_condition); std::string data; if (sysfs.read(filename, data) < 0) { thd_log_error("Unable to read %s\n", filename.c_str()); return THD_ERROR; } int value = std::stoi(data, NULL); return compare_condition(condition, value); } return THD_ERROR; } int cthd_gddv::evaluate_temperature_condition( struct condition condition) { std::string sensor_name; if (condition.ignore_condition) return THD_ERROR; size_t pos = condition.device.find_last_of("."); if (pos == std::string::npos) sensor_name = condition.device; else sensor_name = condition.device.substr(pos + 1); cthd_sensor *sensor = thd_engine->search_sensor(sensor_name); if (!sensor) { thd_log_info("Unable to find a sensor for %s\n", condition.device.c_str()); condition.ignore_condition = 1; return THD_ERROR; } int value = sensor->read_temperature(); // Conditions are specified in decikelvin, temperatures are in // millicelsius. value = value / 100 + 2732; return compare_condition(condition, value); } #ifdef ANDROID int cthd_gddv::evaluate_lid_condition(struct condition condition) { int value = 1; return compare_condition(condition, value); } #else int cthd_gddv::evaluate_lid_condition(struct condition condition) { int value = 0; if (lid_dev) { struct input_event ev; while (libevdev_has_event_pending(lid_dev)) libevdev_next_event(lid_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); int lid_closed = libevdev_get_event_value(lid_dev, EV_SW, SW_LID); value = !lid_closed; } return compare_condition(condition, value); } #endif int cthd_gddv::evaluate_workload_condition( struct condition condition) { // We don't have a good way to assert workload at the moment, so just // default to bursty return compare_condition(condition, 3); } #ifdef ANDROID /* * Platform Type * Clamshell(1) * Tablet(2) * Other/Invalid(0) * */ int cthd_gddv::evaluate_platform_type_condition( struct condition condition) { int value = 2;//Tablet return compare_condition(condition, value); } #else int cthd_gddv::evaluate_platform_type_condition( struct condition condition) { int value = 1; if (tablet_dev) { struct input_event ev; while (libevdev_has_event_pending(tablet_dev)) libevdev_next_event(tablet_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); int tablet = libevdev_get_event_value(tablet_dev, EV_SW, SW_TABLET_MODE); if (tablet) value = 2; } return compare_condition(condition, value); } #endif int cthd_gddv::evaluate_power_slider_condition( struct condition condition) { return compare_condition(condition, power_slider); } #ifdef ANDROID /* *Power Source AC(0) DC(1) Short Term DC(2) * */ int cthd_gddv::evaluate_ac_condition(struct condition condition) { csys_fs cdev_sysfs("/sys/class/power_supply/AC/online"); std::string buffer; int status = 0; int value = 0; thd_log_debug("evaluate evaluate_ac_condition %" PRIu64 "\n", condition.condition); if (cdev_sysfs.exists("")) { cdev_sysfs.read("", buffer); std::istringstream(buffer) >> status; thd_log_debug("evaluate found battery sys status=%d\n",status); } if (status!=1) { value = 1; } thd_log_debug("evaluate found battery sys value=%d\n",value); return compare_condition(condition, value); } #else int cthd_gddv::evaluate_ac_condition(struct condition condition) { int value = 0; bool on_battery = up_client_get_on_battery(upower_client); if (on_battery) value = 1; return compare_condition(condition, value); } #endif int cthd_gddv::evaluate_condition(struct condition condition) { int ret = THD_ERROR; if (condition.condition == Default) return THD_SUCCESS; thd_log_debug("evaluate condition.condition %" PRIu64 "\n", condition.condition); if ((condition.condition >= Oem0 && condition.condition <= Oem5) || (condition.condition >= (adaptive_condition) 0x1000 && condition.condition < (adaptive_condition) 0x10000)) ret = evaluate_oem_condition(condition); if (condition.condition == Temperature || condition.condition == Temperature_without_hysteresis || condition.condition == (adaptive_condition) 0) { ret = evaluate_temperature_condition(condition); } if (condition.condition == Lid_state) { ret = evaluate_lid_condition(condition); } if (condition.condition == Power_source) { ret = evaluate_ac_condition(condition); } if (condition.condition == Workload) { ret = evaluate_workload_condition(condition); } if (condition.condition == Platform_type) { ret = evaluate_platform_type_condition(condition); } if (condition.condition == Power_slider) { ret = evaluate_power_slider_condition(condition); } if (condition.condition == Motion) { thd_log_debug("Match motion == 0 :%d\n", condition.argument); if (condition.argument == 0) ret = THD_SUCCESS; } if (ret) { if (condition.time && condition.state_entry_time == 0) { condition.state_entry_time = time(NULL); } ret = compare_time(condition); } else { condition.state_entry_time = 0; } return ret; } int cthd_gddv::evaluate_condition_set( std::vector<struct condition> condition_set) { for (int i = 0; i < (int) condition_set.size(); i++) { thd_log_debug("evaluate condition.condition at index %d\n", i); if (evaluate_condition(condition_set[i]) != 0) return THD_ERROR; } return THD_SUCCESS; } int cthd_gddv::evaluate_conditions() { int target = -1; for (int i = 0; i < (int) conditions.size(); i++) { thd_log_debug("evaluate condition set %d\n", i); if (evaluate_condition_set(conditions[i]) == THD_SUCCESS) { target = conditions[i][0].target; thd_log_debug("Condition Set matched:%d target:%d\n", i, target); break; } } return target; } struct psvt* cthd_gddv::find_psvt(std::string name) { for (int i = 0; i < (int) psvts.size(); i++) { if (!strcasecmp(psvts[i].name.c_str(), name.c_str())) { return &psvts[i]; } } return NULL; } struct itmt* cthd_gddv::find_itmt(std::string name) { for (int i = 0; i < (int) itmts.size(); i++) { if (!strcasecmp(itmts[i].name.c_str(), name.c_str())) { return &itmts[i]; } } return NULL; } int cthd_gddv::find_agressive_target() { int max_pl1_max = 0; int max_target_id = -1; for (int i = 0; i < (int) targets.size(); i++) { int argument; if (targets[i].code != "PL1MAX" && targets[i].code != "PL1PowerLimit") continue; try { argument = std::stoi(targets[i].argument, NULL); } catch (...) { thd_log_info("Invalid target target:%s %s\n", targets[i].code.c_str(), targets[i].argument.c_str()); continue; } thd_log_info("target:%s %d\n", targets[i].code.c_str(), argument); if (max_pl1_max < argument) { max_pl1_max = argument; max_target_id = i; } } return max_target_id; } #ifndef ANDROID void cthd_gddv::update_power_slider() { g_autoptr(GVariant) active_profile_v = NULL; active_profile_v = g_dbus_proxy_get_cached_property (power_profiles_daemon, "ActiveProfile"); if (active_profile_v && g_variant_is_of_type (active_profile_v, G_VARIANT_TYPE_STRING)) { const char *active_profile = g_variant_get_string (active_profile_v, NULL); if (strcmp (active_profile, "power-saver") == 0) power_slider = 25; /* battery saver */ else if (strcmp (active_profile, "balanced") == 0) power_slider = 75; /* better performance */ else if (strcmp (active_profile, "performance") == 0) power_slider = 100; /* best performance */ else power_slider = 75; } else { power_slider = 75; } thd_log_info("Power slider is now set to %d\n", power_slider); } static void power_profiles_changed_cb(cthd_gddv *gddv) { gddv->update_power_slider(); } #endif #ifndef ANDROID static int is_event_device(const struct dirent *dir) { return strncmp("event", dir->d_name, 5) == 0; } void cthd_gddv::setup_input_devices() { struct dirent **namelist; int i, ndev, ret; ndev = scandir("/dev/input", &namelist, is_event_device, versionsort); for (i = 0; i < ndev; i++) { struct libevdev *dev = NULL; char fname[267]; int fd = -1; snprintf(fname, sizeof(fname), "/dev/input/%s", namelist[i]->d_name); fd = open(fname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) continue; ret = libevdev_new_from_fd(fd, &dev); if (ret) { close(fd); continue; } if (!tablet_dev && libevdev_has_event_code(dev, EV_SW, SW_TABLET_MODE)) tablet_dev = dev; if (!lid_dev && libevdev_has_event_code(dev, EV_SW, SW_LID)) lid_dev = dev; if (lid_dev != dev && tablet_dev != dev) { libevdev_free(dev); close(fd); } } } #endif int cthd_gddv::gddv_init(void) { csys_fs sysfs(""); char *buf; size_t size; if (sysfs.exists("/sys/bus/platform/devices/INT3400:00")) { int3400_base_path = "/sys/bus/platform/devices/INT3400:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1040:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1040:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1041:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1041:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC10A0:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC10A0:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1042:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1042:00/"; } else if (sysfs.exists("/sys/bus/platform/devices/INTC1068:00")) { int3400_base_path = "/sys/bus/platform/devices/INTC1068:00/"; } else { return THD_ERROR; } if (sysfs.read(int3400_base_path + "firmware_node/path", int3400_path) < 0) { thd_log_debug("Unable to locate INT3400 firmware path\n"); return THD_ERROR; } size = sysfs.size(int3400_base_path + "data_vault"); if (size == 0) { thd_log_debug("Unable to open GDDV data vault\n"); return THD_ERROR; } buf = new char[size]; if (!buf) { thd_log_error("Unable to allocate memory for GDDV"); return THD_FATAL_ERROR; } if (sysfs.read(int3400_base_path + "data_vault", buf, size) < int(size)) { thd_log_debug("Unable to read GDDV data vault\n"); delete[] buf; return THD_FATAL_ERROR; } try { if (parse_gddv(buf, size, NULL)) { thd_log_debug("Unable to parse GDDV"); delete[] buf; return THD_FATAL_ERROR; } merge_appc(); dump_ppcc(); dump_psvt(); dump_itmt(); dump_apat(); dump_apct(); dump_idsps(); dump_trips(); delete [] buf; } catch (std::exception &e) { thd_log_warn("%s\n", e.what()); delete [] buf; return THD_FATAL_ERROR; } #ifndef ANDROID setup_input_devices(); upower_client = up_client_new(); if (!upower_client) { thd_log_info("Unable to connect to upower\n"); /* But continue to work */ } g_autoptr(GDBusConnection) bus = NULL; bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL); if (bus) { power_profiles_daemon = g_dbus_proxy_new_sync (bus, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "net.hadess.PowerProfiles", "/net/hadess/PowerProfiles", "net.hadess.PowerProfiles", NULL, NULL); if (power_profiles_daemon) { g_signal_connect_swapped (power_profiles_daemon, "g-properties-changed", (GCallback) power_profiles_changed_cb, this); power_profiles_changed_cb(this); } else { thd_log_info("Could not setup DBus watch for power-profiles-daemon"); } } #endif return THD_SUCCESS; } void cthd_gddv::gddv_free(void) { destroy_dynamic_sources(); } 07070100000045000081A400000000000000000000000166853FAC00001920000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_gddv.h/* * cthd_engine_adaptive.cpp: Adaptive thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Copyright 2020 Google LLC * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name Matthew Garrett <mjg59@google.com> * */ #ifndef THD_GDDV_H_ #define THD_GDDV_H_ #ifndef ANDROID #include <libevdev/libevdev.h> #include <upower.h> #endif #include "thd_engine.h" #include "thd_trt_art_reader.h" #define DECI_KELVIN_TO_CELSIUS(t) ({ \ int _t = (t); \ ((_t-2732 >= 0) ? (_t-2732+5)/10 : (_t-2732-5)/10); \ }) enum adaptive_condition { Default = 0x01, Orientation, Proximity, Motion, Dock, Workload, Cooling_mode, Power_source, Aggregate_power_percentage, Lid_state, Platform_type, Platform_SKU, Utilisation, TDP, Duty_cycle, Power, Temperature, Display_orientation, Oem0, Oem1, Oem2, Oem3, Oem4, Oem5, PMAX, PSRC, ARTG, CTYP, PROP, Unk1, Unk2, Battery_state, Battery_rate, Battery_remaining, Battery_voltage, PBSS, Battery_cycles, Battery_last_full, Power_personality, Battery_design_capacity, Screen_state, AVOL, ACUR, AP01, AP02, AP10, Time, Temperature_without_hysteresis, Mixed_reality, User_presence, RBHF, VBNL, CMPP, Battery_percentage, Battery_count, Power_slider }; enum adaptive_comparison { ADAPTIVE_EQUAL = 0x01, ADAPTIVE_LESSER_OR_EQUAL, ADAPTIVE_GREATER_OR_EQUAL, }; enum adaptive_operation { AND = 0x01, FOR }; struct psv { std::string name; std::string source; std::string target; int priority; int sample_period; int temp; int domain; int control_knob; std::string limit; int step_size; int limit_coeff; int unlimit_coeff; }; struct condition { uint64_t condition; std::string device; uint64_t comparison; int argument; enum adaptive_operation operation; enum adaptive_comparison time_comparison; int time; int target; int state; int state_entry_time; int ignore_condition; }; struct custom_condition { enum adaptive_condition condition; std::string name; std::string participant; int domain; int type; }; struct psvt { std::string name; std::vector<struct psv> psvs; }; struct itmt_entry { std::string target; int trip_point; std::string pl1_min; std::string pl1_max; std::string unused; }; struct trt_entry { std::string source; std::string dest; int priority; int sample_rate; int resd0; int resd1; int resd2; int resd3; }; struct itmt { std::string name; std::vector<struct itmt_entry> itmt_entries; }; struct trippoint { std::string name; std::string type_str; trip_point_type_t type; int temp; }; class cthd_gddv { private: std::vector<ppcc_t> ppccs; std::vector<struct custom_condition> custom_conditions; std::vector<rel_object_t> rel_list; std::vector<struct psvt> psvts; std::vector<struct itmt> itmts; std::vector<std::string> idsps; std::vector<struct trippoint> trippoints; std::string int3400_path; #ifndef ANDROID UpClient *upower_client; GDBusProxy *power_profiles_daemon; struct libevdev *tablet_dev; struct libevdev *lid_dev; #endif std::string int3400_base_path; int power_slider; void destroy_dynamic_sources(); int get_type(char *object, int *offset); uint64_t get_uint64(char *object, int *offset); char* get_string(char *object, int *offset); int merge_custom(struct custom_condition *custom, struct condition *condition); int merge_appc(void); int parse_appc(char *appc, int len); int parse_apat(char *apat, int len); int parse_apct(char *apct, int len); int parse_ppcc(char *name, char *ppcc, int len); int parse_psvt(char *name, char *psvt, int len); int parse_itmt(char *name, char *itmt, int len); int parse_trt(char *trt, int len); void parse_idsp(char *name, char *idsp, int len); void parse_trip_point(char *name, char *type, char *val, int len); int handle_compressed_gddv(char *buf, int size); int parse_gddv_key(char *buf, int size, int *end_offset); int parse_gddv(char *buf, int size, int *end_offset); int verify_condition(struct condition condition); int compare_condition(struct condition condition, int value); int compare_time(struct condition condition); int evaluate_oem_condition(struct condition condition); int evaluate_temperature_condition(struct condition condition); int evaluate_ac_condition(struct condition condition); int evaluate_lid_condition(struct condition condition); int evaluate_workload_condition(struct condition condition); int evaluate_platform_type_condition(struct condition condition); int evaluate_power_slider_condition(struct condition condition); int evaluate_condition(struct condition condition); int evaluate_condition_set(std::vector<struct condition> condition_set); void exec_fallback_target(int target); void dump_apat(); void dump_apct(); void dump_ppcc(); void dump_psvt(); void dump_itmt(); void dump_idsps(); void dump_trips(); #ifndef ANDROID void setup_input_devices(); #endif int get_trip_temp(std::string name, trip_point_type_t type); public: #ifndef ANDROID cthd_gddv() : upower_client( NULL), power_profiles_daemon(NULL), tablet_dev(NULL), lid_dev(NULL), int3400_base_path(""), power_slider(75) { } #else cthd_gddv() : int3400_base_path(""), current_condition_set(0xffff) { } #endif ~cthd_gddv(); std::vector<std::vector<struct condition>> conditions; std::vector<struct adaptive_target> targets; ppcc_t* get_ppcc_param(std::string name); int gddv_init(void); void gddv_free(void); int verify_conditions(); int evaluate_conditions(); void update_power_slider(); int find_agressive_target(); struct psvt* find_psvt(std::string name); struct itmt* find_itmt(std::string name); struct psvt* find_def_psvt(); int search_idsp(std::string name); }; #endif /* THD_GDDV_H_ */ 07070100000046000081A400000000000000000000000166853FAC00000E0C000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_int3400.cpp/* * thd_int3400.cpp: Load and check INT3400 uuids for match * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_int3400.h" #include "thd_cdev_gen_sysfs.h" #include <iostream> #include <fstream> #include <iostream> #include <sstream> cthd_INT3400::cthd_INT3400(std::string _uuid) : uuid(_uuid), base_path("") { csys_fs cdev_sysfs(""); if (cdev_sysfs.exists("/sys/bus/acpi/devices/INT3400:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INT3400:00/physical_node/uuids/"; } else if (cdev_sysfs.exists("/sys/bus/acpi/devices/INTC1040:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INTC1040:00/physical_node/uuids/"; } else if (cdev_sysfs.exists("/sys/bus/acpi/devices/INTC1041:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INTC1041:00/physical_node/uuids/"; } else if (cdev_sysfs.exists("/sys/bus/acpi/devices/INTC10A0:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INTC10A0:00/physical_node/uuids/"; } else if (cdev_sysfs.exists("/sys/bus/acpi/devices/INTC1042:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INTC1042:00/physical_node/uuids/"; } else if (cdev_sysfs.exists("/sys/bus/acpi/devices/INTC1068:00/physical_node/uuids")) { base_path = "/sys/bus/acpi/devices/INTC1068:00/physical_node/uuids/"; } thd_log_info("INT3400 Base path is %s\n", base_path.c_str()); } int cthd_INT3400::match_supported_uuid() { if (base_path == "") return THD_ERROR; std::string filename = base_path + "available_uuids"; std::ifstream ifs(filename.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { thd_log_debug("uuid: %s\n", line.c_str()); if (line == uuid) return THD_SUCCESS; } ifs.close(); } return THD_ERROR; } void cthd_INT3400::set_default_uuid(void) { if (set_policy_osc() == THD_SUCCESS) return; if (base_path == "") return; std::string filename = base_path + "current_uuid"; std::ofstream ofs(filename.c_str(), std::ofstream::out); if (ofs.good()) { thd_log_info("Set Default UUID: %s\n", uuid.c_str()); ofs << uuid; } } int cthd_INT3400::set_policy_osc(void) { if (base_path == "") return THD_ERROR; std::string filename = base_path + "available_uuids"; std::ifstream ifs(filename.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; if (std::getline(ifs, line)) { thd_log_debug("available uuids: %s\n", line.c_str()); if (line == "UNKNOWN") { std::string _filename = base_path + "current_uuid"; std::ofstream ofs(_filename.c_str(), std::ofstream::out); if (ofs.good()) { std::string _uuid = "42A441D6-AE6A-462b-A84B-4A8CE79027D3"; thd_log_info("Set Default UUID: %s\n", _uuid.c_str()); ofs << _uuid; } return THD_SUCCESS; } } ifs.close(); } return THD_ERROR; } 07070100000047000081A400000000000000000000000166853FAC000004D0000000000000000000000000000000000000003600000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_int3400.h/* * thd_int3400.h: Load and check INT3400 uuids for match * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef SRC_THD_INT3400_UUID_H_ #define SRC_THD_INT3400_UUID_H_ #include "thd_common.h" #include <string> #include <vector> class cthd_INT3400 { private: std::string uuid; std::string base_path; int set_policy_osc(void); public: cthd_INT3400(std::string _uuid); int match_supported_uuid(void); void set_default_uuid(void); }; #endif /* SRC_THD_INT3400_UUID_H_ */ 07070100000048000081A400000000000000000000000166853FAC0000083F000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_kobj_uevent.cpp/* * thd_kobj_uevent.cpp: Get notification from kobj uevent * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_kobj_uevent.h" #include "thd_common.h" int cthd_kobj_uevent::kobj_uevent_open() { memset(&nls, 0, sizeof(struct sockaddr_nl)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (fd < 0) return fd; if (bind(fd, (struct sockaddr*) &nls, sizeof(struct sockaddr_nl))) { thd_log_warn("kob_uevent bin failed \n"); close(fd); return -1; } return fd; } void cthd_kobj_uevent::kobj_uevent_close() { close(fd); } bool cthd_kobj_uevent::check_for_event() { ssize_t i = 0; ssize_t len; const char *dev_path = "DEVPATH="; unsigned int dev_path_len = strlen(dev_path); char buffer[max_buffer_size]; len = recv(fd, buffer, sizeof(buffer) - 1, MSG_DONTWAIT); if (len <= 0) return false; buffer[len] = '\0'; while (i < len) { if (strlen(buffer + i) > dev_path_len && !strncmp(buffer + i, dev_path, dev_path_len)) { if (!strncmp(buffer + i + dev_path_len, device_path, strlen(device_path))) { return true; } } i += strlen(buffer + i) + 1; } return false; } void cthd_kobj_uevent::register_dev_path(char *path) { strncpy(device_path, path, max_buffer_size); device_path[max_buffer_size - 1] = '\0'; } 07070100000049000081A400000000000000000000000166853FAC000005CD000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_kobj_uevent.h/* * thd_kobj_uevent.h: Get notification from kobj uevent * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_KOBJ_UEVENT_H_ #define THD_KOBJ_UEVENT_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <poll.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <linux/types.h> #include <linux/netlink.h> class cthd_kobj_uevent { private: static const int max_buffer_size = 512; struct sockaddr_nl nls; int fd; char device_path[max_buffer_size]; public: cthd_kobj_uevent() { fd = 0; memset(&nls, 0, sizeof(nls)); device_path[0] = '\0'; } int kobj_uevent_open(); void kobj_uevent_close(); void register_dev_path(char *path); bool check_for_event(); } ; #endif 0707010000004A000081A400000000000000000000000166853FAC00001270000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_lzma_dec.cpp// From ESIF/Products/ESIF_CMP/Sources esif_cmp.c // Remove white spaces #include <errno.h> #include <inttypes.h> //#include <lzma.h> #include <linux/input.h> #include <sys/types.h> #include "LzmaDec.h" #include "thd_common.h" // Duplicate ESIF_CMP/Sources/Alloc.c MyAlloc void *MyAlloc(size_t size) { if (size == 0) return NULL; #ifdef _SZ_ALLOC_DEBUG { void *p = malloc(size); // PRINT_ALLOC("Alloc ", g_allocCount, size, p); return p; } #else return malloc(size); #endif } void MyFree(void *address) { //PRINT_FREE("Free ", g_allocCount, address); free(address); } #define UNUSED_VAR(x) (void)x; static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); } static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); } const ISzAlloc g_Alloc = { SzAlloc, SzFree }; // Standard LZMA File Header #define LZMA_PROPS_SIZE 5 // [XX YY YY YY YY] Where XX = Encoded -lc -lp -pb options, YY = Encoded -d option #pragma pack(push, 1) struct LzmaHeader { unsigned char properties[LZMA_PROPS_SIZE];// encoded LZMA_PROPS options unsigned long long original_size; // original uncompressed data size }; #pragma pack(pop) #define ENCODED_SIGNATURE {'\x5D','\x00'} /* Hardcoded LZMA Compression Property Values (and their LZMA_SDK v18.01 lzma.exe command line equivalents) * Items marked with "##" should never be changed since they affect the 5-byte LZMA Properties Header Signature * The following parameters correspond to to the ESIF_COMPRESS_SIGNATURE defined in esif_sdk_iface_compress.h, * which always maps to [5D 00 XX XX XX] for -lc3 -lp0 -pb2 and -d12 to -d27 lzma.exe options. */ #define LZMA_PROPS_LEVEL 9 // Compression Level [-a1 = 9] #define LZMA_PROPS_DICTSIZE (1 << 24) // Dictionary Size [-d24] ## #define LZMA_PROPS_LITCTXBITS 3 // Literal Context Bits [-lc3] ## #define LZMA_PROPS_LITPOSBITS 0 // Literal Pos Bits [-lp0] ## #define LZMA_PROPS_NUMPOSBITS 2 // Number of Pos Bits [-pb2] ## #define LZMA_PROPS_FASTBYTES 128 // Number of Fast Bytes [-fb128] #define LZMA_PROPS_THREADS 1 // Number of Threads [-mt1] #define LZMA_PADDING_MINSIZE 256 // Minimum Padding Bytes for Compression Buffer #define LZMA_PADDING_PERCENT 0.05 // Percent Padding Bytes for Compression Buffer (0.0-1.0) #define LZMA_MAX_COMPRESSED_SIZE (((size_t)(-1) >> 1) - 1) int lzma_decompress( unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen ) { int rc = THD_ERROR; struct LzmaHeader *header = NULL; // NULL dest = Return Required Buffer Size if (dest == NULL && destLen && src && srcLen > sizeof(*header)) { header = (struct LzmaHeader *)src; unsigned char encoded_signature[] = ENCODED_SIGNATURE; // Compute Original Decompressed Size if valid Header Properties if ((memcmp(header->properties, encoded_signature, sizeof(encoded_signature)) == 0) && (header->original_size > 0 && header->original_size != (unsigned long long)(-1))) { *destLen = (size_t)header->original_size; rc = 0; } else { rc = THD_ERROR; } } else if (dest && destLen && src && srcLen > sizeof(*header)) { header = (struct LzmaHeader *)src; size_t lzmaSrcLen = srcLen - sizeof(*header); ELzmaStatus status; rc = LzmaDecode( dest, destLen, src + sizeof(*header), &lzmaSrcLen, src, sizeof(*header), LZMA_FINISH_ANY, &status, &g_Alloc); // Validate Data not Truncated since LzmaDecode returns OK if destLen too small if (*destLen < header->original_size) { rc = THD_ERROR; } // Bounds Check if (*destLen > LZMA_MAX_COMPRESSED_SIZE) { *destLen = LZMA_MAX_COMPRESSED_SIZE; rc = THD_ERROR; } } return rc; } 0707010000004B000081A400000000000000000000000166853FAC000000E1000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_lzma_dec.h#include <errno.h> #include <inttypes.h> #include <linux/input.h> #include <sys/types.h> int lzma_decompress( unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen ); 0707010000004C000081A400000000000000000000000166853FAC00008751000000000000000000000000000000000000003600000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_parse.cpp/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* Parser to parse thermal configuration file. This uses libxml2 API. * */ #include "thd_parse.h" #include <stdlib.h> #include <algorithm> #include "thd_sys_fs.h" #include "thd_trt_art_reader.h" #define DEBUG_PARSER_PRINT(x,...) void cthd_parse::string_trim(std::string &str) { std::string chars = "\n \t\r"; for (unsigned int i = 0; i < chars.length(); ++i) { str.erase(std::remove(str.begin(), str.end(), chars[i]), str.end()); } } char *cthd_parse::char_trim(char *str) { int i; if (!str) return NULL; if (str[0] == '\0') return str; while (isspace(*str)) str++; for (i = strlen(str) - 1; (isspace(str[i])); i--) ; str[i + 1] = '\0'; return str; } cthd_parse::cthd_parse() : matched_thermal_info_index(-1), doc(NULL), root_element(NULL), auto_config( 0) { std::string name_conf = TDCONFDIR; std::string name_run = TDRUNDIR; #ifdef ANDROID filename = name_run + "/" + "thermal-conf.xml"; #else filename = name_conf + "/" + "thermal-conf.xml"; #endif filename_auto = name_run + "/" + "thermal-conf.xml.auto"; filename_auto_conf = name_conf + "/" + "thermal-conf.xml.auto"; } int cthd_parse::parser_init(std::string config_file) { cthd_acpi_rel rel; const char *xml_config_file; int ret; if (config_file.empty()) { std::ifstream conf_auto(filename_auto_conf.c_str()); if (conf_auto.is_open()) { thd_log_msg("Using generated %s \n", filename_auto_conf.c_str()); xml_config_file = filename_auto_conf.c_str(); auto_config = 1; } else { ret = rel.generate_conf(filename_auto); if (!ret) { thd_log_msg("Using generated %s\n", filename_auto.c_str()); xml_config_file = filename_auto.c_str(); auto_config = 1; } else { xml_config_file = filename.c_str(); } } } else { xml_config_file = config_file.c_str(); } /* We have not tested existence yet in this case. */ if (!auto_config) { std::ifstream conf(xml_config_file); if (!conf.is_open()) { thd_log_msg("Config file %s does not exist\n", xml_config_file); return THD_ERROR; } } thd_log_msg("Using config file %s\n", xml_config_file); doc = xmlReadFile(xml_config_file, NULL, 0); if (doc == NULL) { thd_log_warn("error: could not parse file %s\n", xml_config_file); return THD_ERROR; } root_element = xmlDocGetRootElement(doc); if (root_element == NULL) { thd_log_warn("error: could not get root element \n"); xmlFreeDoc(doc); return THD_ERROR; } return THD_SUCCESS; } int cthd_parse::parse_dependency_values(xmlNode * a_node, xmlDoc *doc, trip_cdev_depend_t *dependency) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "CoolingDeviceType")) { dependency->cdev.assign(char_trim(tmp_value)); } else if (!strcasecmp((const char*) cur_node->name, "CoolingDeviceState")) { dependency->state.assign(char_trim(tmp_value)); } dependency->dependency = 1; xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_trip_cdev(xmlNode * a_node, xmlDoc *doc, trip_cdev_t *trip_cdev) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "type")) { trip_cdev->type.assign((const char*) tmp_value); string_trim(trip_cdev->type); } else if (!strcasecmp((const char*) cur_node->name, "influence")) { trip_cdev->influence = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "SamplingPeriod")) { trip_cdev->sampling_period = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "TargetState")) { trip_cdev->target_state = atoi(tmp_value); trip_cdev->target_state_valid = 1; } else if(!strcasecmp((const char*) cur_node->name, "PidControl")) { pid_control_t pid_params; parse_pid_values(cur_node->children, doc, &pid_params); trip_cdev->pid_param.kp = pid_params.Kp; trip_cdev->pid_param.ki = pid_params.Ki; trip_cdev->pid_param.kd = pid_params.Kd; trip_cdev->pid_param.valid = 1; } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_trip_point(xmlNode * a_node, xmlDoc *doc, trip_point_t *trip_pt) { xmlNode *cur_node = NULL; char *tmp_value; trip_cdev_t trip_cdev; trip_pt->temperature = 0; trip_pt->trip_pt_type = ACTIVE; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!tmp_value) { continue; } if (!strcasecmp((const char*) cur_node->name, "Temperature")) { trip_pt->temperature = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Power")) { trip_pt->temperature = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Hyst")) { trip_pt->hyst = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "CoolingDevice")) { trip_cdev.influence = 0; trip_cdev.sampling_period = 0; trip_cdev.target_state_valid = 0; trip_cdev.target_state = 0; trip_cdev.type.clear(); trip_cdev.pid_param.valid = 0; trip_cdev.pid_param.kp = 0.0; trip_cdev.pid_param.ki = 0.0; trip_cdev.pid_param.kd = 0.0; parse_new_trip_cdev(cur_node->children, doc, &trip_cdev); trip_pt->cdev_trips.push_back(trip_cdev); } else if (!strcasecmp((const char*) cur_node->name, "SensorType")) { trip_pt->sensor_type.assign(tmp_value); string_trim(trip_pt->sensor_type); } else if (!strcasecmp((const char*) cur_node->name, "type")) { char *type_val = char_trim(tmp_value); if (type_val && !strcasecmp(type_val, "active")) trip_pt->trip_pt_type = ACTIVE; else if (type_val && !strcasecmp(type_val, "passive")) trip_pt->trip_pt_type = PASSIVE; else if (type_val && !strcasecmp(type_val, "critical")) trip_pt->trip_pt_type = CRITICAL; else if (type_val && !strcasecmp(type_val, "hot")) trip_pt->trip_pt_type = HOT; else if (type_val && !strcasecmp(type_val, "max")) trip_pt->trip_pt_type = MAX; } else if (!strcasecmp((const char*) cur_node->name, "ControlType")) { char *ctrl_val = char_trim(tmp_value); if (ctrl_val && !strcasecmp(ctrl_val, "SEQUENTIAL")) trip_pt->control_type = SEQUENTIAL; else trip_pt->control_type = PARALLEL; } else if (!strcasecmp((const char*) cur_node->name, "DependsOn")) { parse_dependency_values(cur_node->children, doc, &trip_pt->dependency); } xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_trip_points(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr) { xmlNode *cur_node = NULL; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "TripPoint")) { trip_point_t trip_pt; trip_pt.hyst = trip_pt.temperature = 0; trip_pt.trip_pt_type = PASSIVE; trip_pt.control_type = PARALLEL; trip_pt.influence = 100; trip_pt.sensor_type.clear(); trip_pt.dependency.dependency = 0; if (parse_new_trip_point(cur_node->children, doc, &trip_pt) == THD_SUCCESS) info_ptr->trip_pts.push_back(trip_pt); } } } return THD_SUCCESS; } int cthd_parse::parse_pid_values(xmlNode * a_node, xmlDoc *doc, pid_control_t *pid_ptr) { xmlNode *cur_node = NULL; char *tmp_value; pid_ptr->Kp = 0.0005; pid_ptr->Ki = 0.0001; pid_ptr->Kd = 0.0001; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "Kp")) { pid_ptr->Kp = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Kd")) { pid_ptr->Kd = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Ki")) { pid_ptr->Ki = atof(tmp_value); } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_zone(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "TripPoints")) { parse_trip_points(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "Type")) { info_ptr->type.assign((const char*) tmp_value); string_trim(info_ptr->type); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_cooling_dev(xmlNode * a_node, xmlDoc *doc, cooling_dev_t *cdev) { xmlNode *cur_node = NULL; char *tmp_value; cdev->max_state = cdev->min_state = 0; cdev->mask = 0; cdev->inc_dec_step = 1; cdev->read_back = true; cdev->auto_down_control = false; cdev->status = 0; cdev->pid_enable = false; cdev->unit_val = ABSOULUTE_VALUE; cdev->debounce_interval = 0; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char *) cur_node->name, "Index")) { cdev->index = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "Type")) { cdev->type_string.assign((const char*) tmp_value); string_trim(cdev->type_string); } else if (!strcasecmp((const char *) cur_node->name, "Path")) { cdev->mask |= CDEV_DEF_BIT_PATH; cdev->path_str.assign((const char*) tmp_value); string_trim(cdev->path_str); } else if (!strcasecmp((const char *) cur_node->name, "MinState")) { cdev->mask |= CDEV_DEF_BIT_MIN_STATE; cdev->min_state = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "MaxState")) { cdev->mask |= CDEV_DEF_BIT_MAX_STATE; cdev->max_state = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "IncDecStep")) { cdev->mask |= CDEV_DEF_BIT_STEP; cdev->inc_dec_step = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "ReadBack")) { cdev->mask |= CDEV_DEF_BIT_READ_BACK; cdev->read_back = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "DebouncePeriod")) { cdev->mask |= CDEV_DEF_BIT_DEBOUNCE_VAL; cdev->debounce_interval = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "PidControl")) { cdev->mask |= CDEV_DEF_BIT_PID_PARAMS; cdev->pid_enable = true; parse_pid_values(cur_node->children, doc, &cdev->pid); } else if (!strcasecmp((const char *) cur_node->name, "AutoOffMode")) { cdev->mask |= CDEV_DEF_BIT_AUTO_DOWN; if (atoi(tmp_value)) cdev->auto_down_control = true; else cdev->auto_down_control = false; } else if (!strcasecmp((const char *) cur_node->name, "WritePrefix")){ cdev->mask |= CDEV_DEF_BIT_WRITE_PREFIX; cdev->write_prefix.assign((const char*) tmp_value); } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_cooling_devs(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; cooling_dev_t cdev; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "CoolingDevice")) { cdev.index = cdev.max_state = cdev.min_state = 0; cdev.inc_dec_step = 1; cdev.auto_down_control = false; cdev.path_str.clear(); cdev.type_string.clear(); parse_new_cooling_dev(cur_node->children, doc, &cdev); info_ptr->cooling_devs.push_back(cdev); } } } return THD_SUCCESS; } int cthd_parse::parse_thermal_zones(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; thermal_zone_t zone; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalZone")) { zone.trip_pts.clear(); parse_new_zone(cur_node->children, doc, &zone); info_ptr->zones.push_back(zone); } } } return THD_SUCCESS; } int cthd_parse::parse_new_sensor_link(xmlNode * a_node, xmlDoc *doc, thermal_sensor_link_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "SensorType")) { info_ptr->name.assign(tmp_value); string_trim(info_ptr->name); } else if (!strcasecmp((const char*) cur_node->name, "Multiplier")) { info_ptr->multiplier = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Offset")) { info_ptr->offset = atof(tmp_value); } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_sensor(xmlNode * a_node, xmlDoc *doc, thermal_sensor_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "Type")) { info_ptr->name.assign(tmp_value); string_trim(info_ptr->name); } else if (!strcasecmp((const char*) cur_node->name, "Path")) { info_ptr->mask |= SENSOR_DEF_BIT_PATH; info_ptr->path.assign(tmp_value); string_trim(info_ptr->path); } else if (!strcasecmp((const char*) cur_node->name, "AsyncCapable")) { info_ptr->async_capable = atoi(tmp_value); info_ptr->mask |= SENSOR_DEF_BIT_ASYNC_CAPABLE; } else if (!strcasecmp((const char*) cur_node->name, "Virtual")) { info_ptr->virtual_sensor = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "SensorLink")) { parse_new_sensor_link(cur_node->children, doc, &info_ptr->sensor_link); } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_thermal_sensors(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; thermal_sensor_t sensor; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalSensor")) { sensor.name.clear(); sensor.path.clear(); sensor.async_capable = false; sensor.mask = 0; sensor.virtual_sensor = false; parse_new_sensor(cur_node->children, doc, &sensor); info_ptr->sensors.push_back(sensor); } } } return THD_SUCCESS; } int cthd_parse::parse_new_platform_info(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; info_ptr->default_preference = PREF_ENERGY_CONSERVE; info_ptr->polling_interval = 0; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!tmp_value) { continue; } if (!strcasecmp((const char*) cur_node->name, "uuid")) { info_ptr->uuid.assign((const char*) tmp_value); string_trim(info_ptr->uuid); } else if (!strcasecmp((const char*) cur_node->name, "ProductName")) { info_ptr->product_name.assign((const char*) tmp_value); string_trim(info_ptr->product_name); } else if (!strcasecmp((const char*) cur_node->name, "ProductSku")) { info_ptr->product_sku.assign((const char*) tmp_value); string_trim(info_ptr->product_sku); } else if (!strcasecmp((const char*) cur_node->name, "Name")) { info_ptr->name.assign((const char*) tmp_value); string_trim(info_ptr->name); } else if (!strcasecmp((const char*) cur_node->name, "Preference")) { char *pref_val = char_trim(tmp_value); if (pref_val && !strcasecmp(pref_val, "PERFORMANCE")) info_ptr->default_preference = PREF_PERFORMANCE; else info_ptr->default_preference = PREF_ENERGY_CONSERVE; } else if (!strcasecmp((const char*) cur_node->name, "ThermalZones")) { parse_thermal_zones(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "ThermalSensors")) { parse_thermal_sensors(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "CoolingDevices")) { parse_cooling_devs(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "PollingInterval")) { info_ptr->polling_interval = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "PPCC")) { parse_ppcc(cur_node->children, doc, &info_ptr->ppcc); info_ptr->ppcc.valid = 1; } xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_platform(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; unsigned char *tmp_value; thermal_info_t info; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (unsigned char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "Platform")) { info.cooling_devs.clear(); info.zones.clear(); info.polling_interval = 0; info.ppcc.valid = 0; info.ppcc.limit_1_valid = 0; parse_new_platform_info(cur_node->children, doc, &info); thermal_info_list.push_back(info); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_ppcc(xmlNode * a_node, xmlDoc *doc, ppcc_t *ppcc) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { if (!strcasecmp((const char*) cur_node->name, "PowerLimitMinimum")) { ppcc->power_limit_min = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "PowerLimitMaximum")) { ppcc->power_limit_max = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "TimeWindowMinimum")) { ppcc->time_wind_min = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "TimeWindowMaximum")) { ppcc->time_wind_max = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "StepSize")) { ppcc->step_size = atof(tmp_value); } xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_thermal_conf(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalConfiguration")) { parse_new_platform(cur_node->children, doc, info_ptr); } } } return THD_SUCCESS; } int cthd_parse::parse(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; thermal_info_t info; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalConfiguration")) { parse_new_platform(cur_node->children, doc, &info); } } } return THD_SUCCESS; } int cthd_parse::start_parse() { parse(root_element, doc); return THD_SUCCESS; } void cthd_parse::parser_deinit() { for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { thermal_info_list[i].sensors.clear(); for (unsigned int j = 0; j < thermal_info_list[i].zones.size(); ++j) { thermal_info_list[i].zones[j].trip_pts.clear(); } thermal_info_list[i].zones.clear(); thermal_info_list[i].cooling_devs.clear(); } xmlFreeDoc(doc); } void cthd_parse::dump_thermal_conf() { thd_log_info(" Dumping parsed XML Data\n"); for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { thd_log_info(" *** Index %u ***\n", i); thd_log_info("Name: %s\n", thermal_info_list[i].name.c_str()); thd_log_info("Product Name: %s\n", thermal_info_list[i].product_name.c_str()); thd_log_info("Product SKU: %s\n", thermal_info_list[i].product_sku.c_str()); thd_log_info("UUID: %s\n", thermal_info_list[i].uuid.c_str()); thd_log_info("type: %d\n", thermal_info_list[i].default_preference); thd_log_info("Polling Interval: %d seconds\n", thermal_info_list[i].polling_interval); if (thermal_info_list[i].ppcc.valid) { thd_log_info("PPCC: max_power_limit %d\n", thermal_info_list[i].ppcc.power_limit_max); thd_log_info("PPCC: min_power_limit %d\n", thermal_info_list[i].ppcc.power_limit_min); thd_log_info("PPCC: time_window_max %d\n", thermal_info_list[i].ppcc.time_wind_max); thd_log_info("PPCC: time_window_min %d\n", thermal_info_list[i].ppcc.time_wind_min); thd_log_info("PPCC: step_size %d\n", thermal_info_list[i].ppcc.step_size); } for (unsigned int j = 0; j < thermal_info_list[i].sensors.size(); ++j) { thd_log_info("\tSensor %u \n", j); thd_log_info("\t Name: %s\n", thermal_info_list[i].sensors[j].name.c_str()); thd_log_info("\t Path: %s\n", thermal_info_list[i].sensors[j].path.c_str()); thd_log_info("\t Async Capable: %d\n", thermal_info_list[i].sensors[j].async_capable); thd_log_info("\t Virtual: %d\n", thermal_info_list[i].sensors[j].virtual_sensor); if (thermal_info_list[i].sensors[j].virtual_sensor) { thd_log_info("\t\t Link type: %s\n", thermal_info_list[i].sensors[j].sensor_link.name.c_str()); thd_log_info("\t\t Link mult: %f\n", thermal_info_list[i].sensors[j].sensor_link.multiplier); thd_log_info("\t\t Link offset: %f\n", thermal_info_list[i].sensors[j].sensor_link.offset); } } for (unsigned int j = 0; j < thermal_info_list[i].zones.size(); ++j) { thd_log_info("\tZone %u \n", j); thd_log_info("\t Name: %s\n", thermal_info_list[i].zones[j].type.c_str()); for (unsigned int k = 0; k < thermal_info_list[i].zones[j].trip_pts.size(); ++k) { thd_log_info("\t\t Trip Point %u \n", k); thd_log_info("\t\t temp/power %d \n", thermal_info_list[i].zones[j].trip_pts[k].temperature); thd_log_info("\t\t trip type %d \n", thermal_info_list[i].zones[j].trip_pts[k].trip_pt_type); thd_log_info("\t\t hyst id %d \n", thermal_info_list[i].zones[j].trip_pts[k].hyst); thd_log_info("\t\t sensor type %s \n", thermal_info_list[i].zones[j].trip_pts[k].sensor_type.c_str()); if (thermal_info_list[i].zones[j].trip_pts[k].dependency.dependency) thd_log_info("\t\t Dependency on %s:%s \n", thermal_info_list[i].zones[j].trip_pts[k].dependency.cdev.c_str(), thermal_info_list[i].zones[j].trip_pts[k].dependency.state.c_str()); for (unsigned int l = 0; l < thermal_info_list[i].zones[j].trip_pts[k].cdev_trips.size(); ++l) { thd_log_info("\t\t cdev index %u \n", l); thd_log_info("\t\t\t type %s \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].type.c_str()); thd_log_info("\t\t\t influence %d \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].influence); thd_log_info("\t\t\t SamplingPeriod %d \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].sampling_period); if (thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].target_state_valid) thd_log_info("\t\t\t TargetState %d \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].target_state); if (thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].pid_param.valid) thd_log_info("\t\t\t PID values %f:%f:%f \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].pid_param.kp, thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].pid_param.ki, thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].pid_param.kd); } } } for (unsigned int l = 0; l < thermal_info_list[i].cooling_devs.size(); ++l) { thd_log_info("\tCooling Dev %u \n", l); thd_log_info("\t\tType: %s\n", thermal_info_list[i].cooling_devs[l].type_string.c_str()); thd_log_info("\t\tPath: %s\n", thermal_info_list[i].cooling_devs[l].path_str.c_str()); thd_log_info("\t\tMin: %d\n", thermal_info_list[i].cooling_devs[l].min_state); thd_log_info("\t\tMax: %d\n", thermal_info_list[i].cooling_devs[l].max_state); thd_log_info("\t\tStep: %d\n", thermal_info_list[i].cooling_devs[l].inc_dec_step); thd_log_info("\t\tReadBack: %d\n", thermal_info_list[i].cooling_devs[l].read_back); thd_log_info("\t\tDebouncePeriod: %d\n", thermal_info_list[i].cooling_devs[l].debounce_interval); thd_log_info("\t\tAutoDownControl: %d\n", thermal_info_list[i].cooling_devs[l].auto_down_control); if (thermal_info_list[i].cooling_devs[l].pid_enable) { thd_log_info("\t PID: Kp %f\n", thermal_info_list[i].cooling_devs[l].pid.Kp); thd_log_info("\t PID: Ki %f\n", thermal_info_list[i].cooling_devs[l].pid.Ki); thd_log_info("\t PID: Kd %f\n", thermal_info_list[i].cooling_devs[l].pid.Kd); } } } } int cthd_parse::get_polling_interval() { return thermal_info_list[matched_thermal_info_index].polling_interval; } bool cthd_parse::match_product_sku(int index) { std::string line; std::ifstream product_sku("/sys/class/dmi/id/product_sku"); if (product_sku.is_open() && getline(product_sku, line)) { if (!thermal_info_list[index].product_sku.size()) return true; string_trim(line); thd_log_debug("config product sku [%s] match with [%s]\n", thermal_info_list[index].product_sku.c_str(), line.c_str()); if (thermal_info_list[index].product_sku == "*") { thd_log_info("Product Sku matched [wildcard]\n"); return true; } if (line == thermal_info_list[index].product_sku) { thd_log_info("Product Sku matched \n"); return true; } } else { return true; } return false; } bool cthd_parse::platform_matched() { std::string line; std::ifstream product_uuid("/sys/class/dmi/id/product_uuid"); if (product_uuid.is_open() && getline(product_uuid, line)) { for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { if (!thermal_info_list[i].uuid.size()) continue; string_trim(line); thd_log_debug("config product uuid [%s] match with [%s]\n", thermal_info_list[i].uuid.c_str(), line.c_str()); if (thermal_info_list[i].uuid == "*") { matched_thermal_info_index = i; thd_log_info("UUID matched [wildcard]\n"); return true; } if (line == thermal_info_list[i].uuid) { matched_thermal_info_index = i; thd_log_info("UUID matched \n"); return true; } } } std::ifstream product_name("/sys/class/dmi/id/product_name"); if (product_name.is_open() && getline(product_name, line)) { for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { if (!thermal_info_list[i].product_name.size()) continue; string_trim(line); thd_log_debug("config product name [%s] match with [%s]\n", thermal_info_list[i].product_name.c_str(), line.c_str()); if (thermal_info_list[i].product_name == "*") { matched_thermal_info_index = i; thd_log_info("Product Name matched [wildcard]\n"); return true; } if (line == thermal_info_list[i].product_name) { if (!match_product_sku(i)) continue; matched_thermal_info_index = i; thd_log_info("Product Name matched \n"); return true; } } } for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { if (!thermal_info_list[i].uuid.size()) continue; if (!thermal_info_list[i].product_name.compare(0, 1, "*")) { matched_thermal_info_index = i; thd_log_info("Product Name matched \n"); return true; } } return false; } int cthd_parse::trip_count(unsigned int zone_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) { return thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts.size(); } else return -1; } trip_point_t* cthd_parse::get_trip_point(unsigned int zone_index, unsigned int trip_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) { if (trip_index < thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts.size()) return &thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts[trip_index]; return NULL; } else return NULL; } cooling_dev_t* cthd_parse::get_cool_dev_index(unsigned int cdev_index) { if (cdev_index < thermal_info_list[matched_thermal_info_index].cooling_devs.size()) return &thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index]; else return NULL; } thermal_sensor_t* cthd_parse::get_sensor_dev_index(unsigned int sensor_index) { if (sensor_index < thermal_info_list[matched_thermal_info_index].sensors.size()) return &thermal_info_list[matched_thermal_info_index].sensors[sensor_index]; else return NULL; } thermal_zone_t *cthd_parse::get_zone_dev_index(unsigned int zone_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) return &thermal_info_list[matched_thermal_info_index].zones[zone_index]; else return NULL; } ppcc_t *cthd_parse::get_ppcc_param(std::string name) { if (name != "TCPU.D0") return NULL; if (matched_thermal_info_index >= 0 && thermal_info_list[matched_thermal_info_index].ppcc.valid) return &thermal_info_list[matched_thermal_info_index].ppcc; return NULL; } bool cthd_parse::pid_status(int cdev_index) { return thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid_enable; } bool cthd_parse::get_pid_values(int cdev_index, int *Kp, int *Ki, int *Kd) { if (thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid_enable) { *Kp = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Kp; *Kd = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Kd; *Ki = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Ki; return true; } return false; } int cthd_parse::set_default_preference() { cthd_preference thd_pref; int ret; if (thermal_info_list[matched_thermal_info_index].default_preference == PREF_PERFORMANCE) ret = thd_pref.set_preference("PERFORMANCE"); else ret = thd_pref.set_preference("ENERGY_CONSERVE"); return ret; } 0707010000004D000081A400000000000000000000000166853FAC0000199C000000000000000000000000000000000000003400000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_parse.h/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_PARSE_H #define THD_PARSE_H #include <string> #include <vector> #include <libxml/parser.h> #include <libxml/tree.h> #include "thermald.h" #include "thd_trip_point.h" #define CDEV_DEF_BIT_MIN_STATE 0x0001 #define CDEV_DEF_BIT_MAX_STATE 0x0002 #define CDEV_DEF_BIT_STEP 0x0004 #define CDEV_DEF_BIT_READ_BACK 0x0008 #define CDEV_DEF_BIT_AUTO_DOWN 0x0010 #define CDEV_DEF_BIT_PATH 0x0020 #define CDEV_DEF_BIT_STATUS 0x0040 #define CDEV_DEF_BIT_UNIT_VAL 0x0080 #define CDEV_DEF_BIT_DEBOUNCE_VAL 0x0100 #define CDEV_DEF_BIT_PID_PARAMS 0x0200 #define CDEV_DEF_BIT_WRITE_PREFIX 0x0400 #define SENSOR_DEF_BIT_PATH 0x0001 #define SENSOR_DEF_BIT_ASYNC_CAPABLE 0x0002 typedef struct { double Kp; double Ki; double Kd; } pid_control_t; typedef struct { std::string name; double multiplier; double offset; } thermal_sensor_link_t; typedef struct { unsigned int mask; std::string name; std::string path; bool async_capable; bool virtual_sensor; thermal_sensor_link_t sensor_link; } thermal_sensor_t; typedef struct { int dependency; std::string cdev; std::string state; }trip_cdev_depend_t; typedef struct { std::string type; int influence; int sampling_period; int target_state_valid; int target_state; pid_param_t pid_param; } trip_cdev_t; typedef struct { int temperature; int hyst; trip_point_type_t trip_pt_type; trip_control_type_t control_type; int influence; std::string sensor_type; trip_cdev_depend_t dependency; std::vector<trip_cdev_t> cdev_trips; } trip_point_t; typedef struct { std::string type; std::vector<trip_point_t> trip_pts; } thermal_zone_t; typedef enum { ABSOULUTE_VALUE, RELATIVE_PERCENTAGES } unit_value_t; typedef struct { bool status; unsigned int mask; // Fields which are present in config int index; unit_value_t unit_val; int min_state; int max_state; int inc_dec_step; bool read_back; // For some device read back current state is not possible bool auto_down_control; std::string type_string; std::string path_str; int debounce_interval; bool pid_enable; pid_control_t pid; std::string write_prefix; } cooling_dev_t; typedef struct { std::string name; int valid; int power_limit_min; int power_limit_max; int time_wind_min; int time_wind_max; int step_size; int limit_1_valid; int power_limit_1_min; int power_limit_1_max; int time_wind_1_min; int time_wind_1_max; int step_1_size; } ppcc_t; typedef struct { std::string name; std::string uuid; std::string product_name; std::string product_sku; int default_preference; int polling_interval; ppcc_t ppcc; std::vector<thermal_sensor_t> sensors; std::vector<thermal_zone_t> zones; std::vector<cooling_dev_t> cooling_devs; } thermal_info_t; class cthd_parse { private: std::string filename; std::string filename_auto; std::string filename_auto_conf; std::vector<thermal_info_t> thermal_info_list; int matched_thermal_info_index; xmlDoc *doc; xmlNode *root_element; int auto_config; int parse(xmlNode * a_node, xmlDoc *doc); int parse_pid_values(xmlNode * a_node, xmlDoc *doc, pid_control_t *pid_ptr); int parse_dependency_values(xmlNode * a_node, xmlDoc *doc, trip_cdev_depend_t *dependency); int parse_new_trip_cdev(xmlNode * a_node, xmlDoc *doc, trip_cdev_t *trip_cdev); int parse_new_thermal_conf(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); int parse_new_platform_info(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); int parse_new_zone(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr); int parse_new_cooling_dev(xmlNode * a_node, xmlDoc *doc, cooling_dev_t *info_ptr); int parse_new_trip_point(xmlNode * a_node, xmlDoc *doc, trip_point_t *trip_pt); int parse_thermal_zones(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_new_sensor(xmlNode * a_node, xmlDoc *doc, thermal_sensor_t *info_ptr); int parse_new_sensor_link(xmlNode * a_node, xmlDoc *doc, thermal_sensor_link_t *info_ptr); int parse_thermal_sensors(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_cooling_devs(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_trip_points(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr); int parse_new_platform(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); int parse_ppcc(xmlNode * a_node, xmlDoc *doc, ppcc_t *ppcc); void string_trim(std::string &str); char *char_trim(char *trim); bool match_product_sku(int index); public: cthd_parse(); int parser_init(std::string config_file); void parser_deinit(); int start_parse(); void dump_thermal_conf(); bool platform_matched(); int get_polling_interval(); ppcc_t *get_ppcc_param(std::string name); int zone_count() { return thermal_info_list[matched_thermal_info_index].zones.size(); } int cdev_count() { return thermal_info_list[matched_thermal_info_index].cooling_devs.size(); } int sensor_count() { return thermal_info_list[matched_thermal_info_index].sensors.size(); } int thermal_conf_auto() { return auto_config; } int thermal_matched_platform_index() { return matched_thermal_info_index; } int set_default_preference(); int trip_count(unsigned int zone_index); bool pid_status(int cdev_index); bool get_pid_values(int cdev_index, int *Kp, int *Ki, int *Kd); trip_point_t *get_trip_point(unsigned int zone_index, unsigned int trip_index); cooling_dev_t *get_cool_dev_index(unsigned int cdev_index); thermal_sensor_t *get_sensor_dev_index(unsigned int sensor_index); thermal_zone_t *get_zone_dev_index(unsigned int zone_index); // std::string get_sensor_path(int zone_index) { // return thermal_info_list[matched_thermal_info_index].zones[zone_index].path; // } }; #endif 0707010000004E000081A400000000000000000000000166853FAC000007FC000000000000000000000000000000000000003400000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_pid.cpp/* * thd_pid.cpp: pid implementation * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_pid.h" cthd_pid::cthd_pid() { kp = 0.0005; ki = kd = 0.0001; last_time = 0; err_sum = 0.0; last_err = 0.0; target_temp = 0; } int cthd_pid::pid_output(unsigned int curr_temp, int initial_value) { double output; double d_err = 0; int error = curr_temp - target_temp; time_t now; time(&now); if (last_time == 0) { last_time = now; /* Initialize integrative component (err_sum) so that current * output is the initial_value. * d_err must be assumed to be zero for this */ if (ki) err_sum = (initial_value - kp * error) / ki; else err_sum = 0; } time_t timeChange = (now - last_time); thd_log_debug("pid_output error %d %g:%g\n", error, kp, kp * error); err_sum += (error * timeChange); if (timeChange) d_err = (error - last_err) / timeChange; else d_err = 0.0; /*Compute PID Output*/ output = kp * error + ki * err_sum + kd * d_err; thd_log_debug("pid %d:%d:%d:%d\n", (int) output, (int) (kp * error), (int) (ki * err_sum), (int) (kd * d_err)); /*Remember some variables for next time*/ last_err = error; last_time = now; thd_log_debug("pid_output %d:%d %g:%d\n", curr_temp, target_temp, output, (int) output); return (int) output; } 0707010000004F000081A400000000000000000000000166853FAC00000568000000000000000000000000000000000000003200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_pid.h/* * thd_pid.h: pid interface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thermald.h" #include <time.h> typedef struct { int valid; double kp; double ki; double kd; }pid_param_t; class cthd_pid { private: double err_sum, last_err; time_t last_time; unsigned int target_temp; public: cthd_pid(); double kp, ki, kd; void set_pid_param(double _kp, double _ki, double _kd) { kp = _kp; ki = _ki; kd = _kd; } int pid_output(unsigned int curr_temp, int initial_value = 0); void set_target_temp(unsigned int temp) { target_temp = temp; } void reset() { err_sum = last_err = last_time = 0; } }; 07070100000050000081A400000000000000000000000166853FAC00000F20000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_preference.cpp/* * thd_preference.cpp: Thermal preference class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_preference.h" cthd_preference::cthd_preference() : preference(PREF_ENERGY_CONSERVE), old_preference(0) { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_ENERGY_CONSERVE; } else { ifs >> preference; } ifs.close(); } std::string cthd_preference::int_pref_to_string(int pref) { std::string perf_str; switch (preference) { case PREF_PERFORMANCE: perf_str = "PERFORMANCE"; break; case PREF_ENERGY_CONSERVE: perf_str = "ENERGY_CONSERVE"; break; case PREF_DISABLED: perf_str = "DISABLE"; break; default: perf_str = "INVALID"; break; } return perf_str; } int cthd_preference::string_pref_to_int(std::string &pref_str) { int pref; if (pref_str == "PERFORMANCE") pref = PREF_PERFORMANCE; else if (pref_str == "ENERGY_CONSERVE") pref = PREF_ENERGY_CONSERVE; else if (pref_str == "DISABLE") pref = PREF_DISABLED; else pref = PREF_PERFORMANCE; return pref; } std::string cthd_preference::get_preference_str() { return int_pref_to_string(preference); } const char *cthd_preference::get_preference_cstr() { return strdup(int_pref_to_string(preference).c_str()); } int cthd_preference::get_preference() { return preference; } void cthd_preference::refresh() { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_ENERGY_CONSERVE; } else { ifs >> preference; } ifs.close(); } bool cthd_preference::set_preference(const char *pref_str) { std::string str(pref_str); int pref = string_pref_to_int(str); std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ofstream fout(filename.str().c_str()); if (!fout.good()) { return false; } fout << pref; fout.close(); // Save the old preference old_preference = preference; std::stringstream filename_save; filename_save << TDRUNDIR << "/" << "thd_preference.conf.save"; std::ofstream fout_save(filename_save.str().c_str()); if (!fout_save.good()) { return false; } fout_save << old_preference; fout_save.close(); std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_PERFORMANCE; } else { //ifs.read(reinterpret_cast < char * > (&preference), sizeof(preference)); ifs >> preference; } ifs.close(); thd_log_debug("old_preference %d new preference %d\n", old_preference, preference); return true; } int cthd_preference::get_old_preference() { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf.save"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { old_preference = PREF_PERFORMANCE; } else { //ifs.read(reinterpret_cast < char * > (&preference), sizeof(preference)); ifs >> old_preference; } ifs.close(); return old_preference; } 07070100000051000081A400000000000000000000000166853FAC000005CA000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_preference.h/* * thd_preference.h: Thermal preference class interface file * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_PREFERENCE_H #define THD_PREFERENCE_H #include "thd_common.h" #include <string> #include <cstring> #include <sstream> #include <iostream> #include <fstream> enum { PREF_ENERGY_CONSERVE, PREF_PERFORMANCE, PREF_DISABLED }; class cthd_preference { private: int preference; int old_preference; int string_pref_to_int(std::string &pref_str); std::string int_pref_to_string(int pref); public: cthd_preference(); bool set_preference(const char *pref); std::string get_preference_str(); const char *get_preference_cstr(); int get_preference(); int get_old_preference(); void refresh(); }; #endif 07070100000052000081A400000000000000000000000166853FAC00002190000000000000000000000000000000000000004100000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_rapl_power_meter.cpp/* * thd_rapl_power_meter.cpp: thermal cooling class implementation * using RAPL * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_rapl_power_meter.h" #include <dirent.h> #include <fnmatch.h> #include <time.h> static void *rapl_periodic_callback(void *data) { cthd_rapl_power_meter *rapl_cl = (cthd_rapl_power_meter*) data; for (;;) { if (!rapl_cl->rapl_energy_loop()) break; sleep(rapl_cl->rapl_callback_timeout); } return NULL; } cthd_rapl_power_meter::cthd_rapl_power_meter(unsigned int mask) : rapl_present(true), rapl_sysfs("/sys/class/powercap/intel-rapl/"), domain_list( 0), last_time(0), poll_thread(0), measure_mask(mask), enable_measurement( false) { thd_attr = pthread_attr_t(); if (rapl_sysfs.exists()) { thd_log_debug("RAPL sysfs present \n"); rapl_present = true; last_time = time(NULL); rapl_read_domains(rapl_sysfs.get_base_path()); } else { thd_log_warn("NO RAPL sysfs present \n"); rapl_present = false; } } void cthd_rapl_power_meter::rapl_read_domains(const char *dir_name) { int count = 0; csys_fs sys_fs; if (rapl_present) { DIR *dir; struct dirent *dir_entry; thd_log_debug("RAPL base path %s\n", dir_name); if ((dir = opendir(dir_name)) != NULL) { while ((dir_entry = readdir(dir)) != NULL) { std::string buffer; std::stringstream path; int status; rapl_domain_t domain; domain.half_way = 0; domain.energy_counter = 0; domain.energy_cumulative_counter = 0; domain.max_energy_range = 0; domain.max_energy_range_threshold = 0; domain.power = 0; domain.max_power = 0; domain.min_power = 0; domain.type = INVALID; if (!strcmp(dir_entry->d_name, ".") || !strcmp(dir_entry->d_name, "..")) continue; thd_log_debug("RAPL domain dir %s\n", dir_entry->d_name); path << dir_name << dir_entry->d_name << "/" << "name"; if (!sys_fs.exists(path.str())) { thd_log_debug(" %s doesn't exist\n", path.str().c_str()); continue; } status = sys_fs.read(path.str(), buffer); if (status < 0) continue; thd_log_debug("name %s\n", buffer.c_str()); if (fnmatch("package-*", buffer.c_str(), 0) == 0) { domain.type = PACKAGE; std::stringstream path; path << dir_name << dir_entry->d_name << "/"; rapl_read_domains(path.str().c_str()); } else if (buffer == "core") { domain.type = CORE; } else if (buffer == "uncore") { domain.type = UNCORE; } else if (buffer == "dram") { domain.type = DRAM; } if (measure_mask & domain.type) { domain.name = buffer; domain.path = std::string(dir_name) + std::string(dir_entry->d_name); domain_list.push_back(domain); ++count; } } closedir(dir); } else { thd_log_debug("opendir failed %s :%s\n", strerror(errno), rapl_sysfs.get_base_path()); } } thd_log_info("RAPL domain count %d\n", count); } void cthd_rapl_power_meter::rapl_enable_periodic_timer() { pthread_attr_init(&thd_attr); pthread_attr_setdetachstate(&thd_attr, PTHREAD_CREATE_DETACHED); pthread_create(&poll_thread, &thd_attr, rapl_periodic_callback, (void*) this); } bool cthd_rapl_power_meter::rapl_energy_loop() { csys_fs sys_fs; int status; unsigned long long counter; unsigned long long diff; time_t curr_time; if (!enable_measurement) return false; curr_time = time(NULL); if ((curr_time - last_time) <= 0) return true; for (unsigned int i = 0; i < domain_list.size(); ++i) { std::string buffer; std::string path; if (!domain_list[i].max_energy_range) { std::string _path; std::string _buffer; _path = domain_list[i].path + "/" + "max_energy_range_uj"; status = sys_fs.read(_path, _buffer); if (status >= 0) domain_list[i].max_energy_range = atoll(_buffer.c_str()) / 1000; domain_list[i].max_energy_range_threshold = domain_list[i].max_energy_range / 2; } path = domain_list[i].path + "/" + "energy_uj"; status = sys_fs.read(path, buffer); if (status >= 0) { counter = domain_list[i].energy_counter; domain_list[i].energy_counter = atoll(buffer.c_str()) / 1000; // To milli Js diff = 0; if (domain_list[i].half_way && domain_list[i].energy_counter < domain_list[i].max_energy_range_threshold) { // wrap around domain_list[i].energy_cumulative_counter += domain_list[i].max_energy_range; diff = domain_list[i].max_energy_range - counter; counter = 0; domain_list[i].half_way = 0; } else if (domain_list[i].energy_counter > domain_list[i].max_energy_range_threshold) domain_list[i].half_way = 1; if (counter) domain_list[i].power = (domain_list[i].energy_counter - counter + diff) / (curr_time - last_time); if (domain_list[i].power > domain_list[i].max_power) domain_list[i].max_power = domain_list[i].power; if (domain_list[i].min_power == 0) domain_list[i].min_power = domain_list[i].power; else if (domain_list[i].power < domain_list[i].min_power) domain_list[i].min_power = domain_list[i].power; thd_log_debug(" energy %d:%lld:%lld mj: %u mw \n", domain_list[i].type, domain_list[i].energy_cumulative_counter, domain_list[i].energy_counter + domain_list[i].energy_cumulative_counter, domain_list[i].power); } } last_time = curr_time; return true; } unsigned long long cthd_rapl_power_meter::rapl_action_get_energy( domain_type type) { unsigned long long value = 0; for (unsigned int i = 0; i < domain_list.size(); ++i) { if (type == domain_list[i].type) { value = (domain_list[i].energy_counter + domain_list[i].energy_cumulative_counter) * 1000; if (!value) { rapl_energy_loop(); value = (domain_list[i].energy_counter + domain_list[i].energy_cumulative_counter) * 1000; } break; } } return value; } unsigned int cthd_rapl_power_meter::rapl_action_get_power(domain_type type) { unsigned int value = 0; if (!rapl_present) return 0; for (unsigned int i = 0; i < domain_list.size(); ++i) { if (type == domain_list[i].type) { value = domain_list[i].power * 1000; if (!value) { rapl_energy_loop(); sleep(1); rapl_energy_loop(); value = domain_list[i].power * 1000; } break; } } return value; } unsigned int cthd_rapl_power_meter::rapl_action_get_max_power( domain_type type) { unsigned int value = 0; if (!rapl_present) return 0; for (unsigned int i = 0; i < domain_list.size(); ++i) { if (type == domain_list[i].type) { int status; std::string _path; std::string _buffer; csys_fs sys_fs; unsigned int const_0_val, const_1_val; const_0_val = 0; const_1_val = 0; _path = domain_list[i].path + "/" + "constraint_0_max_power_uw"; status = sys_fs.read(_path, _buffer); if (status >= 0) const_0_val = atoi(_buffer.c_str()); _path = domain_list[i].path + "/" + "constraint_1_max_power_uw"; status = sys_fs.read(_path, _buffer); if (status >= 0) const_1_val = atoi(_buffer.c_str()); value = const_1_val > const_0_val ? const_1_val : const_0_val; if (value) return value; } } return value; } unsigned int cthd_rapl_power_meter::rapl_action_get_power(domain_type type, unsigned int *max_power, unsigned int *min_power) { unsigned int value = 0; if (!rapl_present) return 0; for (unsigned int i = 0; i < domain_list.size(); ++i) { if (type == domain_list[i].type) { value = domain_list[i].power * 1000; if (!value) { rapl_energy_loop(); sleep(1); rapl_energy_loop(); value = domain_list[i].power * 1000; } *max_power = domain_list[i].max_power * 1000; *min_power = domain_list[i].min_power * 1000; break; } } return value; } void cthd_rapl_power_meter::rapl_measure_power() { if (rapl_present && enable_measurement) rapl_energy_loop(); } 07070100000053000081A400000000000000000000000166853FAC000009CC000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_rapl_power_meter.h/* * thd_rapl_power_meter.h: thermal cooling class interface * using RAPL * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_CDEV_RAPL_POWER_METER_H_ #define THD_CDEV_RAPL_POWER_METER_H_ #include "thd_common.h" #include "thd_sys_fs.h" #include <vector> typedef enum { INVALID = 0, PACKAGE = 0x01, DRAM = 0x02, CORE = 0x04, UNCORE = 0x08 } domain_type; typedef struct { domain_type type; std::string name; std::string path; // Store in milli-units to have a bigger range unsigned long long max_energy_range; unsigned long long max_energy_range_threshold; int half_way; unsigned long long energy_cumulative_counter; unsigned long long energy_counter; unsigned int power; unsigned int max_power; unsigned int min_power; } rapl_domain_t; class cthd_rapl_power_meter { private: bool rapl_present; csys_fs rapl_sysfs; std::vector<rapl_domain_t> domain_list; time_t last_time; pthread_t poll_thread; pthread_attr_t thd_attr; unsigned int measure_mask; bool enable_measurement; public: static const int rapl_callback_timeout = 10; //seconds cthd_rapl_power_meter(unsigned int mask = PACKAGE | DRAM); void rapl_read_domains(const char *base_path); void rapl_enable_periodic_timer(); bool rapl_energy_loop(); void rapl_measure_power(); void rapl_start_measure_power() { enable_measurement = true; } void rapl_stop_measure_power() { enable_measurement = false; } // return in micro units to be compatible with kernel ABI unsigned long long rapl_action_get_energy(domain_type type); unsigned int rapl_action_get_power(domain_type type); unsigned int rapl_action_get_power(domain_type type, unsigned int *max_power, unsigned int *min_power); unsigned int rapl_action_get_max_power(domain_type type); }; #endif 07070100000054000081A400000000000000000000000166853FAC00000DF1000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor.cpp/* * thd_sensor.h: thermal sensor class implementation * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_sensor.h" #include "thd_engine.h" cthd_sensor::cthd_sensor(int _index, std::string control_path, std::string _type_str, int _type) : index(_index), type(_type), sensor_sysfs(control_path.c_str()), sensor_active( false), type_str(_type_str), async_capable(false), virtual_sensor( false), thresholds(0), scale(1) { } int cthd_sensor::sensor_update() { if (type == SENSOR_TYPE_THERMAL_SYSFS) { if (sensor_sysfs.exists("type")) { sensor_sysfs.read("type", type_str); thd_log_info("sensor_update: type %s\n", type_str.c_str()); } else return THD_ERROR; if (sensor_sysfs.exists("temp")) { return THD_SUCCESS; } else { thd_log_msg("sensor id %d: No temp sysfs for reading temp\n", index); return THD_ERROR; } } if (type == SENSOR_TYPE_RAW) { if (sensor_sysfs.exists("")) { return THD_SUCCESS; } else { thd_log_msg("sensor id %d %s: No temp sysfs for reading raw temp\n", index, sensor_sysfs.get_base_path()); return THD_ERROR; } } return THD_SUCCESS; } unsigned int cthd_sensor::read_temperature() { csys_fs sysfs; std::string buffer; int temp; thd_log_debug("read_temperature sensor ID %d\n", index); if (type == SENSOR_TYPE_THERMAL_SYSFS) sensor_sysfs.read("temp", buffer); else sensor_sysfs.read("", buffer); std::istringstream(buffer) >> temp; if (temp < 0) temp = 0; thd_log_debug("Sensor %s :temp %u \n", type_str.c_str(), temp); return (unsigned int)temp / scale; } void cthd_sensor::enable_uevent() { csys_fs cdev_sysfs("/sys/class/thermal/"); std::stringstream policy_sysfs; policy_sysfs << "thermal_zone" << index << "/policy"; if (cdev_sysfs.exists(policy_sysfs.str().c_str())) { cdev_sysfs.write(policy_sysfs.str(), "user_space"); } } int cthd_sensor::set_threshold(int index, int temp) { if (type != SENSOR_TYPE_THERMAL_SYSFS) return THD_ERROR; std::stringstream tcdev; std::stringstream thres; int status = 0; if (thd_engine->get_poll_interval()) return THD_SUCCESS; if (!async_capable) { return THD_ERROR; } tcdev << "trip_point_" << index << "_temp"; thres << temp; if (sensor_sysfs.exists(tcdev.str().c_str())) { status = sensor_sysfs.write(tcdev.str(), thres.str()); } thd_log_debug("cthd_sensor::set_threshold: status %d\n", status); if (status > 0) { enable_uevent(); return THD_SUCCESS; } else return THD_ERROR; } void cthd_sensor::sensor_poll_trip(bool status) { if (status) thd_engine->thd_engine_poll_enable(index); else thd_engine->thd_engine_poll_disable(index); } void cthd_sensor::sensor_fast_poll(bool status) { if (status) thd_engine->thd_engine_fast_poll_enable(index); else thd_engine->thd_engine_fast_poll_disable(index); } 07070100000055000081A400000000000000000000000166853FAC0000099D000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor.h/* * thd_sensor.h: thermal sensor class interface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_H_ #define THD_SENSOR_H_ #include <vector> #include "thd_common.h" #include "thd_sys_fs.h" #define SENSOR_TYPE_THERMAL_SYSFS 0 #define SENSOR_TYPE_RAW 1 class cthd_sensor { protected: int index; int type; csys_fs sensor_sysfs; bool sensor_active; std::string type_str; bool async_capable; bool virtual_sensor; private: std::vector<int> thresholds; int scale; void enable_uevent(); public: cthd_sensor(int _index, std::string control_path, std::string _type_str, int _type = SENSOR_TYPE_THERMAL_SYSFS); virtual ~cthd_sensor() { } int sensor_update(); virtual std::string get_sensor_type() { return type_str; } virtual std::string get_sensor_path() { return sensor_sysfs.get_base_path(); } virtual unsigned int read_temperature(); int get_index() { return index; } int set_threshold(int index, int temp); ; void update_path(std::string str) { sensor_sysfs.update_path(str); } void set_async_capable(bool capable) { async_capable = capable; } bool check_async_capable() { return async_capable; } void set_scale(int _scale) { scale = _scale; } virtual void sensor_dump() { thd_log_info("sensor index:%d %s %s Async:%d \n", index, type_str.c_str(), sensor_sysfs.get_base_path(), async_capable); } // Even if sensors are capable of async, it is possible that it is not reliable enough // at critical monitoring point. Sensors can be forced to go to poll mode at that temp void sensor_poll_trip(bool status); void sensor_fast_poll(bool status); bool is_virtual() { return virtual_sensor; } }; #endif /* THD_SENSOR_H_ */ 07070100000056000081A400000000000000000000000166853FAC00000679000000000000000000000000000000000000004800000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_amdgpu_power.cpp/* * thd_sensor_kbl_amdgpu_power.cpp: Power Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_sensor_kbl_amdgpu_power.h" cthd_sensor_kbl_amdgpu_power::cthd_sensor_kbl_amdgpu_power(int index) : cthd_sensor(index, "", "amdgpu-power", SENSOR_TYPE_RAW) { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/hwmon/"; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { // Check name std::string name_path = base_path + entry->d_name + "/name"; std::ifstream ifs(name_path.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { if (line == "amdgpu") { update_path( base_path + entry->d_name + "/power1_average"); set_scale(1000); } } ifs.close(); } } closedir(dir); } } 07070100000057000081A400000000000000000000000166853FAC0000044A000000000000000000000000000000000000004600000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_amdgpu_power.h/* * thd_sensor_kbl_amdgpu_power.h: Power Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_KBL_AMD_GPU_POWER_H #define THD_SENSOR_KBL_AMD_GPU_POWER_H #include "thd_sensor.h" class cthd_sensor_kbl_amdgpu_power: public cthd_sensor { private: public: cthd_sensor_kbl_amdgpu_power(int index); }; #endif 07070100000058000081A400000000000000000000000166853FAC00000666000000000000000000000000000000000000004A00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_amdgpu_thermal.cpp/* * thd_sensor_kbl_amdgpu_thermal.cpp: Thermal Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_sensor_kbl_amdgpu_thermal.h" cthd_sensor_kbl_amdgpu_thermal::cthd_sensor_kbl_amdgpu_thermal(int index) : cthd_sensor(index, "", "amdgpu-temperature", SENSOR_TYPE_RAW) { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/hwmon/"; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { // Check name std::string name_path = base_path + entry->d_name + "/name"; std::ifstream ifs(name_path.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { if (line == "amdgpu") { update_path(base_path + entry->d_name + "/temp1_input"); } } ifs.close(); } } closedir(dir); } } 07070100000059000081A400000000000000000000000166853FAC00000456000000000000000000000000000000000000004800000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_amdgpu_thermal.h/* * thd_sensor_kbl_amdgpu_thermal.h: Thermal Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_KBL_AMD_GPU_THERMAL_H #define THD_SENSOR_KBL_AMD_GPU_THERMAL_H #include "thd_sensor.h" class cthd_sensor_kbl_amdgpu_thermal: public cthd_sensor { private: public: cthd_sensor_kbl_amdgpu_thermal(int index); }; #endif 0707010000005A000081A400000000000000000000000166853FAC0000087F000000000000000000000000000000000000004100000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_g_mcp.cpp/* * thd_sensor_kbl_amdgpu_power.cpp: Power Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_sensor_kbl_g_mcp.h" #include "thd_engine.h" cthd_sensor_kbl_g_mcp::cthd_sensor_kbl_g_mcp(int index) : cthd_sensor(index, "", "kbl-g-mcp", SENSOR_TYPE_RAW) { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/hwmon/"; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { // Check name std::string name_path = base_path + entry->d_name + "/name"; std::ifstream ifs(name_path.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { if (line == "amdgpu") { update_path( base_path + entry->d_name + "/power1_average"); set_scale(1000); } } ifs.close(); } } closedir(dir); } } unsigned int cthd_sensor_kbl_g_mcp::read_temperature() { csys_fs sysfs; std::string buffer; int gpu_power; int ret; thd_engine->rapl_power_meter.rapl_start_measure_power(); ret = sensor_sysfs.read("", &gpu_power); if (ret <= 0) gpu_power = 0; unsigned int pkg_power = thd_engine->rapl_power_meter.rapl_action_get_power( PACKAGE); thd_log_debug("Sensor %s :temp %u %u total %u \n", type_str.c_str(), gpu_power, pkg_power, gpu_power + pkg_power); return (unsigned int)(gpu_power + pkg_power); } 0707010000005B000081A400000000000000000000000166853FAC0000044E000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_kbl_g_mcp.h/* * thd_sensor_kbl_amdgpu_power.h: Power Sensor for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_KBL_G_MCP_H #define THD_SENSOR_KBL_G_MCP_H #include "thd_sensor.h" class cthd_sensor_kbl_g_mcp: public cthd_sensor { private: public: cthd_sensor_kbl_g_mcp(int index); unsigned int read_temperature(); }; #endif 0707010000005C000081A400000000000000000000000166853FAC000005A5000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_rapl_power.cpp/* * thd_sensor_rapl_power.cpp: Power Sensor for RAPL * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_sensor_rapl_power.h" #include "thd_engine.h" cthd_sensor_rapl_power::cthd_sensor_rapl_power(int index) : cthd_sensor(index, "", "rapl_pkg_power", SENSOR_TYPE_RAW) { update_path("/sys/class/powercap/intel-rapl"); } unsigned int cthd_sensor_rapl_power::read_temperature() { thd_engine->rapl_power_meter.rapl_start_measure_power(); unsigned int pkg_power = thd_engine->rapl_power_meter.rapl_action_get_power( PACKAGE); pkg_power = (pkg_power / 1000); thd_log_debug("Sensor %s :power %u \n", type_str.c_str(), pkg_power); return pkg_power; } 0707010000005D000081A400000000000000000000000166853FAC00000444000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_rapl_power.h/* * thd_sensor_rapl_power.h: Power Sensor for rapl * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_RAPL_POWER_H #define THD_SENSOR_RAPL_POWER_H #include "thd_sensor.h" class cthd_sensor_rapl_power: public cthd_sensor { private: public: cthd_sensor_rapl_power(int index); unsigned int read_temperature(); }; #endif 0707010000005E000081A400000000000000000000000166853FAC000007EE000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_virtual.cpp/* * thd_sensor_virtual.h: thermal sensor virtual class implementation * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_sensor_virtual.h" #include "thd_engine.h" cthd_sensor_virtual::cthd_sensor_virtual(int _index, std::string _type_str, std::string _link_type_str, double _multiplier, double _offset) : cthd_sensor(_index, "none", _type_str), link_sensor(NULL), link_type_str( _link_type_str), multiplier(_multiplier), offset(_offset) { virtual_sensor = true; } cthd_sensor_virtual::~cthd_sensor_virtual() { } int cthd_sensor_virtual::sensor_update() { cthd_sensor *sensor = thd_engine->search_sensor(link_type_str); if (sensor) link_sensor = sensor; else return THD_ERROR; return THD_SUCCESS; } unsigned int cthd_sensor_virtual::read_temperature() { unsigned int temp; temp = link_sensor->read_temperature(); temp = temp * multiplier + offset; thd_log_debug("cthd_sensor_virtual::read_temperature %u\n", temp); return temp; } int cthd_sensor_virtual::sensor_update_param(std::string new_dep_sensor, double slope, double intercept) { cthd_sensor *sensor = thd_engine->search_sensor(new_dep_sensor); if (sensor) link_sensor = sensor; else return THD_ERROR; multiplier = slope; offset = intercept; return THD_SUCCESS; } 0707010000005F000081A400000000000000000000000166853FAC00000673000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sensor_virtual.h/* * thd_sensor_virtual.h: thermal sensor virtual class interface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SENSOR_VIRTUAL_H_ #define THD_SENSOR_VIRTUAL_H_ #include "thd_sensor.h" class cthd_sensor_virtual: public cthd_sensor { private: cthd_sensor *link_sensor; std::string link_type_str; double multiplier; double offset; public: cthd_sensor_virtual(int _index, std::string _type_str, std::string _link_type_str, double multiplier, double offset); ~cthd_sensor_virtual(); int sensor_update(); unsigned int read_temperature(); virtual void sensor_dump() { if (link_sensor) thd_log_info("sensor index:%d %s virtual link %s %f %f \n", index, type_str.c_str(), link_sensor->get_sensor_type().c_str(), multiplier, offset); } int sensor_update_param(std::string new_dep_sensor, double slope, double intercept); }; #endif /* THD_SENSOR_VIRTUAL_H_ */ 07070100000060000081A400000000000000000000000166853FAC000014F9000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sys_fs.cpp/* * thd_sys_fs.cpp: sysfs class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_sys_fs.h" #include "thd_common.h" #include <stdlib.h> int csys_fs::write(const std::string &path, const std::string &buf) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_WRONLY); if (fd < 0) { thd_log_info("sysfs write failed %s\n", p.c_str()); return -errno; } int ret = ::write(fd, buf.c_str(), buf.size()); if (ret < 0) { ret = -errno; thd_log_info("sysfs write failed %s\n", p.c_str()); } close(fd); return ret; } int csys_fs::write(const std::string &path, unsigned int position, unsigned long long data) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_WRONLY); if (fd < 0) { thd_log_info("sysfs write failed %s\n", p.c_str()); return -errno; } if (::lseek(fd, position, SEEK_CUR) == -1) { thd_log_info("sysfs write failed %s\n", p.c_str()); close(fd); return -errno; } int ret = ::write(fd, &data, sizeof(data)); if (ret < 0) thd_log_info("sysfs write failed %s\n", p.c_str()); close(fd); return ret; } int csys_fs::write(const std::string &path, unsigned int data) { std::ostringstream os; os << data; return csys_fs::write(path, os.str()); } int csys_fs::read(const std::string &path, char *buf, int len) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_RDONLY); int orig_len = len; if (fd < 0) { thd_log_info("sysfs read failed %s\n", p.c_str()); return -errno; } while (len > 0) { int ret = ::read(fd, buf, len); if (ret < 0) { thd_log_info("sysfs read failed %s\n", p.c_str()); close(fd); return ret; } buf += ret; len -= ret; } close(fd); return orig_len - len; } int csys_fs::read(const std::string &path, unsigned int position, char *buf, int len) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_info("sysfs read failed %s\n", p.c_str()); return -errno; } if (::lseek(fd, position, SEEK_CUR) == -1) { thd_log_info("sysfs read failed %s\n", p.c_str()); close(fd); return -errno; } int ret = ::read(fd, buf, len); if (ret < 0) thd_log_info("sysfs read failed %s\n", p.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, int *ptr_val) { std::string p = base_path + path; char str[16]; int ret; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_info("sysfs open failed %s\n", p.c_str()); return -errno; } ret = ::read(fd, str, sizeof(str)); if (ret > 0) *ptr_val = atoi(str); else thd_log_info("sysfs read failed %s\n", p.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, unsigned long *ptr_val) { std::string p = base_path + path; char str[32]; int ret; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_info("sysfs read failed %s\n", p.c_str()); return -errno; } ret = ::read(fd, str, sizeof(str)); if (ret > 0) *ptr_val = atol(str); else thd_log_info("sysfs read failed %s\n", p.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, std::string &buf) { std::string p = base_path + path; int ret = 0; #ifndef ANDROID try { #endif std::ifstream f(p.c_str(), std::fstream::in); if (f.fail()) { thd_log_info("sysfs read failed %s\n", p.c_str()); return -EINVAL; } f >> buf; if (f.bad()) { thd_log_info("sysfs read failed %s\n", p.c_str()); ret = -EIO; } f.close(); #ifndef ANDROID } catch (...) { thd_log_info("csys_fs::read exception %s\n", p.c_str()); ret = -EIO; } #endif return ret; } bool csys_fs::exists(const std::string &path) { struct stat s; return (bool) (stat((base_path + path).c_str(), &s) == 0); } size_t csys_fs::size(const std::string &path) { struct stat s; if (stat((base_path + path).c_str(), &s) == 0) return s.st_size; return 0; } int csys_fs::create() { int fd = ::open(base_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU); if (fd < 0) { thd_log_info("sysfs create failed %s\n", base_path.c_str()); return -errno; } close(fd); return 0; } bool csys_fs::exists() { return csys_fs::exists(""); } mode_t csys_fs::get_mode(const std::string &path) { struct stat s; if (stat((base_path + path).c_str(), &s) == 0) return s.st_mode; else return 0; } int csys_fs::read_symbolic_link_value(const std::string &path, char *buf, int len) { std::string p = base_path + path; int ret = ::readlink(p.c_str(), buf, len); if (ret < 0) { *buf = '\0'; thd_log_info("read_symbolic_link %s\n", path.c_str()); return -errno; } buf[ret] = '\0'; return 0; } 07070100000061000081A400000000000000000000000166853FAC000008A9000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_sys_fs.h/* * thd_sys_fs.h: sysfs class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_SYS_FS_H_ #define THD_SYS_FS_H_ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <fstream> #include <iostream> #include <sstream> #include <string> class csys_fs { private: std::string base_path; public: csys_fs() : base_path("") { } ; csys_fs(const char *path) : base_path(path) { } /* write data to base path (dir) + provided path */ int write(const std::string &path, const std::string &buf); int write(const std::string &path, unsigned int data); int write(const std::string &path, unsigned int position, unsigned long long data); /* read data from base path (dir) + provided path */ int read(const std::string &path, char *buf, int len); int read(const std::string &path, std::string &buf); int read(const std::string &path, int *ptr_val); int read(const std::string &path, unsigned long *ptr_val); int read(const std::string &path, unsigned int position, char *buf, int len); const char *get_base_path() { return base_path.c_str(); } int read_symbolic_link_value(const std::string &path, char *buf, int len); bool exists(const std::string &path); bool exists(); size_t size(const std::string &path); int create(); mode_t get_mode(const std::string &path); void update_path(std::string path) { base_path = path; } }; #endif /* THD_SYS_FS_H_ */ 07070100000062000081A400000000000000000000000166853FAC0000295B000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_trip_point.cpp/* * thd_trip_point.cpp: thermal zone class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/reboot.h> #include "thd_trip_point.h" #include "thd_engine.h" cthd_trip_point::cthd_trip_point(int _index, trip_point_type_t _type, unsigned int _temp, unsigned int _hyst, int _zone_id, int _sensor_id, trip_control_type_t _control_type) : index(_index), type(_type), temp(_temp), hyst(_hyst), control_type( _control_type), zone_id(_zone_id), sensor_id(_sensor_id), trip_on( false), poll_on(false), depend_cdev(NULL), depend_cdev_state(0), depend_cdev_state_rel( EQUAL), crit_trip_count(0) { thd_log_debug("Add trip pt %d:%d:0x%x:%d:%d\n", type, zone_id, sensor_id, temp, hyst); } void cthd_trip_point::set_dependency(std::string cdev, std::string state_str) { cthd_cdev *cdev_ptr; cdev_ptr = thd_engine->search_cdev(cdev); if (cdev_ptr) { int match; int state_index = 0; depend_cdev = cdev_ptr; match = state_str.compare(0, 2, "=="); if (!match) { depend_cdev_state_rel = EQUAL; state_index = 2; } match = state_str.compare(0, 1, ">"); if (!match) { depend_cdev_state_rel = GREATER; state_index = 1; } match = state_str.compare(0, 1, "<"); if (!match) { state_index = 1; depend_cdev_state_rel = LESSER; } match = state_str.compare(0, 2, "<="); if (!match) { depend_cdev_state_rel = LESSER_OR_EQUAL; state_index = 2; } match = state_str.compare(0, 2, ">="); if (!match) { depend_cdev_state_rel = GREATER_OR_EQUAL; state_index = 2; } depend_cdev_state = atoi(state_str.substr(state_index).c_str()); } } bool cthd_trip_point::thd_trip_point_check(int id, unsigned int read_temp, int pref, bool *reset) { int on = -1; int off = -1; bool apply = false; *reset = false; if (type == INVALID_TRIP_TYPE) return false; if (depend_cdev && read_temp >= temp) { int _state = depend_cdev->get_curr_state(); int valid = 0; switch (depend_cdev_state_rel) { case EQUAL: if (_state == depend_cdev_state) valid = 1; break; case GREATER: if (_state > depend_cdev_state) valid = 1; break; case LESSER: if (_state < depend_cdev_state) valid = 1; break; case LESSER_OR_EQUAL: if (_state <= depend_cdev_state) valid = 1; break; case GREATER_OR_EQUAL: if (_state >= depend_cdev_state) valid = 1; break; default: break; } if (!valid) { thd_log_info("constraint failed %s:%d:%d:%d \n", depend_cdev->get_cdev_type().c_str(), _state, depend_cdev_state_rel, depend_cdev_state); return false; } } if (sensor_id != DEFAULT_SENSOR_ID && sensor_id != id) return false; if (read_temp == 0) { thd_log_debug("TEMP == 0 pref: %d\n", pref); } if (type == CRITICAL) { int ret = -1; if (!ignore_critical && read_temp >= temp) { thd_log_warn("critical temp reached \n"); if (crit_trip_count < consecutive_critical_events) { ++crit_trip_count; return true; } crit_trip_count = 0; sync(); #ifdef ANDROID ret = property_set("sys.powerctl", "shutdown,thermal"); #else reboot(RB_POWER_OFF); #endif if (ret != 0) thd_log_warn("power off failed ret=%d err=%s\n", ret, strerror(errno)); else thd_log_warn("power off initiated\n"); return true; } crit_trip_count = 0; } if (type == HOT) { if (!ignore_critical && read_temp >= temp) { thd_log_warn("Hot temp reached \n"); if (crit_trip_count < consecutive_critical_events) { ++crit_trip_count; return true; } crit_trip_count = 0; thd_log_warn("Hot temp reached \n"); csys_fs power("/sys/power/"); power.write("state", "mem"); return true; } crit_trip_count = 0; } if (type == POLLING && sensor_id != DEFAULT_SENSOR_ID) { cthd_sensor *sensor = thd_engine->get_sensor(sensor_id); if (sensor) { if (!poll_on && read_temp >= temp) { thd_log_debug("polling trip reached, on \n"); sensor->sensor_poll_trip(true); poll_on = true; sensor->sensor_fast_poll(true); if (sensor->check_async_capable()) sensor->set_threshold(0, temp); } else if (poll_on && read_temp < temp) { sensor->sensor_poll_trip(false); thd_log_debug("Dropped below poll threshold \n"); *reset = true; poll_on = false; sensor->sensor_fast_poll(false); if (sensor->check_async_capable()) sensor->set_threshold(0, temp); } } return true; } thd_log_debug("pref %d type %d temp %d trip %d \n", pref, type, read_temp, temp); switch (pref) { case PREF_DISABLED: return false; break; case PREF_PERFORMANCE: if (type == ACTIVE || type == MAX) { apply = true; thd_log_debug("Active Trip point applicable \n"); } break; case PREF_ENERGY_CONSERVE: if (type == PASSIVE || type == MAX) { apply = true; thd_log_debug("Passive Trip point applicable \n"); } break; default: break; } if (apply) { if (read_temp >= temp) { thd_log_debug("Trip point applicable > %d:%d \n", index, temp); on = 1; trip_on = true; } else if ((trip_on && (read_temp + hyst) < temp) || (!trip_on && read_temp < temp)) { thd_log_debug("Trip point applicable < %d:%d \n", index, temp); off = 1; trip_on = false; } } else return false; if (on != 1 && off != 1) return true; int i, ret; thd_log_debug("cdev size for this trippoint %lu\n", (unsigned long) cdevs.size()); if (on > 0) { for (unsigned i = 0; i < cdevs.size(); ++i) { cthd_cdev *cdev = cdevs[i].cdev; if (cdevs[i].sampling_priod) { time_t tm; time(&tm); if ((tm - cdevs[i].last_op_time) < cdevs[i].sampling_priod) { thd_log_debug("Too early to act zone:%d index %d tm %jd\n", zone_id, cdev->thd_cdev_get_index(), (intmax_t)tm - cdevs[i].last_op_time); break; } cdevs[i].last_op_time = tm; } thd_log_debug("cdev at index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); /* * When the cdev is already in max state, we skip this cdev. */ if (cdev->in_max_state()) { thd_log_debug("Need to switch to next cdev target %d \n", cdev->map_target_state(cdevs[i].target_state_valid, cdevs[i].target_state)); // No scope of control with this cdev continue; } if (cdevs[i].target_state == TRIP_PT_INVALID_TARGET_STATE) cdevs[i].target_state = cdev->get_min_state(); ret = cdev->thd_cdev_set_state(temp, temp, read_temp, (type == MAX), 1, zone_id, index, cdevs[i].target_state_valid, cdev->map_target_state(cdevs[i].target_state_valid, cdevs[i].target_state), &cdevs[i].pid_param, cdevs[i].pid, false, cdevs[i].min_max_valid, cdevs[i].min_state, cdevs[i].max_state); if (control_type == SEQUENTIAL && ret == THD_SUCCESS) { // Only one cdev activation break; } } } if (off > 0) { for (i = cdevs.size() - 1; i >= 0; --i) { cthd_cdev *cdev = cdevs[i].cdev; thd_log_debug("cdev at index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); if (cdev->in_min_state()) { thd_log_debug("Need to switch to next cdev \n"); // No scope of control with this cdev continue; } if (cdevs[i].target_state == TRIP_PT_INVALID_TARGET_STATE) cdevs[i].target_state = cdev->get_min_state(); cdev->thd_cdev_set_state(temp, temp, read_temp, (type == MAX), 0, zone_id, index, cdevs[i].target_state_valid, cdev->map_target_state(cdevs[i].target_state_valid, cdevs[i].target_state), &cdevs[i].pid_param, cdevs[i].pid, false, cdevs[i].min_max_valid, cdevs[i].min_state, cdevs[i].max_state); if (control_type == SEQUENTIAL) { // Only one cdev activation break; } } } return true; } void cthd_trip_point::thd_trip_point_add_cdev(cthd_cdev &cdev, int influence, int sampling_period, int target_state_valid, int target_state, pid_param_t *pid_param, int min_max_valid, int min_state, int max_state) { trip_pt_cdev_t thd_cdev = {}; thd_cdev.cdev = &cdev; thd_cdev.influence = influence; thd_cdev.sampling_priod = sampling_period; thd_cdev.last_op_time = 0; thd_cdev.target_state_valid = target_state_valid; thd_cdev.target_state = target_state; thd_cdev.min_max_valid = min_max_valid; thd_log_info("min:%d max:%d\n", min_state, max_state); if (min_max_valid) { thd_cdev.min_state = min_state; thd_cdev.max_state = max_state; } if (pid_param && pid_param->valid) { thd_log_info("pid valid %f:%f:%f\n", pid_param->kp, pid_param->ki, pid_param->kd); memcpy(&thd_cdev.pid_param, pid_param, sizeof(pid_param_t)); thd_cdev.pid.set_pid_param(pid_param->kp, pid_param->ki, pid_param->kd); } else { memset(&thd_cdev.pid_param, 0, sizeof(pid_param_t)); } trip_cdev_add(thd_cdev); } int cthd_trip_point::thd_trip_point_add_cdev_index(int _index, int influence) { cthd_cdev *cdev = thd_engine->thd_get_cdev_at_index(_index); if (cdev) { trip_pt_cdev_t thd_cdev = {}; thd_cdev.cdev = cdev; thd_cdev.influence = influence; thd_cdev.sampling_priod = 0; thd_cdev.last_op_time = 0; thd_cdev.target_state_valid = 0; trip_cdev_add(thd_cdev); return THD_SUCCESS; } else { thd_log_warn("thd_trip_point_add_cdev_index not present %d\n", _index); return THD_ERROR; } } void cthd_trip_point::thd_trip_cdev_state_reset(int force) { thd_log_debug("thd_trip_cdev_state_reset \n"); for (int i = cdevs.size() - 1; i >= 0; --i) { cthd_cdev *cdev = cdevs[i].cdev; thd_log_debug("thd_trip_cdev_state_reset index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); if (!force && cdev->in_min_state()) { thd_log_debug("Need to switch to next cdev \n"); // No scope of control with this cdev continue; } cdev->thd_cdev_set_min_state(zone_id, index); } } 07070100000063000081A400000000000000000000000166853FAC00001B7D000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_trip_point.h/* * thd_trip_point.h: thermal zone trip points class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_TRIP_POINT_H #define THD_TRIP_POINT_H #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_cdev.h" #define __STDC_LIMIT_MACROS #include <stdint.h> #include <time.h> #include <vector> #include <algorithm> // std::sort #include <stdexcept> typedef enum { CRITICAL, HOT, MAX, PASSIVE, ACTIVE, POLLING, INVALID_TRIP_TYPE } trip_point_type_t; typedef enum { PARALLEL, // All associated cdevs are activated together SEQUENTIAL // one after other once the previous cdev reaches its max state } trip_control_type_t; #define TRIP_PT_INVALID_TARGET_STATE INT32_MAX typedef enum { EQUAL, GREATER, LESSER, LESSER_OR_EQUAL, GREATER_OR_EQUAL } trip_point_cdev_depend_rel_t; typedef struct { cthd_cdev *cdev; int influence; int sampling_priod; time_t last_op_time; int target_state_valid; int target_state; pid_param_t pid_param; cthd_pid pid; int min_max_valid; int min_state; int max_state; } trip_pt_cdev_t; #define DEFAULT_SENSOR_ID 0xFFFF static bool trip_cdev_sort(trip_pt_cdev_t &cdev1, trip_pt_cdev_t &cdev2) { return (cdev1.influence > cdev2.influence); } class cthd_trip_point { private: int index; trip_point_type_t type; unsigned int temp; unsigned int hyst; std::vector<trip_pt_cdev_t> cdevs; trip_control_type_t control_type; int zone_id; int sensor_id; bool trip_on; bool poll_on; cthd_cdev *depend_cdev; int depend_cdev_state; trip_point_cdev_depend_rel_t depend_cdev_state_rel; int crit_trip_count; bool check_duplicate(cthd_cdev *cdev, int *index) { for (unsigned int i = 0; i < cdevs.size(); ++i) { if (cdevs[i].cdev->get_cdev_type() == cdev->get_cdev_type()) { *index = i; return true; } } return false; } public: static const int default_influence = 0; static const int consecutive_critical_events = 4; cthd_trip_point(int _index, trip_point_type_t _type, unsigned int _temp, unsigned int _hyst, int _zone_id, int _sensor_id, trip_control_type_t _control_type = PARALLEL); bool thd_trip_point_check(int id, unsigned int read_temp, int pref, bool *reset); void thd_trip_point_add_cdev(cthd_cdev &cdev, int influence, int sampling_period = 0, int target_state_valid = 0, int target_state = TRIP_PT_INVALID_TARGET_STATE, pid_param_t *pid_param = NULL, int min_max_valid = 0, int min_state = 0, int max_state = 0); void delete_cdevs() { cdevs.clear(); } void thd_trip_cdev_state_reset(int force = 0); int thd_trip_point_value() { return temp; } void thd_trip_update_set_point(unsigned int new_value) { temp = new_value; } int thd_trip_point_add_cdev_index(int _index, int influence); void thd_trip_point_set_control_type(trip_control_type_t type) { control_type = type; } trip_point_type_t get_trip_type() { return type; } unsigned int get_trip_temp() { return temp; } unsigned int get_trip_hyst() { return hyst; } void update_trip_temp(unsigned int _temp) { temp = _temp; } void update_trip_type(trip_point_type_t _type) { type = _type; } void update_trip_hyst(unsigned int _temp) { hyst = _temp; } int get_sensor_id() { return sensor_id; } unsigned int get_cdev_count() { return cdevs.size(); } int is_target_valid(int &target_state) { target_state = 0; for (unsigned int i = 0; i < cdevs.size(); ++i) { trip_pt_cdev_t &cdev = cdevs[i]; if (cdev.target_state_valid) { thd_log_debug("matched %d\n", cdev.target_state); target_state = cdev.target_state; return THD_SUCCESS; } } return THD_ERROR; } int set_first_target_invalid() { if (cdevs.size()) { trip_pt_cdev_t &cdev = cdevs[0]; cdev.target_state_valid = 0; return THD_SUCCESS; } return THD_ERROR; } int set_first_target(int state) { if (cdevs.size()) { trip_pt_cdev_t &cdev = cdevs[0]; cdev.target_state_valid = 1; cdev.target_state = state; return THD_SUCCESS; } return THD_ERROR; } cthd_cdev* get_first_cdev() { if (!cdevs.size()) return NULL; return cdevs[0].cdev; } void set_dependency(std::string cdev, std::string state_str); #ifndef ANDROID trip_pt_cdev_t &get_cdev_at_index(unsigned int index) { if (index < cdevs.size()) return cdevs[index]; else throw std::invalid_argument("index"); } #endif void trip_cdev_add(trip_pt_cdev_t &trip_cdev) { int index; if (check_duplicate(trip_cdev.cdev, &index)) { cdevs[index].influence = trip_cdev.influence; } else cdevs.push_back(trip_cdev); std::sort(cdevs.begin(), cdevs.end(), trip_cdev_sort); } void trip_dump() { std::string _type_str; if (type == CRITICAL) _type_str = "critical"; else if (type == MAX) _type_str = "max"; else if (type == PASSIVE) _type_str = "passive"; else if (type == ACTIVE) _type_str = "active"; else if (type == POLLING) _type_str = "polling"; else if (type == HOT) _type_str = "hot"; else _type_str = "invalid"; thd_log_info( "index %d: type:%s temp:%u hyst:%u zone id:%d sensor id:%d control_type:%d cdev size:%lu\n", index, _type_str.c_str(), temp, hyst, zone_id, sensor_id, control_type, (unsigned long) cdevs.size()); if (depend_cdev) { thd_log_info("Depends on cdev %s:%d:%d\n", depend_cdev->get_cdev_type().c_str(), depend_cdev_state_rel, depend_cdev_state); } for (unsigned int i = 0; i < cdevs.size(); ++i) { thd_log_info("cdev[%u] %s, Sampling period: %d\n", i, cdevs[i].cdev->get_cdev_type().c_str(), cdevs[i].sampling_priod); if (cdevs[i].target_state_valid) thd_log_info("\t target_state:%d\n", cdevs[i].target_state); else thd_log_info("\t target_state:not defined\n"); thd_log_info("min_max %d\n", cdevs[i].min_max_valid); if (cdevs[i].pid_param.valid) thd_log_info("\t pid: kp=%g ki=%g kd=%g\n", cdevs[i].pid_param.kp, cdevs[i].pid_param.ki, cdevs[i].pid_param.kd); if (cdevs[i].min_max_valid) { thd_log_info("\t min_state:%d\n", cdevs[i].min_state); thd_log_info("\t max_state:%d\n", cdevs[i].max_state); } } } }; static inline bool trip_sort(cthd_trip_point trip1, cthd_trip_point trip2) { return (trip1.get_trip_temp() < trip2.get_trip_temp()); } #endif 07070100000064000081A400000000000000000000000166853FAC000052BF000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_trt_art_reader.cpp/* * thd_trt_art_reader.cpp: Create configuration using ACPI * _ART and _TRT tables * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_trt_art_reader.h" #include "acpi_thermal_rel_ioct.h" #include "thd_int3400.h" using namespace std; #define PRINT_ERROR(...) thd_log_info(__VA_ARGS__) #define PRINT_DEBUG(...) thd_log_debug(__VA_ARGS__) typedef struct { const char *source; const char *sub_string; } sub_string_t; sub_string_t source_substitue_strings[] = { { "TPCH", "pch_wildcat_point" }, { NULL, NULL } }; sub_string_t target_substitue_strings[] = { { "B0D4", "rapl_controller" }, { "DPLY", "LCD" }, { "DISP", "LCD" }, { "TMEM", "rapl_controller_dram" }, { "TCPU", "rapl_controller" }, { "B0DB", "rapl_controller" }, { NULL, NULL } }; sub_string_t sensor_substitue_strings[] = { { "TPCH", "pch_wildcat_point" }, { NULL, NULL } }; typedef enum { TARGET_DEV, SOURCE_DEV, SENSOR_DEV } sub_type_t; /* * The _TRT and _ART table may refer to entry, for which we have * we need to tie to some control device, which is not enumerated * as a thermal cooling device. In this case, we substitute them * to an inbuilt standard name. */ static void associate_device(sub_type_t type, string &name) { DIR *dir; struct dirent *entry; std::string base_path = "/sys/bus/platform/devices/"; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "INT340", strlen("INT340"))) { char buf[256]; int ret; std::string name_path = base_path + entry->d_name + "/firmware_node"; ret = readlink(name_path.c_str(), buf, sizeof(buf) - 1); if (ret > 0) { buf[ret] = '\0'; name_path.clear(); name_path = base_path + entry->d_name + "/" + std::string(buf) + "/"; csys_fs acpi_sysfs(name_path.c_str()); std::string uid; if (acpi_sysfs.exists("uid")) { ret = acpi_sysfs.read("uid", uid); if (ret < 0) continue; } else if (acpi_sysfs.exists("path")) { ret = acpi_sysfs.read("path", uid); if (ret < 0) continue; size_t pos = uid.find_last_of("."); if (pos != std::string::npos) { uid = uid.substr(pos + 1); } } else continue; if (name == uid) { if (name_path.find("INT3406") != std::string::npos) { name = "DISP"; } else if (name_path.find("INT3402") != std::string::npos) { name = "TMEM"; } else if (name_path.find("INT3401") != std::string::npos) { name = "TCPU"; } closedir(dir); return; } } } } closedir(dir); } } static void subtitute_string(sub_type_t type, string &name) { int i = 0; sub_string_t *list; if (type == TARGET_DEV) { associate_device(type, name); list = target_substitue_strings; } else if (type == SOURCE_DEV) list = source_substitue_strings; else list = sensor_substitue_strings; while (list[i].source) { if (name == list[i].source) { name = list[i].sub_string; break; } i++; } } cthd_acpi_rel::cthd_acpi_rel() : rel_cdev("/dev/acpi_thermal_rel"), xml_hdr("<?xml version=\"1.0\"?>\n"), conf_begin( "<ThermalConfiguration>\n"), conf_end( "</ThermalConfiguration>\n"), conf_file(), trt_data(NULL), trt_count( 0), art_data(NULL), art_count(0), psvt_data(NULL), psvt_count(0) { } int cthd_acpi_rel::process_psvt(std::string file_name) { cthd_INT3400 int3400 ("9E04115A-AE87-4D1C-9500-0F3E340BFE75"); std::string prefix; int ret; PRINT_DEBUG("Trying presence of PSVT\n"); if (int3400.match_supported_uuid () != THD_SUCCESS) { thd_log_info ("Passive 2 UUID is not present, hence ignore PSVT, as it may have junk!!\n"); return THD_ERROR; } ret = read_psvt (); if (ret) return ret; dump_psvt (); conf_file.open (file_name.c_str ()); if (!conf_file.is_open ()) { PRINT_ERROR("failed to open output file [%s]\n", file_name.c_str ()); return THD_ERROR; } conf_file << xml_hdr.c_str (); conf_file << conf_begin.c_str (); prefix = indentation = "\t"; conf_file << indentation.c_str () << "<Platform>" << "\n"; create_platform_conf (); create_platform_pref (0); prefix = indentation; conf_file << prefix.c_str () << "<ThermalZones>" << "\n"; // Read PSVT parse_target_devices (); indentation += "\t"; for (unsigned long i = 0; i < rel_list.size (); ++i) { PRINT_DEBUG("Add target target, i::%lu.\n", i); if (rel_list[i].target_device == "") { PRINT_ERROR("Empty target, skipping ..\n"); continue; } conf_file << prefix.c_str () << "\t" << "<ThermalZone>" << "\n"; subtitute_string (SOURCE_DEV, rel_list[i].target_device); conf_file << prefix.c_str () << "\t\t" << "<Type>" << rel_list[i].target_device.c_str () << "</Type>" << "\n"; conf_file << prefix.c_str () << "\t\t" << "<TripPoints>" << "\n"; add_psvt_trip_point (rel_list[i]); conf_file << prefix.c_str () << "\t\t" << "</TripPoints>" << "\n"; conf_file << prefix.c_str () << "\t" << "</ThermalZone>" << "\n"; // } conf_file << prefix.c_str () << "</ThermalZones>" << "\n"; conf_file << prefix.c_str () << "</Platform>" << "\n"; conf_file << conf_end.c_str (); conf_file.close (); delete[] psvt_data; return THD_SUCCESS; } int cthd_acpi_rel::generate_conf(std::string file_name) { int trt_status; string prefix; int art_status; int ret = 0; cthd_INT3400 int3400("42A441D6-AE6A-462b-A84B-4A8CE79027D3"); std::ifstream conf_file_check(file_name.c_str()); if (conf_file_check.is_open()) { PRINT_ERROR(" File Exists: file_name %s, so no regenerate\n", file_name.c_str()); conf_file_check.close(); return 0; } conf_file_check.close(); art_status = read_art(); trt_status = read_trt(); if (trt_status < 0 && art_status < 0) { PRINT_ERROR("TRT/ART read failed\n"); return process_psvt(file_name); } if (int3400.match_supported_uuid() != THD_SUCCESS) { thd_log_info("Passive 1 UUID is not present, hence ignore _TRT, as it may have junk!!\n"); thd_log_info("Try Passive 2\n"); return process_psvt(file_name); } conf_file.open(file_name.c_str()); if (!conf_file.is_open()) { PRINT_ERROR("failed to open output file [%s]\n", file_name.c_str()); ret = -1; goto cleanup; } conf_file << xml_hdr.c_str(); conf_file << conf_begin.c_str(); prefix = indentation = "\t"; conf_file << indentation.c_str() << "<Platform>" << "\n"; create_platform_conf(); create_platform_pref(0); create_thermal_zones(); conf_file << prefix.c_str() << "</Platform>" << "\n"; conf_file << conf_end.c_str(); conf_file.close(); cleanup: if (trt_status > 0) delete[] trt_data; if (art_status > 0) delete[] art_data; return ret; } int cthd_acpi_rel::read_psvt() { unsigned long length, count; int fd, ret; fd = open (rel_cdev.c_str (), O_RDWR); if (fd < 0) { PRINT_ERROR("failed to open %s \n", rel_cdev.c_str ()); return -1; } ret = ioctl (fd, ACPI_THERMAL_GET_PSVT_COUNT, &count); if (ret < 0) { PRINT_ERROR(" failed to GET COUNT on %s\n", rel_cdev.c_str ()); close (fd); return -1; } PRINT_DEBUG("PSVT count %lu ...\n", count); ret = ioctl (fd, ACPI_THERMAL_GET_PSVT_LEN, &length); if (ret < 0 || !length) { PRINT_ERROR(" failed to GET LEN on %s\n", rel_cdev.c_str ()); close (fd); return -1; } PRINT_DEBUG("PSVT length %lu ...\n", length); psvt_data = (unsigned char*) new char[length]; if (!psvt_data) { PRINT_ERROR("cannot allocate buffer %lu to read PSVT\n", length); close (fd); return -1; } ret = ioctl (fd, ACPI_THERMAL_GET_PSVT, psvt_data); if (ret < 0) { PRINT_ERROR(" failed to GET PSVT on %s\n", rel_cdev.c_str ()); close (fd); return -1; } psvt_count = count; close (fd); return 0; } int cthd_acpi_rel::read_art() { int fd; int ret; unsigned long count, length; fd = open(rel_cdev.c_str(), O_RDWR); if (fd < 0) { PRINT_ERROR("failed to open %s \n", rel_cdev.c_str()); return -1; } ret = ioctl(fd, ACPI_THERMAL_GET_ART_COUNT, &count); if (ret < 0) { PRINT_ERROR(" failed to GET COUNT on %s\n", rel_cdev.c_str()); close(fd); return -1; } PRINT_DEBUG("ART count %lu ...\n", count); ret = ioctl(fd, ACPI_THERMAL_GET_ART_LEN, &length); if (ret < 0 || !length) { PRINT_ERROR(" failed to GET LEN on %s\n", rel_cdev.c_str()); close(fd); return -1; } PRINT_DEBUG("ART length %lu ...\n", length); art_data = (unsigned char*) new char[length]; if (!art_data) { PRINT_ERROR("cannot allocate buffer %lu to read ART\n", length); close(fd); return -1; } ret = ioctl(fd, ACPI_THERMAL_GET_ART, art_data); if (ret < 0) { PRINT_ERROR(" failed to GET ART on %s\n", rel_cdev.c_str()); close(fd); return -1; } art_count = count; dump_art(); close(fd); return 0; } int cthd_acpi_rel::read_trt() { int fd; int ret; unsigned long count, length; fd = open(rel_cdev.c_str(), O_RDWR); if (fd < 0) { PRINT_ERROR("failed to open %s \n", rel_cdev.c_str()); return -1; } ret = ioctl(fd, ACPI_THERMAL_GET_TRT_COUNT, &count); if (ret < 0) { PRINT_ERROR(" failed to GET COUNT on %s\n", rel_cdev.c_str()); close(fd); return -1; } PRINT_DEBUG("TRT count %lu ...\n", count); ret = ioctl(fd, ACPI_THERMAL_GET_TRT_LEN, &length); if (ret < 0 || !length) { PRINT_ERROR(" failed to GET LEN on %s\n", rel_cdev.c_str()); close(fd); return -1; } trt_data = (unsigned char*) new char[length]; if (!trt_data) { PRINT_ERROR("cannot allocate buffer %lu to read TRT\n", length); close(fd); return -1; } ret = ioctl(fd, ACPI_THERMAL_GET_TRT, trt_data); if (ret < 0) { PRINT_ERROR(" failed to GET TRT on %s\n", rel_cdev.c_str()); close(fd); return -1; } trt_count = count; dump_trt(); close(fd); return 0; } #define ACPI_TYPE_STRING 0x02 #define DECI_KELVIN_TO_CELSIUS(t) ({ \ int _t = (t); \ ((_t-2732 >= 0) ? (_t-2732+5)/10 : (_t-2732-5)/10); \ }) void cthd_acpi_rel::add_psvt_trip_point(rel_object_t &rel_obj) { if (!rel_obj.psvt_objects.size ()) return; PRINT_DEBUG("add_passive_trip_point\n"); string prefix = indentation + "\t"; for (unsigned int j = 0; j < rel_obj.psvt_objects.size (); ++j) { union psvt_object *object = (union psvt_object*) rel_obj.psvt_objects[j]; string device_name = object->acpi_psvt_entry.source_device; int limit_value = -1; conf_file << prefix.c_str () << "<TripPoint>\n"; conf_file << prefix.c_str () << "\t" << "<SensorType>" << object->acpi_psvt_entry.target_device << "</SensorType>\n"; PRINT_DEBUG("object->acpi_psvt_entry.control_knob_type:%llu\n", object->acpi_psvt_entry.control_knob_type); if (object->acpi_psvt_entry.control_knob_type == ACPI_TYPE_STRING) { if (!strncasecmp (object->acpi_psvt_entry.limit.string, "MAX", 3)) conf_file << prefix.c_str () << "\t" << "<Temperature>" << (DECI_KELVIN_TO_CELSIUS(object->acpi_psvt_entry.passive_temp) + 1) * 1000 << "</Temperature>\n"; if (!strncasecmp (object->acpi_psvt_entry.limit.string, "MIN", 3)) conf_file << prefix.c_str () << "\t" << "<Temperature>" << (DECI_KELVIN_TO_CELSIUS(object->acpi_psvt_entry.passive_temp) * 1000) << "</Temperature>\n"; } else { limit_value = object->acpi_psvt_entry.limit.integer; conf_file << prefix.c_str () << "\t" << "<Temperature>" << (DECI_KELVIN_TO_CELSIUS(object->acpi_psvt_entry.passive_temp) * 1000) << "</Temperature>\n"; } conf_file << prefix.c_str () << "\t" << "<Type>" << "Passive" << "</Type>\n"; conf_file << prefix.c_str () << "\t" << "<CoolingDevice>\n"; subtitute_string (TARGET_DEV, device_name); conf_file << prefix.c_str () << "\t\t" << "<type>" << device_name.c_str () << "</type>\n"; if (object->acpi_psvt_entry.sample_period) conf_file << prefix.c_str () << "\t\t" << "<SamplingPeriod>" << (object->acpi_psvt_entry.sample_period / 10) << "</SamplingPeriod>\n"; if (limit_value > 0) conf_file << prefix.c_str () << "\t\t" << "<TargetState>" << (limit_value * 1000) << "</TargetState>\n"; conf_file << prefix.c_str () << "\t" << "</CoolingDevice>\n"; conf_file << prefix.c_str () << "</TripPoint>\n"; } } void cthd_acpi_rel::add_passive_trip_point(rel_object_t &rel_obj) { if (!rel_obj.trt_objects.size()) return; string prefix = indentation + "\t"; conf_file << prefix.c_str() << "<TripPoint>\n"; subtitute_string(SENSOR_DEV, rel_obj.target_sensor); conf_file << prefix.c_str() << "\t" << "<SensorType>" << rel_obj.target_sensor.c_str() << "</SensorType>\n"; conf_file << prefix.c_str() << "\t" << "<Temperature>" << "*" << "</Temperature>\n"; conf_file << prefix.c_str() << "\t" << "<type>" << "passive" "</type>\n"; conf_file << prefix.c_str() << "\t" << "<ControlType>" << "SEQUENTIAL" << "</ControlType>\n"; for (unsigned int j = 0; j < rel_obj.trt_objects.size(); ++j) { union trt_object *object = (union trt_object *) rel_obj.trt_objects[j]; string device_name = object->acpi_trt_entry.source_device; conf_file << prefix.c_str() << "\t" << "<CoolingDevice>\n"; subtitute_string(TARGET_DEV, device_name); conf_file << prefix.c_str() << "\t\t" << "<type>" << device_name.c_str() << "</type>\n"; conf_file << prefix.c_str() << "\t\t" << "<influence>" << object->acpi_trt_entry.influence << "</influence>\n"; conf_file << prefix.c_str() << "\t\t" << "<SamplingPeriod>" << object->acpi_trt_entry.sample_period * 100 / 1000 << "</SamplingPeriod>\n"; conf_file << prefix.c_str() << "\t" << "</CoolingDevice>\n"; } conf_file << prefix.c_str() << "</TripPoint>\n"; } void cthd_acpi_rel::add_active_trip_point(rel_object_t &rel_obj) { if (!rel_obj.art_objects.size()) return; string prefix = indentation + "\t"; conf_file << prefix.c_str() << "<TripPoint>\n"; subtitute_string(SENSOR_DEV, rel_obj.target_sensor); conf_file << prefix.c_str() << "\t" << "<SensorType>" << rel_obj.target_sensor.c_str() << "</SensorType>\n"; conf_file << prefix.c_str() << "\t" << "<Temperature>" << "*" << "</Temperature>\n"; conf_file << prefix.c_str() << "\t" << "<type>" << "active" "</type>\n"; conf_file << prefix.c_str() << "\t" << "<ControlType>" << "SEQUENTIAL" << "</ControlType>\n"; for (unsigned int j = 0; j < rel_obj.art_objects.size(); ++j) { union art_object *object = (union art_object *) rel_obj.art_objects[j]; string device_name = object->acpi_art_entry.source_device; conf_file << prefix.c_str() << "\t" << "<CoolingDevice>\n"; subtitute_string(TARGET_DEV, device_name); conf_file << prefix.c_str() << "\t\t" << "<type>" << device_name.c_str() << "</type>\n"; conf_file << prefix.c_str() << "\t\t" << "<influence>" << object->acpi_art_entry.weight << "</influence>\n"; conf_file << prefix.c_str() << "\t" << "</CoolingDevice>\n"; } conf_file << prefix.c_str() << "</TripPoint>\n"; } void cthd_acpi_rel::create_thermal_zone(string type) { unsigned int i; indentation += "\t"; string prefix = indentation; indentation += '\t'; parse_target_devices(); for (i = 0; i < rel_list.size(); ++i) { if (rel_list[i].target_device == "") { PRINT_ERROR("Empty target, skipping ..\n"); continue; } conf_file << prefix.c_str() << "<ThermalZone>" << "\n"; subtitute_string(SOURCE_DEV, rel_list[i].target_device); conf_file << prefix.c_str() << "\t" << "<Type>" << rel_list[i].target_device.c_str() << "</Type>" << "\n"; conf_file << prefix.c_str() << "\t" << "<TripPoints>" << "\n"; indentation += "\t"; add_passive_trip_point(rel_list[i]); add_active_trip_point(rel_list[i]); conf_file << prefix.c_str() << "\t" << "</TripPoints>" << "\n"; conf_file << prefix.c_str() << "</ThermalZone>" << "\n"; } } void cthd_acpi_rel::parse_target_devices() { union trt_object *trt = (union trt_object *) trt_data; union art_object *art = (union art_object *) art_data; union psvt_object *psvt = (union psvt_object *) psvt_data; unsigned int i; for (i = 0; i < trt_count; i++) { rel_object_t rel_obj(trt[i].acpi_trt_entry.target_device); vector<rel_object_t>::iterator find_iter; find_iter = find_if(rel_list.begin(), rel_list.end(), object_finder(trt[i].acpi_trt_entry.target_device)); if (find_iter == rel_list.end()) { rel_obj.trt_objects.push_back(&trt[i]); rel_list.push_back(rel_obj); } else find_iter->trt_objects.push_back(&trt[i]); } for (i = 0; i < art_count; i++) { rel_object_t rel_obj(art[i].acpi_art_entry.target_device); vector<rel_object_t>::iterator find_iter; find_iter = find_if(rel_list.begin(), rel_list.end(), object_finder(art[i].acpi_art_entry.target_device)); if (find_iter == rel_list.end()) { rel_obj.art_objects.push_back(&art[i]); rel_list.push_back(rel_obj); } else find_iter->art_objects.push_back(&art[i]); } PRINT_DEBUG("parse target devices\n"); for (i = 0; i < psvt_count; i++) { rel_object_t rel_obj(psvt[i].acpi_psvt_entry.target_device, psvt[i].acpi_psvt_entry.passive_temp, psvt[i].acpi_psvt_entry.step_size); PRINT_DEBUG("parse target devices index :%d\n", i); vector<rel_object_t>::iterator find_iter; find_iter = find_if(rel_list.begin(), rel_list.end(), object_finder(psvt[i].acpi_psvt_entry.target_device)); if (find_iter == rel_list.end()) { rel_obj.psvt_objects.push_back(&psvt[i]); rel_list.push_back(rel_obj); } else find_iter->psvt_objects.push_back(&psvt[i]); } } void cthd_acpi_rel::create_thermal_zones() { string prefix = indentation; conf_file << prefix.c_str() << "<ThermalZones>" << "\n"; // Read ... create_thermal_zone("test"); // conf_file << prefix.c_str() << "</ThermalZones>" << "\n"; } void cthd_acpi_rel::create_platform_conf() { // string line; string prefix; indentation += "\t"; prefix = indentation; conf_file << prefix.c_str() << "<Name>" << "_TRT export" << "</Name>" << "\n"; ifstream product_name("/sys/class/dmi/id/product_name"); conf_file << indentation.c_str() << "<ProductName>"; #if 0 if (product_name.is_open() && getline(product_name, line)) { #else char buffer[256]; if (product_name.is_open() && product_name.getline(buffer, sizeof(buffer))) { string line(buffer); #endif conf_file << line.c_str(); } else conf_file << "*" << "\n"; conf_file << prefix.c_str() << "</ProductName>" << "\n"; } #if LOG_DEBUG_INFO == 1 void cthd_acpi_rel::dump_trt() { union trt_object *trt = (union trt_object *) trt_data; unsigned int i; for (i = 0; i < trt_count; i++) { PRINT_DEBUG("TRT %d: SRC %s:\t", i, trt[i].acpi_trt_entry.source_device); PRINT_DEBUG("TRT %d: TGT %s:\t", i, trt[i].acpi_trt_entry.target_device); PRINT_DEBUG("TRT %d: INF %llu:\t", i, trt[i].acpi_trt_entry.influence); PRINT_DEBUG("TRT %d: SMPL %llu:\n", i, trt[i].acpi_trt_entry.sample_period); } } void cthd_acpi_rel::dump_psvt() { union psvt_object *trt = (union psvt_object*) psvt_data; unsigned int i; for (i = 0; i < psvt_count; i++) { PRINT_DEBUG("PSVT %d\n", i); PRINT_DEBUG("PSVT %d: SRC %s:\n", i, trt[i].acpi_psvt_entry.source_device); PRINT_DEBUG("TRT %d: TGT %s:\n", i, trt[i].acpi_psvt_entry.target_device); PRINT_DEBUG("TRT %d: SMPL %llu:\n", i, trt[i].acpi_psvt_entry.sample_period); PRINT_DEBUG("TRT %d: temp %d:\n", i, DECI_KELVIN_TO_CELSIUS(trt[i].acpi_psvt_entry.passive_temp)); PRINT_DEBUG("TRT %d: step %llu:\n", i, trt[i].acpi_psvt_entry.step_size); if (trt[i].acpi_psvt_entry.control_knob_type == ACPI_TYPE_STRING) PRINT_DEBUG("control limit:%s\n", trt[i].acpi_psvt_entry.limit.string); else PRINT_DEBUG("control_limit: %llu\n", trt[i].acpi_psvt_entry.limit.integer); } } void cthd_acpi_rel::dump_art() { union art_object *art = (union art_object *) art_data; unsigned int i; for (i = 0; i < art_count; i++) { PRINT_DEBUG("ART %d: SRC %s:\t", i, art[i].acpi_art_entry.source_device); PRINT_DEBUG("ART %d: TGT %s:\t", i, art[i].acpi_art_entry.target_device); PRINT_DEBUG("ART %d: WT %llu:\n", i, art[i].acpi_art_entry.weight); } } #else void cthd_acpi_rel::dump_trt() { } void cthd_acpi_rel::dump_art() { } #endif void cthd_acpi_rel::create_platform_pref(int perf) { if (perf) conf_file << indentation.c_str() << "<Preference>PERFORMANCE</Preference>" << "\n"; else conf_file << indentation.c_str() << "<Preference>QUIET</Preference>" << "\n"; } 07070100000065000081A400000000000000000000000166853FAC00000A9D000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_trt_art_reader.h/* * thd_trt_art_reader.h: Interface for configuration using ACPI * _ART and _TRT tables * Copyright (C) 2014 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_TRT_ART_READER_H_ #define THD_TRT_ART_READER_H_ #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/ioctl.h> #include <iostream> #include <fstream> #include <list> #include <vector> #include <algorithm> struct rel_object_t { std::string target_device; std::string target_sensor; std::vector<void *> trt_objects; std::vector<void *> art_objects; std::vector<void *> psvt_objects; int temperature; int step; rel_object_t(std::string name, int _temperature = 0, int _step = 0) { target_device = name; target_sensor = name; temperature = _temperature; step = _step; } }; struct object_finder { object_finder(char *key) : obj_key(key) { } bool operator()(const rel_object_t& o) const { return obj_key == o.target_device; } const std::string obj_key; }; class cthd_acpi_rel { private: std::string rel_cdev; std::string xml_hdr; std::string conf_begin; std::string conf_end; std::string output_file_name; std::ofstream conf_file; std::vector<rel_object_t> rel_list; unsigned char *trt_data; unsigned int trt_count; unsigned char *art_data; unsigned int art_count; unsigned char *psvt_data; unsigned int psvt_count; int read_trt(); void dump_trt(); void create_platform_conf(); void create_platform_pref(int perf); void create_thermal_zones(); void create_thermal_zone(std::string type); void add_passive_trip_point(rel_object_t &rel_obj); void add_psvt_trip_point(rel_object_t &rel_obj); void add_active_trip_point(rel_object_t &rel_obj); void parse_target_devices(); int read_art(); void dump_art(); int read_psvt(); int process_psvt(std::string file_name); void dump_psvt(); public: std::string indentation; cthd_acpi_rel(); int generate_conf(std::string file_name); }; #endif /* THD_TRT_ART_READER_H_ */ 07070100000066000081A400000000000000000000000166853FAC000023F1000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone.cpp/* * thd_zone.cpp: thermal zone class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ /* This class implements parent thermal zone/sensor. It is included * in a thermal engine. During initialization, it establishes a * relationship between cooling devices and trip points (where * some action needs to be taken). * When it gets a notification for a change, it reads the temperature * from sensors and uses cthd_trip point to schedule action on the event * if required. */ #include "thd_zone.h" #include "thd_engine.h" cthd_zone::cthd_zone(int _index, std::string control_path, sensor_relate_t rel) : index(_index), zone_sysfs(control_path.c_str()), zone_temp(0), zone_active( false), zone_cdev_binded_status(false), type_str(), sensor_rel( rel) { thd_log_debug("Added zone index:%d \n", index); } cthd_zone::~cthd_zone() { for (unsigned int i = 0; i < trip_points.size(); ++i) { trip_points[i].delete_cdevs(); } trip_points.clear(); sensors.clear(); } void cthd_zone::thermal_zone_temp_change(int id, unsigned int temp, int pref) { int i, count; bool reset = false; count = trip_points.size(); for (i = 0; i < count; ++i) { cthd_trip_point &trip_point = trip_points[i]; trip_point.thd_trip_point_check(id, temp, pref, &reset); // Force all cooling devices to min state if (reset) { zone_reset(0); break; } } } void cthd_zone::update_zone_preference() { if (!zone_active) return; thd_log_debug("update_zone_preference\n"); for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; zone_temp = sensor->read_temperature(); thermal_zone_temp_change(sensor->get_index(), 0, thd_engine->get_preference()); } for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; zone_temp = sensor->read_temperature(); thermal_zone_temp_change(sensor->get_index(), zone_temp, thd_engine->get_preference()); } } int cthd_zone::read_user_set_psv_temp() { std::stringstream filename; int temp = -1; filename << TDRUNDIR << "/" << "thd_user_psv_temp." << type_str << "." << "conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (ifs.good()) { ifs >> temp; thd_log_info("read_user_set_psv_temp %d\n", temp); if (temp < 1000) temp = -1; } ifs.close(); return temp; } void cthd_zone::sort_and_update_poll_trip() { thd_log_debug("sort_and_update_poll_trip: trip_points_size =%zu\n", trip_points.size()); for (unsigned int i = 0; i < trip_points.size(); ++i) { if (trip_points[i].get_trip_type() == POLLING) { thd_log_debug("polling trip already present\n"); trip_points.erase(trip_points.begin() + i); break; } } if (trip_points.size()) { unsigned int polling_trip = 0; std::sort(trip_points.begin(), trip_points.end(), trip_sort); if (trip_points.size()) polling_trip = trip_points[0].get_trip_temp(); unsigned int poll_offset = polling_trip * def_async_trip_offset_pct / 100; if (poll_offset < def_async_trip_offset) poll_offset = def_async_trip_offset; polling_trip -= poll_offset; for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; sensor->set_threshold(0, polling_trip); // If the poll trip is already present then simply update // the trip, instead of creating a new one. cthd_trip_point trip_pt_polling(trip_points.size(), POLLING, polling_trip, 0, index, sensor->get_index()); trip_pt_polling.thd_trip_point_set_control_type(PARALLEL); trip_points.push_back(trip_pt_polling); } } } int cthd_zone::zone_update() { int ret; if (zone_bind_sensors() != THD_SUCCESS) { thd_log_warn("Zone update failed: unable to bind \n"); return THD_ERROR; } ret = read_trip_points(); if (ret != THD_SUCCESS) return THD_ERROR; int usr_psv_temp = read_user_set_psv_temp(); if (usr_psv_temp > 0) { cthd_trip_point trip_pt_passive(0, PASSIVE, usr_psv_temp, 0, index, DEFAULT_SENSOR_ID); update_highest_trip_temp(trip_pt_passive); } ret = read_cdev_trip_points(); if (ret != THD_SUCCESS) { thd_log_info("No cdev trip points loaded for zone index %d\n", index); // Don't bail out as they may be attached by thermal relation tables } sort_and_update_poll_trip(); return THD_SUCCESS; } void cthd_zone::read_zone_temp() { if (zone_active) { unsigned int temp; zone_temp = 0; for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; temp = sensor->read_temperature(); if (zone_temp < temp) zone_temp = temp; if (sensor_rel == SENSOR_INDEPENDENT) thermal_zone_temp_change(sensor->get_index(), temp, thd_engine->get_preference()); } if (sensor_rel == SENSORS_CORELATED && zone_temp) thermal_zone_temp_change(sensors[0]->get_index(), zone_temp, thd_engine->get_preference()); } } void cthd_zone::zone_temperature_notification(int type, int data) { read_zone_temp(); } void cthd_zone::zone_reset(int force) { int i, count; if (zone_active) { count = trip_points.size(); for (i = count - 1; i >= 0; --i) { cthd_trip_point &trip_point = trip_points[i]; trip_point.thd_trip_cdev_state_reset(force); } } } int cthd_zone::bind_cooling_device(trip_point_type_t type, unsigned int trip_temp, cthd_cdev *cdev, int influence, int sampling_period, int target_state_valid, int target_state) { int i, count; bool added = false; // trip_temp = 0 is a special case, where it will add to first matched type count = trip_points.size(); for (i = 0; i < count; ++i) { cthd_trip_point &trip_point = trip_points[i]; if ((trip_point.get_trip_type() == type) && (trip_point.get_trip_temp() > 0) && (trip_temp == 0 || trip_point.get_trip_temp() == trip_temp)) { trip_point.thd_trip_point_add_cdev(*cdev, influence, sampling_period, target_state_valid, target_state); added = true; zone_cdev_set_binded(); break; } } #if 0 // Check again, if we really need this logic if (!added && trip_temp) { // Create a new trip point and add only if trip_temp is valid cthd_trip_point trip_pt(count, type, trip_temp, 0, index, DEFAULT_SENSOR_ID); trip_points.push_back(trip_pt); added = true; } #endif if (added) return THD_SUCCESS; else return THD_ERROR; } int cthd_zone::update_max_temperature(int max_temp) { std::stringstream filename; std::stringstream temp_str; filename << TDRUNDIR << "/" << "thd_user_set_max." << type_str << "." << "conf"; std::ofstream fout(filename.str().c_str()); if (!fout.good()) { return THD_ERROR; } temp_str << max_temp; fout << temp_str.str(); fout.close(); return THD_SUCCESS; } int cthd_zone::update_psv_temperature(int psv_temp) { std::stringstream filename; std::stringstream temp_str; filename << TDRUNDIR << "/" << "thd_user_psv_temp." << type_str << "." << "conf"; std::ofstream fout(filename.str().c_str()); if (!fout.good()) { return THD_ERROR; } temp_str << psv_temp; fout << temp_str.str(); fout.close(); return THD_SUCCESS; } void cthd_zone::add_trip(cthd_trip_point &trip, int force) { if (force) { trip_points.push_back(trip); sort_and_update_poll_trip(); return; } bool add = true; for (unsigned int j = 0; j < trip_points.size(); ++j) { if (trip_points[j].get_trip_type() == trip.get_trip_type()) { thd_log_debug("updating existing trip temp \n"); trip_points[j] = trip; add = false; break; } } if (add) trip_points.push_back(trip); sort_and_update_poll_trip(); } void cthd_zone::update_highest_trip_temp(cthd_trip_point &trip) { if (trip_points.size()) { thd_log_info("trip_points.size():%zu\n", trip_points.size()); for (unsigned int j = trip_points.size() - 1;; --j) { if (trip_points[j].get_trip_type() == trip.get_trip_type()) { thd_log_info("updating existing trip temp \n"); trip_points[j].update_trip_temp(trip.get_trip_temp()); trip_points[j].update_trip_hyst(trip.get_trip_hyst()); break; } } sort_and_update_poll_trip(); } } void cthd_zone::update_trip_temp(cthd_trip_point &trip) { for (unsigned int j = 0; j < trip_points.size(); ++j) { if (trip_points[j].get_trip_type() == trip.get_trip_type()) { thd_log_debug("updating existing trip temp \n"); trip_points[j].update_trip_temp(trip.get_trip_temp()); trip_points[j].update_trip_hyst(trip.get_trip_hyst()); break; } } sort_and_update_poll_trip(); } 07070100000067000081A400000000000000000000000166853FAC00001737000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone.h/* * thd_zone.h: thermal zone class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_H #define THD_ZONE_H #include <vector> #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_cdev.h" #include "thd_trip_point.h" #include "thd_sensor.h" typedef struct { int zone; int type; unsigned int data; } thermal_zone_notify_t; // If the zone has multiple sensors, there are two possibilities // Either the are independent, means that each has own set of trip points // Or related. In this case one trip point. Here we take max of the sensor reading // and then apply trip typedef enum { SENSOR_INDEPENDENT, SENSORS_CORELATED } sensor_relate_t; class cthd_zone { protected: int index; std::vector<cthd_trip_point> trip_points; std::string temperature_sysfs_path; csys_fs zone_sysfs; unsigned int zone_temp; bool zone_active; bool zone_cdev_binded_status; std::string type_str; std::vector<cthd_sensor *> sensors; sensor_relate_t sensor_rel; virtual int zone_bind_sensors() = 0; void thermal_zone_temp_change(int id, unsigned int temp, int pref); private: void sort_and_update_poll_trip(); public: static const unsigned int def_async_trip_offset = 5000; static const unsigned int def_async_trip_offset_pct = 10; cthd_zone(int _index, std::string control_path, sensor_relate_t rel = SENSOR_INDEPENDENT); virtual ~cthd_zone(); void zone_temperature_notification(int type, int data); int zone_update(); virtual void update_zone_preference(); void zone_reset(int force = 0); virtual int read_trip_points() = 0; virtual int read_cdev_trip_points() = 0; virtual void read_zone_temp(); int get_zone_index() { return index; } void add_trip(cthd_trip_point &trip, int force = 0); void update_trip_temp(cthd_trip_point &trip); void update_highest_trip_temp(cthd_trip_point &trip); void set_zone_active() { zone_active = true; } ; void set_zone_inactive() { zone_active = false; } bool zone_active_status() { return zone_active; } bool zone_cdev_binded() { return zone_cdev_binded_status; } void zone_cdev_set_binded() { thd_log_debug("zone %s bounded \n", type_str.c_str()); zone_cdev_binded_status = true; } std::string get_zone_type() { return type_str; } std::string get_zone_path() { return zone_sysfs.get_base_path(); } void set_zone_type(std::string type) { type_str = type; } void bind_sensor(cthd_sensor *sensor) { for (unsigned int i = 0; i < sensors.size(); ++i) { if (sensors[i] == sensor) return; } sensors.push_back(sensor); } // Even if one sensor, it is using doesn't // provide async control, return false bool check_sensor_async_status() { for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor = sensors[i]; if (!sensor->check_async_capable()) { return false; } } return true; } unsigned int get_trip_count() { return trip_points.size(); } int update_max_temperature(int max_temp); int update_psv_temperature(int psv_temp); int read_user_set_psv_temp(); int bind_cooling_device(trip_point_type_t type, unsigned int trip_temp, cthd_cdev *cdev, int influence, int sampling_period = 0, int target_state_valid = 0, int target_state = 0); int get_sensor_count() { return sensors.size(); } cthd_sensor *get_sensor_at_index(unsigned int index) { if (index < sensors.size()) return sensors[index]; else return NULL; } cthd_trip_point *get_trip_at_index(unsigned int index) { if (index < trip_points.size()) return &trip_points[index]; else return NULL; } #ifdef ANDROID void trip_delete_all() { if (!trip_points.size()) return; for (unsigned int i = 0; i < trip_points.size(); ++i) { trip_point_type_t trip_type = trip_points[i].get_trip_type(); if (trip_type==HOT|| trip_type==CRITICAL || trip_type==MAX ) { thd_log_info("keep cdev trip_point %d temp=%d\n", i, trip_points[i].get_trip_temp()); } else trip_points[i].delete_cdevs(); } std::vector<cthd_trip_point>::iterator it; for (it=trip_points.begin(); it!=trip_points.end(); ) { trip_point_type_t trip_type = it->get_trip_type(); if (trip_type==HOT|| trip_type==CRITICAL || trip_type==MAX ) { thd_log_info("keep trip_point temp=%d\n", it->get_trip_temp()); ++it; } else { thd_log_info("remove trip_point temp=%d\n", it->get_trip_temp()); it=trip_points.erase(it); } } } #else void trip_delete_all() { if (!trip_points.size()) return; for (unsigned int i = 0; i < trip_points.size(); ++i) { trip_points[i].delete_cdevs(); } trip_points.clear(); } #endif void zone_dump() { if (!zone_active) return; thd_log_info("\n"); thd_log_info("Zone %d: %s, Active:%d Bind:%d Sensor_cnt:%lu\n", index, type_str.c_str(), zone_active, zone_cdev_binded_status, (unsigned long) sensors.size()); thd_log_info("..sensors.. \n"); for (unsigned int i = 0; i < sensors.size(); ++i) { sensors[i]->sensor_dump(); } thd_log_info("..trips.. \n"); for (unsigned int i = 0; i < trip_points.size(); ++i) { trip_points[i].trip_dump(); } thd_log_info("\n"); } ; }; #endif 07070100000068000081A400000000000000000000000166853FAC00001D37000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_cpu.cpp/* * thd_zone_dts.cpp: thermal engine DTS class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * * This implementation allows using core temperature interface. */ /* Implementation of DTS sensor Zone. This * - Identifies DTS sensors, and use maximum reported temperature to control * - Prioritize the cooling device order * - Sets one control point starting from target temperature (max) not critical. * Very first time it reaches this temperature cthd_model, calculates a set point when * cooling device needs to be activated. * */ #include <dirent.h> #include "thd_zone_cpu.h" #include "thd_engine_default.h" #include "thd_cdev_order_parser.h" const char *def_cooling_devices[] = { "rapl_controller", "intel_pstate", "intel_powerclamp", "cpufreq", "Processor", NULL }; cthd_zone_cpu::cthd_zone_cpu(int index, std::string path, int package_id) : cthd_zone(index, path, SENSORS_CORELATED), dts_sysfs(path.c_str()), critical_temp( 0), max_temp(0), psv_temp(0), trip_point_cnt(0), sensor_mask(0), phy_package_id( package_id), pkg_thres_th_zone(-1), pkg_temp_poll_enable(false) { type_str = "cpu"; thd_log_debug("zone dts syfs: %s, package id %d \n", path.c_str(), package_id); } int cthd_zone_cpu::init() { int temp = 0; bool found = false; max_temp = 0; critical_temp = 0; // Calculate the temperature trip points using the settings in coretemp for (int i = 0; i < max_dts_sensors; ++i) { std::stringstream temp_crit_str; std::stringstream temp_max_str; temp_crit_str << "temp" << i << "_crit"; temp_max_str << "temp" << i << "_max"; if (dts_sysfs.exists(temp_crit_str.str())) { std::string temp_str; dts_sysfs.read(temp_crit_str.str(), temp_str); std::istringstream(temp_str) >> temp; if (critical_temp == 0 || temp < critical_temp) critical_temp = temp; } if (dts_sysfs.exists(temp_max_str.str())) { // Set which index is present sensor_mask = sensor_mask | (1 << i); std::string temp_str; dts_sysfs.read(temp_max_str.str(), temp_str); std::istringstream(temp_str) >> temp; if (max_temp == 0 || temp < max_temp) max_temp = temp; found = true; } } if (!found) { thd_log_error("DTS temperature path not found \n"); return THD_ERROR; } if (critical_temp == 0) critical_temp = def_critical_temp; if (max_temp == 0) { max_temp = critical_temp - def_offset_from_critical; thd_log_info("Force max temp to %d\n", max_temp); } if ((critical_temp - max_temp) < def_offset_from_critical) { max_temp = critical_temp - def_offset_from_critical; thd_log_info("Buggy max temp: to close to critical %d\n", max_temp); } // max_temperature is where the Fan would have been activated fully // psv_temp is set more so that in the case if Fan is not able to // control temperature, the passive temperature will be acted on psv_temp = max_temp + ((critical_temp - max_temp) / 2); thd_log_info("Core temp DTS :critical %d, max %d, psv %d\n", critical_temp, max_temp, psv_temp); return THD_SUCCESS; } int cthd_zone_cpu::load_cdev_xml(cthd_trip_point &trip_pt, std::vector<std::string> &list) { cthd_cdev *cdev; for (unsigned int i = 0; i < list.size(); ++i) { thd_log_debug("- %s\n", list[i].c_str()); cdev = thd_engine->search_cdev(list[i]); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); } } return THD_SUCCESS; } int cthd_zone_cpu::parse_cdev_order() { cthd_cdev_order_parse parser; std::vector<std::string> order_list; int ret = THD_ERROR; if ((ret = parser.parser_init()) == THD_SUCCESS) { if ((ret = parser.start_parse()) == THD_SUCCESS) { ret = parser.get_order_list(order_list); if (ret == THD_SUCCESS) { cthd_trip_point trip_pt_passive(trip_point_cnt, PASSIVE, psv_temp, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt_passive.thd_trip_point_set_control_type(SEQUENTIAL); load_cdev_xml(trip_pt_passive, order_list); trip_points.push_back(trip_pt_passive); trip_point_cnt++; } } parser.parser_deinit(); return ret; } return ret; } int cthd_zone_cpu::read_trip_points() { int ret; cthd_cdev *cdev; int i; ret = parse_cdev_order(); if (ret == THD_SUCCESS) { thd_log_info("CDEVS order specified in thermal-cpu-cdev-order.xml\n"); return THD_SUCCESS; } cthd_trip_point trip_pt_passive(trip_point_cnt, PASSIVE, psv_temp, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt_passive.thd_trip_point_set_control_type(SEQUENTIAL); i = 0; while (def_cooling_devices[i]) { cdev = thd_engine->search_cdev(def_cooling_devices[i]); if (cdev) { trip_pt_passive.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); } ++i; } trip_points.push_back(trip_pt_passive); trip_point_cnt++; // Add active trip point at the end cthd_trip_point trip_pt_active(trip_point_cnt, ACTIVE, max_temp, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt_active.thd_trip_point_set_control_type(SEQUENTIAL); cdev = thd_engine->search_cdev("Fan"); if (cdev) { trip_pt_active.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); trip_points.push_back(trip_pt_active); trip_point_cnt++; } return THD_SUCCESS; } int cthd_zone_cpu::zone_bind_sensors() { cthd_sensor *sensor; int status = THD_ERROR; bool async_sensor = false; if (init() != THD_SUCCESS) return THD_ERROR; if (!thd_engine->rt_kernel_status()) { sensor = thd_engine->search_sensor("pkg-temp-0"); if (sensor) { bind_sensor(sensor); async_sensor = true; } } if (!thd_engine->rt_kernel_status()) { sensor = thd_engine->search_sensor("x86_pkg_temp"); if (sensor) { bind_sensor(sensor); async_sensor = true; } } sensor = thd_engine->search_sensor("soc_dts0"); if (sensor) { bind_sensor(sensor); async_sensor = true; } if (async_sensor) { sensor = thd_engine->search_sensor("hwmon"); if (sensor) { sensor->set_async_capable(true); } return THD_SUCCESS; } // No package temp sensor fallback to core temp int cnt = 0; unsigned int mask = 0x1; do { if (sensor_mask & mask) { std::stringstream temp_input_str; temp_input_str << "temp" << cnt << "_input"; cthd_sensor *sensor; sensor = thd_engine->search_sensor(temp_input_str.str()); if (sensor) { bind_sensor(sensor); status = THD_SUCCESS; } } mask = (mask << 1); cnt++; } while (mask != 0); if (status != THD_SUCCESS) { thd_log_info("Trying to bind hwmon sensor \n"); sensor = thd_engine->search_sensor("hwmon"); if (sensor) { bind_sensor(sensor); status = THD_SUCCESS; thd_log_info("Bind hwmon sensor \n"); } } return status; } int cthd_zone_cpu::read_cdev_trip_points() { return THD_SUCCESS; } 07070100000069000081A400000000000000000000000166853FAC000006F3000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_cpu.h/* * thd_zone_dts.h: thermal engine DTS class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * * This interface allows overriding per zone read data from sysfs for * buggy BIOS. */ #ifndef THD_ZONE_DTS_H #define THD_ZONE_DTS_H #include "thd_zone.h" #include <vector> class cthd_zone_cpu: public cthd_zone { protected: csys_fs dts_sysfs; int critical_temp; int max_temp; int psv_temp; int trip_point_cnt; unsigned int sensor_mask; int phy_package_id; std::vector<std::string> sensor_sysfs; int init(); int parse_cdev_order(); int pkg_thres_th_zone; bool pkg_temp_poll_enable; public: static const int max_dts_sensors = 16; static const int def_hystersis = 0; static const int def_offset_from_critical = 10000; static const int def_critical_temp = 100000; cthd_zone_cpu(int count, std::string path, int package_id); int load_cdev_xml(cthd_trip_point &trip_pt, std::vector<std::string> &list); virtual int read_trip_points(); int read_cdev_trip_points(); int zone_bind_sensors(); }; #endif 0707010000006A000081A400000000000000000000000166853FAC0000085B000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_dynamic.cpp/* * thd_zone_generic.cpp: zone implementation for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_zone_dynamic.h" #include "thd_engine.h" cthd_zone_dynamic::cthd_zone_dynamic(int index, std::string _name, unsigned int _trip_temp, trip_point_type_t _trip_type, std::string _sensor, std::string _cdev) : cthd_zone(index, ""), name(_name), trip_temp(_trip_temp), trip_type(_trip_type), sensor_name( _sensor), cdev_name(_cdev) { type_str = name; } int cthd_zone_dynamic::read_trip_points() { cthd_sensor *sensor = thd_engine->search_sensor(sensor_name); if (!sensor) { thd_log_warn("dynamic sensor: invalid sensor type \n"); return THD_ERROR; } thd_log_info("XX index = %d\n", index); cthd_trip_point trip_pt(0, trip_type, trip_temp, 0, index, sensor->get_index()); trip_points.push_back(trip_pt); cthd_cdev *cdev = thd_engine->search_cdev(cdev_name); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); zone_cdev_set_binded(); } else return THD_ERROR; return THD_SUCCESS; } int cthd_zone_dynamic::read_cdev_trip_points() { return 0; } int cthd_zone_dynamic::zone_bind_sensors() { cthd_sensor *sensor = thd_engine->search_sensor(sensor_name); if (!sensor) { thd_log_warn("dynamic sensor: invalid sensor type \n"); return THD_ERROR; } bind_sensor(sensor); return THD_SUCCESS; } 0707010000006B000081A400000000000000000000000166853FAC0000058B000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_dynamic.h/* * thd_zone_generic.cpp: zone implementation for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_DYNAMIC_H_ #define THD_ZONE_DYNAMIC_H_ #include "thd_zone.h" class cthd_zone_dynamic: public cthd_zone { private: std::string name; unsigned int trip_temp; trip_point_type_t trip_type; std::string sensor_name; std::string cdev_name; public: cthd_zone_dynamic(int index, std::string _name, unsigned int _trip_temp, trip_point_type_t _trip_type, std::string _sensor, std::string _cdev); virtual int read_trip_points(); virtual int read_cdev_trip_points(); virtual int zone_bind_sensors(); }; #endif /* THD_ZONE_DYNAMIC_H_ */ 0707010000006C000081A400000000000000000000000166853FAC00000D17000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_generic.cpp/* * thd_zone_generic.cpp: zone implementation for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_zone_generic.h" #include "thd_engine.h" cthd_zone_generic::cthd_zone_generic(int index, int _config_index, std::string type) : cthd_zone(index, ""), config_index(_config_index) { type_str = type; } int cthd_zone_generic::read_trip_points() { thermal_zone_t *zone_config = thd_engine->parser.get_zone_dev_index( config_index); int trip_point_cnt = 0; if (!zone_config) return THD_ERROR; for (unsigned int i = 0; i < zone_config->trip_pts.size(); ++i) { trip_point_t &trip_pt_config = zone_config->trip_pts[i]; if (!trip_pt_config.temperature) continue; cthd_sensor *sensor = thd_engine->search_sensor( trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type \n"); continue; } sensor_list.push_back(sensor); cthd_trip_point trip_pt(trip_point_cnt, trip_pt_config.trip_pt_type, trip_pt_config.temperature, trip_pt_config.hyst, index, sensor->get_index(), trip_pt_config.control_type); if (trip_pt_config.dependency.dependency) { trip_pt.set_dependency(trip_pt_config.dependency.cdev, trip_pt_config.dependency.state); } // bind cdev for (unsigned int j = 0; j < trip_pt_config.cdev_trips.size(); ++j) { cthd_cdev *cdev = thd_engine->search_cdev( trip_pt_config.cdev_trips[j].type); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, trip_pt_config.cdev_trips[j].influence, trip_pt_config.cdev_trips[j].sampling_period, trip_pt_config.cdev_trips[j].target_state_valid, trip_pt_config.cdev_trips[j].target_state); zone_cdev_set_binded(); } } trip_points.push_back(trip_pt); ++trip_point_cnt; } if (!trip_points.size()) { thd_log_info( " cthd_zone_generic::read_trip_points fail: No valid trips\n"); return THD_ERROR; } return 0; } int cthd_zone_generic::read_cdev_trip_points() { return 0; } int cthd_zone_generic::zone_bind_sensors() { cthd_sensor *sensor; thermal_zone_t *zone_config = thd_engine->parser.get_zone_dev_index( config_index); if (!zone_config) return THD_ERROR; sensor = NULL; for (unsigned int i = 0; i < zone_config->trip_pts.size(); ++i) { trip_point_t &trip_pt_config = zone_config->trip_pts[i]; sensor = thd_engine->search_sensor(trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type %s\n", trip_pt_config.sensor_type.c_str()); continue; } bind_sensor(sensor); } if (!sensor) return THD_ERROR; return THD_SUCCESS; } 0707010000006D000081A400000000000000000000000166853FAC000004F4000000000000000000000000000000000000003B00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_generic.h/* * thd_zone_generic.h: zone interface for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_GENERIC_H_ #define THD_ZONE_GENERIC_H_ #include "thd_zone.h" class cthd_zone_generic: public cthd_zone { private: int config_index; std::vector<cthd_sensor *> sensor_list; public: cthd_zone_generic(int index, int _config_index, std::string type); virtual int read_trip_points(); virtual int read_cdev_trip_points(); virtual int zone_bind_sensors(); }; #endif /* THD_ZONE_GENERIC_H_ */ 0707010000006E000081A400000000000000000000000166853FAC00000C7B000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_kbl_amdgpu.cpp/* * thd_zone_kbl_amdgpu.cpp: thermal zone for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_engine_default.h" #include "thd_sys_fs.h" #include "thd_zone_kbl_amdgpu.h" cthd_zone_kbl_amdgpu::cthd_zone_kbl_amdgpu(int index): cthd_zone(index, ""), sensor(NULL), dts_sysfs("/sys/class/hwmon/"){ type_str = "amdgpu"; } int cthd_zone_kbl_amdgpu::zone_bind_sensors() { cthd_sensor *sensor; sensor = thd_engine->search_sensor("amdgpu-temperature"); if (sensor) { bind_sensor(sensor); return THD_SUCCESS; } return THD_ERROR; } int cthd_zone_kbl_amdgpu::read_trip_points() { cthd_cdev *cdev; DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/hwmon/"; int crit_temp = 0; thd_log_info("cthd_zone_kbl_amdgpu::read_trip_points \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { // Check name std::string name_path = base_path + entry->d_name + "/name"; std::ifstream ifs(name_path.c_str(), std::ifstream::in); if (ifs.good()) { std::string line; while (std::getline(ifs, line)) { if (line == "amdgpu") { std::string path = base_path + entry->d_name + "/"; csys_fs hwmon_sysfs(path.c_str()); if (!hwmon_sysfs.exists("temp1_crit")) { thd_log_info("path failed %s\n", path.c_str()); closedir(dir); return THD_ERROR; } int ret = hwmon_sysfs.read("temp1_crit", &crit_temp); if (ret < 0) { thd_log_info("crit temp failed\n"); closedir(dir); return THD_ERROR; } } } ifs.close(); } } closedir(dir); } if (!crit_temp) return THD_ERROR; cdev = thd_engine->search_cdev("amdgpu"); if (!cdev) { thd_log_info("amdgpu failed\n"); return THD_ERROR; } cthd_trip_point trip_pt_passive(0, PASSIVE, crit_temp - 10000, 0, index, DEFAULT_SENSOR_ID); trip_pt_passive.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); trip_points.push_back(trip_pt_passive); cthd_trip_point trip_pt_critical(1, CRITICAL, crit_temp, 0, index, DEFAULT_SENSOR_ID); trip_pt_passive.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); trip_points.push_back(trip_pt_critical); thd_log_info("cthd_zone_kbl_amdgpu::read_trip_points OK\n"); return THD_SUCCESS; } int cthd_zone_kbl_amdgpu::read_cdev_trip_points() { return THD_SUCCESS; } 0707010000006F000081A400000000000000000000000166853FAC0000049B000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_kbl_amdgpu.h/* * thd_zone_kbl_amdgpu.h: thermal zone for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_KBL_AMD_GPU_H #define THD_ZONE_KBL_AMD_GPU_H #include "thd_zone.h" class cthd_zone_kbl_amdgpu: public cthd_zone { protected: cthd_sensor *sensor; csys_fs dts_sysfs; public: cthd_zone_kbl_amdgpu(int index); int read_trip_points(); int zone_bind_sensors(); int read_cdev_trip_points(); }; #endif 07070100000070000081A400000000000000000000000166853FAC00000B0A000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_kbl_g_mcp.cpp/* * thd_zone_kbl_amdgpu.cpp: thermal zone for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include <dirent.h> #include "thd_engine_default.h" #include "thd_sys_fs.h" #include "thd_zone_kbl_g_mcp.h" cthd_zone_kbl_g_mcp::cthd_zone_kbl_g_mcp(int index): cthd_zone(index, ""), sensor(NULL), dts_sysfs("/sys/class/hwmon/"){ type_str = "kbl-g-mcp"; } int cthd_zone_kbl_g_mcp::zone_bind_sensors() { cthd_sensor *sensor; sensor = thd_engine->search_sensor("kbl-g-mcp"); if (sensor) { bind_sensor(sensor); return THD_SUCCESS; } return THD_ERROR; } int cthd_zone_kbl_g_mcp::read_trip_points() { cthd_cdev *cdev_gpu, *cdev_cpu; thd_log_info("cthd_zone_kbl_g_mcp::read_trip_points \n"); cdev_gpu = thd_engine->search_cdev("amdgpu"); if (!cdev_gpu) { thd_log_info("amdgpu failed\n"); return THD_ERROR; } int gpu_max_power = cdev_gpu->get_phy_max_state(); cdev_cpu = thd_engine->search_cdev("rapl_controller"); if (!cdev_cpu) { thd_log_info("rapl_controller, failed\n"); return THD_ERROR; } int cpu_max_power = cdev_cpu->get_phy_max_state(); thd_log_debug("gpu %d cpu %d\n", gpu_max_power, cpu_max_power); int total_power = gpu_max_power + cpu_max_power; cthd_trip_point trip_pt_passive(0, PASSIVE, total_power , 0, index, DEFAULT_SENSOR_ID, PARALLEL); pid_param_t pid_param; pid_param.valid = 1; pid_param.kp = -0.4f; pid_param.ki = 0.0f; pid_param.kd = 0.0f; thd_log_info("==pid valid %f:%f:%f\n", pid_param.kp, pid_param.ki, pid_param.kd); trip_pt_passive.thd_trip_point_add_cdev(*cdev_gpu, cthd_trip_point::default_influence, thd_engine->get_poll_interval(), 0, 0, &pid_param); pid_param.kp = -0.6f; pid_param.ki = 0.0f; pid_param.kd = 0.0f; trip_pt_passive.thd_trip_point_add_cdev(*cdev_cpu, cthd_trip_point::default_influence, thd_engine->get_poll_interval(), 0, 0, &pid_param); trip_points.push_back(trip_pt_passive); thd_log_info("cthd_zone_kbl_g_mcp::read_trip_points OK\n"); return THD_SUCCESS; } int cthd_zone_kbl_g_mcp::read_cdev_trip_points() { return THD_SUCCESS; } 07070100000071000081A400000000000000000000000166853FAC00000495000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_kbl_g_mcp.h/* * thd_zone_kbl_amdgpu.h: thermal zone for KBL-G amdgpu * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_KBL_G_MCP_H #define THD_ZONE_KBL_G_MCP_H #include "thd_zone.h" class cthd_zone_kbl_g_mcp: public cthd_zone { protected: cthd_sensor *sensor; csys_fs dts_sysfs; public: cthd_zone_kbl_g_mcp(int index); int read_trip_points(); int zone_bind_sensors(); int read_cdev_trip_points(); }; #endif 07070100000072000081A400000000000000000000000166853FAC0000078B000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_rapl_power.cpp/* * thd_zone_rapl_power.cpp: thermal zone for rapl power * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_engine_default.h" #include "thd_sys_fs.h" #include "thd_zone_rapl_power.h" cthd_zone_rapl_power::cthd_zone_rapl_power(int index) : cthd_zone(index, "") { type_str = "rapl_pkg_power"; } int cthd_zone_rapl_power::zone_bind_sensors() { cthd_sensor *sensor; sensor = thd_engine->search_sensor("rapl_pkg_power"); if (sensor) { bind_sensor(sensor); return THD_SUCCESS; } return THD_ERROR; } int cthd_zone_rapl_power::read_trip_points() { cthd_cdev *cdev_cpu; cdev_cpu = thd_engine->search_cdev("rapl_controller"); if (!cdev_cpu) { thd_log_info("rapl_controller, failed\n"); return THD_ERROR; } cthd_trip_point trip_pt_passive(0, PASSIVE, 100000, 0, index, DEFAULT_SENSOR_ID, PARALLEL); trip_pt_passive.thd_trip_point_add_cdev(*cdev_cpu, cthd_trip_point::default_influence, thd_engine->get_poll_interval(), 0, 0, NULL); trip_points.push_back(trip_pt_passive); thd_log_debug("cthd_zone_rapl_power::read_trip_points OK\n"); return THD_SUCCESS; } int cthd_zone_rapl_power::read_cdev_trip_points() { return THD_SUCCESS; } 07070100000073000081A400000000000000000000000166853FAC00000445000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_rapl_power.h/* * thd_zone_rapl_power.h * * Copyright (C) 2020 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_RAPL_POWER_H #define THD_ZONE_RAPL_POWER_H #include "thd_zone.h" class cthd_zone_rapl_power: public cthd_zone { public: cthd_zone_rapl_power(int index); int read_trip_points(); int zone_bind_sensors(); int read_cdev_trip_points(); }; #endif 07070100000074000081A400000000000000000000000166853FAC0000184C000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_therm_sys_fs.cpp/* * thd_zone_therm_sys_fs.cpp: thermal zone class implementation * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #include "thd_zone_therm_sys_fs.h" #include "thd_engine.h" #include <stdlib.h> cthd_sysfs_zone::cthd_sysfs_zone(int count, std::string path) : cthd_zone(count, path), trip_point_cnt(0) { std::stringstream tc_type_dev; tc_type_dev << index << "/type"; thd_log_debug("Thermal Zone look for %s\n", tc_type_dev.str().c_str()); if (zone_sysfs.exists(tc_type_dev.str())) { zone_sysfs.read(tc_type_dev.str(), type_str); } thd_log_debug("Thermal Zone %d:%s\n", index, type_str.c_str()); } cthd_sysfs_zone::~cthd_sysfs_zone() { std::stringstream trip_sysfs; trip_sysfs << index << "/" << "trip_point_"; for (unsigned int i = 0; i < initial_trip_values.size(); ++i) { std::stringstream temp_stream; temp_stream << trip_sysfs.str() << i << "_temp"; if (initial_trip_values[i] >= 0 && zone_sysfs.exists(temp_stream.str())) { zone_sysfs.write(temp_stream.str(), initial_trip_values[i]); } } } int cthd_sysfs_zone::zone_bind_sensors() { cthd_sensor *sensor; sensor = thd_engine->search_sensor(type_str); if (sensor) { bind_sensor(sensor); } else return THD_ERROR; return THD_SUCCESS; } int cthd_sysfs_zone::read_trip_points() { // Gather all trip points std::stringstream trip_sysfs; trip_sysfs << index << "/" << "trip_point_"; for (int i = 0; i < max_trip_points; ++i) { std::stringstream type_stream; std::stringstream temp_stream; std::stringstream hist_stream; std::string _type_str; std::string _temp_str; std::string _hist_str; trip_point_type_t trip_type; int temp = 0, hyst = 1; mode_t mode = 0; cthd_sensor *sensor; bool wr_mode = false; type_stream << trip_sysfs.str() << i << "_type"; if (zone_sysfs.exists(type_stream.str())) { zone_sysfs.read(type_stream.str(), _type_str); thd_log_debug("read_trip_points %s:%s \n", type_stream.str().c_str(), _type_str.c_str()); } temp_stream << trip_sysfs.str() << i << "_temp"; if (zone_sysfs.exists(temp_stream.str())) { mode = zone_sysfs.get_mode(temp_stream.str()); zone_sysfs.read(temp_stream.str(), _temp_str); std::istringstream(_temp_str) >> temp; thd_log_debug("read_trip_points %s:%s \n", temp_stream.str().c_str(), _temp_str.c_str()); } hist_stream << trip_sysfs.str() << i << "_hyst"; if (zone_sysfs.exists(hist_stream.str())) { zone_sysfs.read(hist_stream.str(), _hist_str); std::istringstream(_hist_str) >> hyst; if (hyst < 1000 || hyst > 5000) hyst = 1000; thd_log_debug("read_trip_points %s:%s \n", hist_stream.str().c_str(), _hist_str.c_str()); } if (_type_str == "critical") trip_type = CRITICAL; else if (_type_str == "hot") trip_type = MAX; else if (_type_str == "active") trip_type = ACTIVE; else if (_type_str == "passive") trip_type = PASSIVE; else trip_type = INVALID_TRIP_TYPE; sensor = thd_engine->search_sensor(type_str); if (sensor && (mode & S_IWUSR)) { sensor->set_async_capable(true); wr_mode = true; initial_trip_values.push_back(temp); } else initial_trip_values.push_back(-1); if (sensor && temp > 0 && trip_type != INVALID_TRIP_TYPE && !wr_mode) { cthd_trip_point trip_pt(trip_point_cnt, trip_type, temp, hyst, index, sensor->get_index()); trip_pt.thd_trip_point_set_control_type(SEQUENTIAL); trip_points.push_back(trip_pt); ++trip_point_cnt; } } thd_log_debug("read_trip_points Added from sysfs %d trips \n", trip_point_cnt); if (!trip_point_cnt) { cthd_sensor *sensor; sensor = thd_engine->search_sensor(type_str); if (!sensor) return THD_ERROR; cthd_trip_point trip_pt(0, PASSIVE, INT32_MAX, 0, index, sensor->get_index()); trip_pt.thd_trip_point_set_control_type(SEQUENTIAL); trip_points.push_back(trip_pt); ++trip_point_cnt; thd_log_debug("Added one default trip\n"); } return THD_SUCCESS; } int cthd_sysfs_zone::read_cdev_trip_points() { thd_log_debug(" >> read_cdev_trip_points for \n"); // Gather all Cdevs // Gather all trip points std::stringstream cdev_sysfs; cdev_sysfs << index << "/" << "cdev"; for (int i = 0; i < max_cool_devs; ++i) { std::stringstream trip_pt_stream, cdev_stream; std::string trip_pt_str; int trip_cnt = -1; char buf[51], *ptr; trip_pt_stream << cdev_sysfs.str() << i << "_trip_point"; if (zone_sysfs.exists(trip_pt_stream.str())) { zone_sysfs.read(trip_pt_stream.str(), trip_pt_str); std::istringstream(trip_pt_str) >> trip_cnt; } else continue; thd_log_debug("cdev trip point: %s contains %d\n", trip_pt_str.c_str(), trip_cnt); cdev_stream << cdev_sysfs.str() << i; if (zone_sysfs.exists(cdev_stream.str())) { thd_log_debug("cdev%d present\n", i); int ret = zone_sysfs.read_symbolic_link_value(cdev_stream.str(), buf, sizeof(buf) - 1); if (ret == 0) { ptr = strstr(buf, "cooling_device"); if (ptr) { ptr += strlen("cooling_device"); thd_log_debug("symbolic name %s:%s\n", buf, ptr); if (trip_cnt >= 0 && trip_cnt < trip_point_cnt) { trip_points[trip_cnt].thd_trip_point_add_cdev_index( atoi(ptr), cthd_trip_point::default_influence); zone_cdev_set_binded(); } else { thd_log_debug("Invalid trip_cnt\n"); } } } } } thd_log_debug( "cthd_sysfs_zone::read_cdev_trip_points: ZONE bound to CDEV status %d \n", zone_cdev_binded_status); return THD_SUCCESS; } 07070100000075000081A400000000000000000000000166853FAC00000568000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/src/thd_zone_therm_sys_fs.h/* * thd_zone_therm_sys_fs.h: thermal zone class interface * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_ZONE_THERM_SYS_FS_H_ #define THD_ZONE_THERM_SYS_FS_H_ #include "thd_zone.h" class cthd_sysfs_zone: public cthd_zone { private: int trip_point_cnt; std::vector<int> initial_trip_values; public: static const int max_trip_points = 50; static const int max_cool_devs = 50; cthd_sysfs_zone(int count, std::string path); ~cthd_sysfs_zone(); virtual int read_trip_points(); virtual int read_cdev_trip_points(); virtual int zone_bind_sensors(); }; #endif /* THD_ZONE_THERM_SYS_FS_H_ */ 07070100000076000081A400000000000000000000000166853FAC00000B7B000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/src/thermald.h/* * thermald.h: Thermal Daemon common header file * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name <Srinivas.Pandruvada@linux.intel.com> * */ #ifndef THD_THERMALD_H #define THD_THERMALD_H #include <stdio.h> #include <getopt.h> #include <locale.h> #include <errno.h> #include <stdlib.h> #include <signal.h> #include <pthread.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <string.h> #include <signal.h> #ifdef ANDROID #define LOG_NDEBUG 1 #undef LOG_TAG #define LOG_TAG "THERMALD" #include <utils/Log.h> #include <log/log.h> #include <cutils/properties.h> #define thd_log_fatal ALOGE #define thd_log_error ALOGE #define thd_log_warn ALOGW #define thd_log_msg ALOGI #if LOG_DEBUG_INFO == 1 #define thd_log_info ALOGI #define thd_log_debug ALOGD #else #define thd_log_info(...) #define thd_log_debug(...) #endif #else #include "config.h" // Keeping the logging flag enabled for non-android cases #define LOG_DEBUG_INFO 1 #define LOCKF_SUPPORT #ifdef GLIB_SUPPORT #include <glib.h> #include <glib/gi18n.h> #include <gmodule.h> // Log macros #define thd_log_fatal g_error // Print error and terminate #define thd_log_error g_critical #define thd_log_warn g_warning #define thd_log_msg g_message #define thd_log_debug g_debug #define thd_log_info(...) g_log(NULL, G_LOG_LEVEL_INFO, __VA_ARGS__) #else static int dummy_printf(const char *__restrict __format, ...) { return 0; } #define thd_log_fatal printf #define thd_log_error printf #define thd_log_warn printf #define thd_log_msg printf #define thd_log_debug dummy_printf #define thd_log_info printf #endif #endif // Common return value defines #define THD_SUCCESS 0 #define THD_ERROR -1 #define THD_FATAL_ERROR -2 // Dbus related /* Well-known name for this service. */ #define THD_SERVICE_NAME "org.freedesktop.thermald" #define THD_SERVICE_OBJECT_PATH "/org/freedesktop/thermald" #define THD_SERVICE_INTERFACE "org.freedesktop.thermald" class cthd_engine; class cthd_engine_therm_sysfs; extern cthd_engine *thd_engine; extern int thd_poll_interval; extern bool thd_ignore_default_control; extern bool workaround_enabled; extern bool disable_active_power; extern bool ignore_critical; #endif 07070100000077000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002900000000thermal_daemon-2.5.7.17.git+fc7464a/test07070100000078000081A400000000000000000000000166853FAC000000AF000000000000000000000000000000000000003200000000thermal_daemon-2.5.7.17.git+fc7464a/test/Makefileobj-m += thermald_test_kern_module.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 07070100000079000081ED00000000000000000000000166853FAC00000AC4000000000000000000000000000000000000003400000000thermal_daemon-2.5.7.17.git+fc7464a/test/cpufreq.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : Test cpufreq cooling" cp cpufreq.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') cpuinfo_min_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq) cpuinfo_max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq) echo "cpuinfo_min_freq:" $cpuinfo_min_freq echo "cpuinfo_max_freq:" $cpuinfo_max_freq cat ${THD0_ZONE}/temp sleep 2 echo "Forcing to throttle" echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $cpuinfo_min_freq ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $cpuinfo_min_freq ]; then echo "cpufreq: Step 0: Test failed" exit 1 else echo "cpufreq: Step 0: Test passed" fi echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $cpuinfo_max_freq ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $cpuinfo_max_freq ]; then echo "cpufreq: Step 1: Test failed" exit 1 else echo "cpufreq: Step 1: Test passed" fi echo "Forcing throttle again " echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $cpuinfo_min_freq ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $cpuinfo_min_freq ]; then echo "cpufreq: Step 0: Test failed" exit 1 else echo "cpufreq: Step 0: Test passed" fi echo "Removing throttle in one step" echo 0 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $cpuinfo_max_freq ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $cpuinfo_max_freq ]; then echo "cpufreq: Step 2: Test failed" exit 1 else echo "cpufreq: Step 2: Test passed" fi 0707010000007A000081A400000000000000000000000166853FAC0000030D000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/test/cpufreq.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>cpufreq</type> <influence>100</influence> <SamplingPeriod>1</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000007B000081ED00000000000000000000000166853FAC0000128E000000000000000000000000000000000000003400000000thermal_daemon-2.5.7.17.git+fc7464a/test/default.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : default CPU cooling" cp default.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') #rapl_max_power=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_max_power_uw) #rapl_min_power=$(expr $rapl_max_power / 2) rapl_max_power=25000000 rapl_min_power=10000000 echo "rapl_min_power:" $rapl_min_power echo "rapl_max_power:" $rapl_max_power min_perf_pct=50 max_perf_pct=100 echo "min_perf_pct:" $min_perf_pct echo "max_perf_pct:" $max_perf_pct CDEV_POWERCLAMP=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:intel_powerclamp | sed 's/\/type.*//') CDEV_PROCESSOR=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:Processor | head -1 | sed 's/\/type.*//') cat ${THD0_ZONE}/temp sleep 2 for i in {0..1} do echo "Forcing to throttle for step" $i echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -le $rapl_min_power ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -gt $rapl_min_power ]; then echo "intel_rapl: Step 0: Test failed" exit 1 else echo "intel_rapl: Step 0: Test passed" fi COUNTER=0 while [ $COUNTER -lt 20 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -le $min_perf_pct ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -gt $min_perf_pct ]; then echo "intel_pstate: Step 0: Test failed" exit 1 else echo "intel_pstate: Step 0: Test passed" fi max_state=$(cat ${CDEV_POWERCLAMP}/max_state) max_state=$(expr $max_state / 2) echo $max_state COUNTER=0 while [ $COUNTER -lt 20 ]; do cur_state=$(cat ${CDEV_POWERCLAMP}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state ]; then echo "powerclamp: Step 0: Test failed" exit 1 else echo "powerclamp: Step 0: Test passed" fi max_state_processor=3 echo $max_state_processor COUNTER=0 while [ $COUNTER -lt 20 ]; do cur_state=$(cat ${CDEV_PROCESSOR}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state_processor ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state_processor ]; then echo "processor: Step 0: Test failed" exit 1 else echo "processor: Step 0: Test passed" fi if [ $i -eq 0 ]; then echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp else echo "Removing throttle in one shot" echo 0 > ${THD0_ZONE}/emul_temp fi COUNTER=0 while [ $COUNTER -lt 20 ]; do cur_state=$(cat ${CDEV_PROCESSOR}/cur_state) echo "current state " ${cur_state} if [ $cur_state -le 0 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt 0 ]; then echo "processor: Step 0: Test failed" exit 1 else echo "processor: Step 0: Test passed" fi COUNTER=0 while [ $COUNTER -lt 20 ]; do cur_state=$(cat ${CDEV_POWERCLAMP}/cur_state) echo "current state " ${cur_state} if [ $cur_state -le 0 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt -1 ]; then echo "powerclamp: Step 0: Test failed" exit 1 else echo "powerclamp: Step 0: Test passed" fi COUNTER=0 while [ $COUNTER -lt 20 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $max_perf_pct ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $max_perf_pct ]; then echo "intel_pstate: Step 2: Test failed" exit 1 else echo "intel_pstate: Step 2: Test passed" fi COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -ge $rapl_max_power ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -lt $rapl_max_power ]; then echo "intel_rapl: Step 1: Test failed" exit 1 else echo "intel_rapl: Step 1: Test passed" fi done 0707010000007C000081A400000000000000000000000166853FAC00000648000000000000000000000000000000000000003500000000thermal_daemon-2.5.7.17.git+fc7464a/test/default.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>10000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>rapl_controller</type> <influence>100</influence> </CoolingDevice> <CoolingDevice> <index>2</index> <type>intel_pstate</type> <influence>90</influence> </CoolingDevice> <CoolingDevice> <index>3</index> <type>intel_powerclamp</type> <influence>80</influence> </CoolingDevice> <CoolingDevice> <index>4</index> <type>Processor</type> <influence>70</influence> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000007D000081ED00000000000000000000000166853FAC00001E99000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/test/exec_config_tests.sh#!/bin/bash echo "Make sure that intel_pstate is not started with --adaptive option" make insmod thermald_test_kern_module.ko CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" #Test 1: Simple association: one zone to one cooling device # check if cdev state reach max when temp >= 40C and when # temp < 40C state reach to 0. echo "Executing test 1: Simple zone to cdev association" cp test1.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:thd_test_0 | sed 's/\/type.*//') THD0_CDEV=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep thd_cdev_0 | sed 's/\/type.*//') echo "Current temperature for thd_test_0 temp to" cat ${THD0_ZONE}/temp sleep 2 echo 50000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi cat ${THD0_CDEV}/cur_state echo 10000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi # TEST 2 echo "Executing test 2: Check if influence field is respected, in picking up cdev" cp test2.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:thd_test_0 | sed 's/\/type.*//') THD0_CDEV0=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep thd_cdev_0 | sed 's/\/type.*//') THD0_CDEV1=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep thd_cdev_1 | sed 's/\/type.*//') echo "Current temperature for thd_test_0 temp to" cat ${THD0_ZONE}/temp sleep 2 echo 50000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp # check the highest priority cdev picked up first COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV1}/cur_state) echo "current state for thd_cdev_1" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi # pick up the next COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV0}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi echo 10000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV0}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV1}/cur_state) echo "current state for thd_cdev_1" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi # Test 3 echo "Executing test 3: Check if sample field is respected" echo "currently it is a visual test only" echo "It will show Too early to act messages, gap between two ops is 15+ sec as per connfig here " cp test3.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:thd_test_0 | sed 's/\/type.*//') THD0_CDEV=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep thd_cdev_0 | sed 's/\/type.*//') echo "Current temperature for thd_test_0 temp to" cat ${THD0_ZONE}/temp sleep 2 echo 50000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi cat ${THD0_CDEV}/cur_state echo 10000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi # Test 4 echo "Executing test 4: one cdev in multiple zones, one zone crossed passive, make sure that the other zone doesn't deactivate an activated cdev " cp test4.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:thd_test_0 | sed 's/\/type.*//') THD0_CDEV=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep thd_cdev_0 | sed 's/\/type.*//') echo "Current temperature for thd_test_0 temp to" cat ${THD0_ZONE}/temp sleep 2 echo 50000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi cat ${THD0_CDEV}/cur_state echo 10000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD0_CDEV}/cur_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi # Test 5 echo "Executing test 5: Test case where sensor/zone/cdev are not" echo " in thermal sysfs and still able to control" cp test5.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD5_ZONE=/sys/kernel/thermald_test THD5_CDEV=/sys/kernel/thermald_test echo "Current temperature" cat ${THD5_ZONE}/sensor_temp sleep 2 echo 50000 > ${THD5_ZONE}/sensor_temp echo "Emulate temp to" cat ${THD5_ZONE}/sensor_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD5_CDEV}/control_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 10 ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 10 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi cat ${THD5_CDEV}/control_state echo 10000 > ${THD5_ZONE}/sensor_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do curr_state=$(cat ${THD5_CDEV}/control_state) echo "current state for thd_cdev_0" ${curr_state} if [ $curr_state -eq 0 ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_state -ne 0 ]; then echo "Test Failed" exit 1 else echo "Test passed" fi 0707010000007E000081ED00000000000000000000000166853FAC00000A3A000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/test/intel_pstate.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : Test intel_pstate cooling" cp intel_pstate.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') min_perf_pct=50 max_perf_pct=100 echo "min_perf_pct:" $min_perf_pct echo "max_perf_pct:" $max_perf_pct cat ${THD0_ZONE}/temp sleep 2 echo "Forcing to throttle" echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -le $min_perf_pct ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -gt $min_perf_pct ]; then echo "intel_pstate: Step 0: Test failed" exit 1 else echo "intel_pstate: Step 0: Test passed" fi echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $max_perf_pct ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $max_perf_pct ]; then echo "intel_state: Step 1: Test failed" exit 1 else echo "intel_psatte: Step 1: Test passed" fi echo "Forcing throttle again " echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -le $min_perf_pct ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -gt $min_perf_pct ]; then echo "intel_pstate: Step 0: Test failed" exit 1 else echo "intel_pstate: Step 0: Test passed" fi echo "Removing throttle in one step" echo 0 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do scaling_max_freq=$(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct) echo "current state " ${scaling_max_freq} if [ $scaling_max_freq -eq $max_perf_pct ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $scaling_max_freq -ne $max_perf_pct ]; then echo "intel_pstate: Step 2: Test failed" exit 1 else echo "intel_pstate: Step 2: Test passed" fi 0707010000007F000081A400000000000000000000000166853FAC00000312000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/test/intel_pstate.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>intel_pstate</type> <influence>100</influence> <SamplingPeriod>1</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 07070100000080000081A400000000000000000000000166853FAC00000480000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/test/intel_rapl.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>10000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>rapl_controller</type> <influence>100</influence> <SamplingPeriod>1</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 07070100000081000081ED00000000000000000000000166853FAC0000095C000000000000000000000000000000000000003700000000thermal_daemon-2.5.7.17.git+fc7464a/test/powerclamp.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : Test powerclamp cooling" cp powerclamp.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') CDEV=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:intel_powerclamp | sed 's/\/type.*//') cat ${THD0_ZONE}/temp sleep 2 echo "Forcing to throttle" echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp max_state=49 #$(cat ${CDEV}/max_state) max_state=$(expr $max_state / 2) echo $max_state COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state ]; then echo "powerclamp: Step 0: Test failed" exit 1 else echo "powerclamp: Step 0: Test passed" fi echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -le 0 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt 0 ]; then echo "powerclamp: Step 0: Test failed" exit 1 else echo "powerclamp: Step 0: Test passed" fi echo "Forcing throttle again " echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state ]; then echo "powerclamp: Step 1: Test failed" exit 1 else echo "powerclamp: Step 1: Test passed" fi echo "Removing throttle in one step" echo 0 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -eq -1 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt 0 ]; then echo "powerclamp: Step 2: Test failed" exit 1 else echo "powerclamp: Step 2: Test passed" fi 07070100000082000081A400000000000000000000000166853FAC00000316000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/test/powerclamp.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>intel_powerclamp</type> <influence>100</influence> <SamplingPeriod>1</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 07070100000083000081ED00000000000000000000000166853FAC00000922000000000000000000000000000000000000003600000000thermal_daemon-2.5.7.17.git+fc7464a/test/processor.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : Test acpi processor cooling" cp processor_acpi.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') CDEV=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:Processor | head -1 | sed 's/\/type.*//') cat ${THD0_ZONE}/temp sleep 2 echo "Forcing to throttle" echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp max_state=3 echo $max_state COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state ]; then echo "processor: Step 0: Test failed" exit 1 else echo "processor: Step 0: Test passed" fi echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -le 0 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt 0 ]; then echo "processor: Step 0: Test failed" exit 1 else echo "processor: Step 0: Test passed" fi echo "Forcing throttle again " echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -ge $max_state ]; then echo "Reached max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -lt $max_state ]; then echo "processor: Step 1: Test failed" exit 1 else echo "processor: Step 1: Test passed" fi echo "Removing throttle in one step" echo 0 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 10 ]; do cur_state=$(cat ${CDEV}/cur_state) echo "current state " ${cur_state} if [ $cur_state -le 0 ]; then echo "Reached min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $cur_state -gt 0 ]; then echo "processor: Step 2: Test failed" exit 1 else echo "processor: Step 2: Test passed" fi 07070100000084000081A400000000000000000000000166853FAC0000030F000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/processor_acpi.xml<?xml version="1.0"?> <ThermalConfiguration> <Platform> <Name>Example Laptop</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>_cpu</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>70000</Temperature> <type>passive</type> <ControlType>SEQUENTIAL</ControlType> <CoolingDevice> <index>1</index> <type>Processor</type> <influence>100</influence> <SamplingPeriod>1</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 07070100000085000081ED00000000000000000000000166853FAC00000B35000000000000000000000000000000000000003100000000thermal_daemon-2.5.7.17.git+fc7464a/test/rapl.sh#!/bin/bash CONF_FILE="/var/run/thermald/thermal-conf.xml.auto" echo "Executing test : Test intel_rapl cooling" cp intel_rapl.xml $CONF_FILE dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Reinit sleep 5 THD0_ZONE=$(grep -r . /sys/class/thermal/* 2>/tmp/err.txt | grep type:x86_pkg_temp | sed 's/\/type.*//') #rapl_max_power=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_max_power_uw) #rapl_min_power=$(expr $rapl_max_power / 2 ) rapl_max_power=25000000 rapl_min_power=10000000 echo "rapl_min_power:" $rapl_min_power echo "rapl_max_power:" $rapl_max_power cat ${THD0_ZONE}/temp sleep 2 echo "Forcing to throttle" echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -le $rapl_min_power ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -gt $rapl_min_power ]; then echo "intel_rapl: Step 0: Test failed" exit 1 else echo "intel_rapl: Step 0: Test passed" fi echo "Removing throttle slowly stepwise" echo 69000 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -ge $rapl_max_power ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -lt $rapl_max_power ]; then echo "intel_rapl: Step 1: Test failed" exit 1 else echo "intel_rapl: Step 1: Test passed" fi echo "Forcing throttle again " echo 70000 > ${THD0_ZONE}/emul_temp echo "Emulate temp to" cat ${THD0_ZONE}/temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -le $rapl_min_power ]; then echo "Reached Min State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -gt $rapl_min_power ]; then echo "intel_rapl: Step 0: Test failed" exit 1 else echo "intel_rapl: Step 0: Test passed" fi echo "Removing throttle in one step" echo 0 > ${THD0_ZONE}/emul_temp COUNTER=0 while [ $COUNTER -lt 20 ]; do curr_power_limit=$(cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw) echo "current state " ${curr_power_limit} if [ $curr_power_limit -ge $rapl_max_power ]; then echo "Reached Max State" break fi sleep 5 let COUNTER=COUNTER+1 done if [ $curr_power_limit -lt $rapl_max_power ]; then echo "intel_rapl: Step 2: Test failed" exit 1 else echo "intel_rapl: Step 2: Test passed" fi 07070100000086000081A400000000000000000000000166853FAC00000179000000000000000000000000000000000000003900000000thermal_daemon-2.5.7.17.git+fc7464a/test/readme_test.txtTests are executed using a loopback Linux driver. Build this as a kernel module by copying thermald_test_kern_module.c to drivers/thermal in Linux kernel source. Add obj-m += thermald_test_kern_module.o in drivers/thermal/Makefile In addition enable in kernel .config CONFIG_THERMAL_EMULATION=y Once kernel driver is loaded using insmod/modprobe execute exec_config_tests.sh 07070100000087000081ED00000000000000000000000166853FAC000000E3000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/test/run_all_tests.sh#!/bin/bash echo "Make sure that thermald is not started with --adaptive option" source rapl.sh source intel_pstate.sh source powerclamp.sh source processor.sh #source cpufreq.sh source default.sh source exec_config_tests.sh 07070100000088000081A400000000000000000000000166853FAC0000041B000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test1.xml<?xml version="1.0"?> <!-- Auto Test 1: Simple association --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>*</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 07070100000089000081A400000000000000000000000166853FAC00000553000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test2.xml<?xml version="1.0"?> <!-- Auto Test 2: Two cooling device to one zone Make sure that influence is used to pick the first cdev --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>*</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> <influence> 80 </influence> </CoolingDevice> <CoolingDevice> <Type>thd_cdev_1</Type> <influence> 100 </influence> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000008A000081A400000000000000000000000166853FAC0000045B000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test3.xml<?xml version="1.0"?> <!-- Auto Test 1: Simple association with sample period --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>*</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> <SamplingPeriod> 10 </SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000008B000081A400000000000000000000000166853FAC00000733000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test4.xml<?xml version="1.0"?> <!-- Auto Test 1: one cdev in multiple zones, one zone crossed passive, make sure that the other zone doesn't deactivate an activated cdev --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>*</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> <ThermalZone> <Type>thd_test_1</Type> <TripPoints> <TripPoint> <SensorType>thd_test_1</SensorType> <Temperature>*</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000008C000081A400000000000000000000000166853FAC000006A3000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test5.xml<?xml version="1.0"?> <!-- Create sensor and control device from non thermal sysfs This shows that thermald can get temp and control via any sysfs entry --> <ThermalConfiguration> <Platform> <Name>sysfs_zone_test</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalSensors> <ThermalSensor> <Type>sysfs_sensor_0</Type> <Path>/sys/kernel/thermald_test/sensor_temp</Path> <AsyncCapable>0</AsyncCapable> </ThermalSensor> </ThermalSensors> <ThermalZones> <ThermalZone> <Type>sysfs_thermal_zone_0</Type> <TripPoints> <TripPoint> <SensorType>sysfs_sensor_0</SensorType> <Temperature>40000</Temperature> <type>passive</type> <CoolingDevice> <Type>sysfs_cdev_0</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Type>sysfs_cdev_0</Type> <Path>/sys/kernel/thermald_test/control_state</Path> <MinState>0</MinState> <IncDecStep>1</IncDecStep> <MaxState>10</MaxState> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> 0707010000008D000081A400000000000000000000000166853FAC00000995000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test6.xml<?xml version="1.0"?> <!-- This test configuration tests how target state can be set. Here we also test if multiple target state for multiple trips to the same cooling device. Here two trips have target state the last trip doesn't have any target state and will go up to max. Make sure that system behaves any temperature change" - 40 to 50 to 60 - Directly to 60 - Wild wings between 10 to 60C --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_ex_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>40000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> <TargetState> 3 </TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>50000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> <TargetState> 6 </TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>60000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> 0707010000008E000081A400000000000000000000000166853FAC00000BB2000000000000000000000000000000000000003300000000thermal_daemon-2.5.7.17.git+fc7464a/test/test7.xml<?xml version="1.0"?> <!-- This example explains how a single real cooling device can be divided into logical cooling devices with different minimum and maximum states. In this case three trips are associated with three logical cooling devices with same path but different minimum and maximum. --> <ThermalConfiguration> <Platform> <Name>THD_TEST_0</Name> <ProductName>*</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>thd_test_ex_0</Type> <TripPoints> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>40000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0_cap_3</Type> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>50000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0_cap_6</Type> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>thd_test_0</SensorType> <Temperature>60000</Temperature> <type>passive</type> <CoolingDevice> <Type>thd_cdev_0_cap_9</Type> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> <CoolingDevices> <CoolingDevice> <Path>/sys/class/thermal/cooling_device7/cur_state</Path> <Type>thd_cdev_0_cap_3</Type> <MinState>0</MinState> <IncDecStep>1</IncDecStep> <MaxState>3</MaxState> </CoolingDevice> <CoolingDevice> <Path>/sys/class/thermal/cooling_device7/cur_state</Path> <Type>thd_cdev_0_cap_6</Type> <MinState>4</MinState> <IncDecStep>1</IncDecStep> <MaxState>6</MaxState> </CoolingDevice> <CoolingDevice> <Path>/sys/class/thermal/cooling_device7/cur_state</Path> <Type>thd_cdev_0_cap_9</Type> <MinState>9</MinState> <IncDecStep>1</IncDecStep> <MaxState>10</MaxState> </CoolingDevice> </CoolingDevices> </Platform> </ThermalConfiguration> 0707010000008F000081A400000000000000000000000166853FAC00006666000000000000000000000000000000000000003800000000thermal_daemon-2.5.7.17.git+fc7464a/test/test_cases.txtSetup - Intel 8th gen system - Use "pch_skylake" to emulate temperature. It can be any sensor which can emulate a temperature. - One window: while true; do cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw;sleep 1; done - Other window: sudo turbostat --show PkgWatt 1. Single step File Name: thermal_conf.xml.1 thermald command line $ sudo ./thermald --no-daemon --loglevel=info --ignore-default-control --config-file test/thermal_conf.xml.1 <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 1.1 No load: Current power is less than max like in this case 6W 1.1.1: Temp == 52C and then Temp == 51C Current power PkgWatt 2.76 2.76 #echo 52000 > emul_temp 25000000 25000000 25000000 25000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 #echo 51000 > emul_temp 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6250000 6500000 6750000 7000000 7250000 7500000 7750000 8000000 8250000 .. .. 25000000 1.1.2 Temp == 52C and then drop below polling temp; #echo 52000 > emul_temp #echo 40000 > emul_temp 000000 25000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 6000000 25000000 25000000 Instead of stepwise the, power is set to 25W in one shot. 1.2 Full load 1.2.1 Go up to min state PkgWatt 14.96 14.96 PkgWatt 14.96 14.96 #echo 52000 > emul_temp 25000000 25000000 14375000 14375000 14375000 14375000 14375000 13500000 13500000 13500000 13500000 13500000 12500000 12500000 12500000 12500000 12500000 10500000 10500000 10500000 10500000 10500000 6500000 6500000 6500000 6500000 6500000 6000000 6000000 6000000 6000000 # echo 51000 > emul_temp 6000000 6250000 6500000 6750000 7000000 7250000 7500000 7750000 8000000 8250000 8500000 8750000 9000000 9250000 9500000 9750000 10000000 10250000 10500000 10750000 11000000 11250000 11500000 11750000 12000000 12250000 12500000 12750000 13000000 13250000 13500000 13750000 14000000 14250000 14500000 14750000 15000000 15250000 15500000 15750000 16000000 16250000 16500000 16750000 17000000 17250000 17500000 18000000 18250000 18500000 18750000 19000000 19250000 19500000 19750000 20000000 20250000 20500000 20750000 21000000 21250000 21500000 21750000 22000000 22250000 22500000 22750000 23000000 23250000 23500000 23750000 24000000 24250000 24500000 24750000 25000000 25000000 1.2.2 Not up to min The trip recovers before. 25000000 24500000 24500000 24500000 24500000 24500000 13500000 13500000 13500000 13750000 14000000 14250000 14500000 14750000 15000000 15250000 15500000 15750000 16000000 16250000 16500000 16750000 17000000 17250000 17500000 17750000 18000000 18250000 18500000 18750000 19000000 19250000 19500000 19750000 20000000 20250000 20500000 20750000 21000000 21250000 21500000 21750000 22000000 22250000 22500000 22750000 23000000 23250000 23500000 23750000 24000000 24250000 24500000 24750000 25000000 25000000 25000000 2. Single target state for max command line: $ sudo ./thermald --no-daemon --loglevel=info --ignore-default-control --config-file test/thermal_conf.xml.2 <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>51000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> ~ Same as the above tests, but at 38C, the full power must be set to 25W. #echo 52000 > emul_temp 25000000 25000000 25000000 25000000 24375000 24375000 24375000 23875000 23875000 23875000 22875000 22875000 22875000 20875000 20875000 20875000 16875000 Wait for a few seconds # echo 51000 > emul_temp 25000000 25000000 25000000 25000000 25000000 25000000 25000000 25000000 # echo 50000 > emul_temp 25000000 25000000 Since already in min state, it will not change power but should not print anything in logs with info log level as there is nothing in the queue. 3. Multi step control command line: $ sudo ./thermald --no-daemon --loglevel=info --ignore-default-control --config-file test/thermal_conf.xml.3 - Run some busy workload to reach TDP - Configuration <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>15000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> Test cases Take one Step to 43C # echo 43000 > emul_temp Set :Power_limit: 13000000 Bring back to max target state #echo 38000 > emul_temp Set :Power_limit: 15000000 Take one Step to 43C # echo 43000 > emul_temp Set :Power_limit: 13000000 Set temp < max target state temperature #echo 35000 > emul_temp Set :Power_limit: 15000000 Take step to 43C # echo 43000 > emul_temp Set :Power_limit: 13000000 Take step to 47C # echo 47000 > emul_temp Set :Power_limit: 12000000 Bring back to max target state #echo 38000 > emul_temp Set :Power_limit: 15000000 Directly take two step to 47C # echo 47000 > emul_temp Set :Power_limit: 12000000 Take step to 52C where the exponenential step control is used #echo 52000 > emul_temp 5000000 14500000 14500000 14500000 14500000 14500000 13375000 13375000 13375000 13375000 13375000 12375000 12375000 12375000 12375000 12375000 10375000 10375000 10375000 10375000 10375000 10500000 10750000 11000000 11250000 11500000 11750000 Bring back the temp 1C lower. It will jump back to last clameped temp by 47C. Which is 12W #echo 52000 > emul_temp Set :Power_limit: 12000000 4. Control via powerclamp <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_powerclamp</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> - Run busy workload - In one window run turbostat sudo turbostat --show Core,CPU,Busy%,Bzy_MHz -i 1 echo 52000 > emul_temp Core CPU Busy% Bzy_MHz - - 98.78 3161 0 0 98.78 3161 0 4 98.78 3161 1 1 98.78 3161 1 5 98.78 3161 2 2 98.78 3161 2 6 98.78 3161 3 3 98.78 3161 3 7 98.78 3161 Core CPU Busy% Bzy_MHz - - 99.72 2196 0 0 99.72 2196 0 4 99.72 2196 1 1 99.72 2196 1 5 99.72 2196 2 2 99.72 2196 2 6 99.72 2196 3 3 99.72 2196 3 7 99.72 2196 Core CPU Busy% Bzy_MHz - - 97.40 2113 0 0 97.25 2112 0 4 97.83 2113 1 1 97.26 2112 1 5 97.86 2113 2 2 96.66 2113 2 6 97.86 2113 3 3 97.26 2113 3 7 97.26 2113 Core CPU Busy% Bzy_MHz - - 94.77 2156 0 0 94.71 2156 0 4 94.65 2156 1 1 94.75 2156 1 5 94.78 2156 2 2 94.75 2156 2 6 94.79 2156 3 3 94.82 2156 3 7 94.87 2156 Core CPU Busy% Bzy_MHz - - 94.21 2128 0 0 94.22 2128 0 4 94.21 2128 1 1 94.21 2128 1 5 94.20 2128 2 2 94.24 2128 2 6 94.19 2128 3 3 94.21 2128 3 7 94.22 2128 Core CPU Busy% Bzy_MHz - - 94.75 2192 0 0 94.74 2192 0 4 94.71 2192 1 1 94.73 2192 1 5 94.78 2192 2 2 94.77 2192 2 6 94.80 2192 3 3 94.74 2192 3 7 94.75 2192 Core CPU Busy% Bzy_MHz - - 94.75 2189 0 0 94.75 2189 0 4 94.70 2189 1 1 94.76 2189 1 5 94.74 2189 2 2 94.79 2189 2 6 94.77 2189 3 3 94.75 2188 3 7 94.74 2188 Core CPU Busy% Bzy_MHz - - 87.87 2209 0 0 87.93 2209 0 4 87.92 2209 1 1 87.90 2209 1 5 87.63 2209 2 2 87.83 2209 2 6 87.99 2208 3 3 87.84 2208 3 7 87.91 2208 Core CPU Busy% Bzy_MHz - - 84.41 2298 0 0 84.43 2297 0 4 84.42 2297 1 1 84.47 2298 1 5 84.42 2298 2 2 84.40 2298 2 6 84.30 2299 3 3 84.37 2299 3 7 84.51 2299 Core CPU Busy% Bzy_MHz - - 84.36 2300 0 0 84.51 2300 0 4 84.28 2300 1 1 84.27 2300 1 5 84.27 2300 2 2 84.32 2300 2 6 84.31 2300 3 3 84.46 2300 3 7 84.46 2300 Core CPU Busy% Bzy_MHz - - 84.11 2307 0 0 84.13 2307 0 4 84.14 2307 1 1 84.14 2307 1 5 84.02 2306 2 2 84.08 2307 2 6 84.19 2306 3 3 84.10 2307 3 7 84.09 2306 Core CPU Busy% Bzy_MHz - - 83.97 2334 0 0 84.01 2334 0 4 83.97 2334 1 1 83.91 2334 1 5 83.97 2334 2 2 84.06 2334 2 6 83.94 2334 3 3 83.90 2334 3 7 84.00 2334 Core CPU Busy% Bzy_MHz - - 77.10 2320 0 0 77.10 2320 0 4 77.06 2320 1 1 77.17 2320 1 5 77.06 2320 2 2 77.13 2320 2 6 77.01 2320 3 3 77.20 2320 3 7 77.08 2320 Core CPU Busy% Bzy_MHz - - 73.13 2300 0 0 73.21 2300 0 4 73.10 2300 1 1 73.28 2300 1 5 73.04 2300 2 2 73.04 2300 2 6 73.12 2300 3 3 73.05 2300 3 7 73.15 2300 Core CPU Busy% Bzy_MHz - - 73.47 2299 0 0 73.55 2299 0 4 73.47 2299 1 1 73.55 2299 1 5 73.19 2299 2 2 73.60 2299 2 6 73.58 2299 3 3 73.37 2299 3 7 73.46 2299 Core CPU Busy% Bzy_MHz - - 73.70 2183 0 0 73.65 2183 0 4 73.65 2183 1 1 73.81 2183 1 5 73.66 2183 2 2 73.82 2183 2 6 73.85 2183 3 3 73.48 2183 3 7 73.67 2183 Core CPU Busy% Bzy_MHz - - 73.12 2100 0 0 73.17 2100 0 4 73.16 2100 1 1 73.16 2100 1 5 73.00 2100 2 2 73.24 2100 2 6 73.06 2100 3 3 73.07 2100 3 7 73.08 2100 Core CPU Busy% Bzy_MHz - - 59.52 1685 0 0 59.81 1686 0 4 59.55 1686 1 1 59.48 1684 1 5 59.57 1686 2 2 59.27 1686 2 6 59.37 1686 3 3 59.51 1684 3 7 59.55 1684 Core CPU Busy% Bzy_MHz - - 52.00 800 0 0 52.10 800 0 4 51.92 800 1 1 51.99 800 1 5 52.02 800 2 2 52.09 800 2 6 51.85 800 3 3 52.13 800 3 7 51.91 800 Core CPU Busy% Bzy_MHz - - 53.40 800 0 0 53.58 800 0 4 52.98 800 1 1 53.92 800 1 5 53.05 800 2 2 53.51 800 2 6 53.31 800 3 3 53.48 800 3 7 53.41 800 Core CPU Busy% Bzy_MHz - - 52.69 800 0 0 52.84 800 0 4 52.71 800 1 1 52.51 800 1 5 52.56 800 2 2 52.66 800 2 6 52.67 800 3 3 52.82 800 3 7 52.72 800 Core CPU Busy% Bzy_MHz - - 52.39 800 0 0 52.26 800 0 4 52.19 800 1 1 52.88 800 1 5 52.57 800 2 2 52.10 800 2 6 52.32 800 3 3 51.92 800 3 7 52.92 800 Core CPU Busy% Bzy_MHz - - 49.97 800 0 0 50.79 800 0 4 50.30 800 1 1 50.19 800 1 5 50.00 800 2 2 50.13 800 2 6 50.07 800 3 3 50.36 800 3 7 47.90 800 #echo 51000 > emul_temp CCore CPU Busy% Bzy_MHz - - 99.38 1908 0 0 98.64 1909 0 4 99.49 1908 1 1 99.49 1908 1 5 99.49 1908 2 2 99.49 1908 2 6 99.49 1908 3 3 99.49 1908 3 7 99.49 1908 5. Control via intel_pstate <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_pstate</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> echo 52000 > emul_temp #while true; do cat /sys/devices/system/cpu/intel_pstate/max_perf_pct; cat /sys/devices/system/cpu/intel_pstate/no_turbo; sleep 1; done 100 0 90 0 90 0 90 0 90 0 90 0 70 1 70 1 70 1 70 1 70 1 50 1 50 1 50 1 50 1 50 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 10 1 #echo 51000 > emul_temp 10 1 20 1 30 1 40 1 50 1 60 1 70 1 80 0 90 0 100 0 100 6. Default with no XML config ..sensors.. [INFO]sensor index:5 x86_pkg_temp /sys/class/thermal/thermal_zone5/ Async:1 [INFO]..trips.. [INFO]index 0: type:passive temp:95000 hyst:0 zone id:7 sensor id:65535 control_type:1 cdev size:4 [INFO]cdev[0] rapl_controller, Sampling period: 0 [INFO] target_state:not defined [INFO]cdev[1] intel_pstate, Sampling period: 0 [INFO] target_state:not defined [INFO]cdev[2] intel_powerclamp, Sampling period: 0 [INFO] target_state:not defined [INFO]cdev[3] Processor, Sampling period: 0 [INFO] target_state:not defined [INFO]index 1: type:polling temp:85500 hyst:0 zone id:7 sensor id:5 control_type:0 cdev size:0 Open one window and monitor for each command. Run a busy workload. # while true; do cat /sys/class/powercap/intel-rapl/intel-rapl\:0/constraint_0_power_limit_uw;sleep 1; done On this platform max power is 15W # while true; do cat /sys/devices/system/cpu/intel_pstate/max_perf_pct; cat /sys/devices/system/cpu/intel_pstate/no_turbo; sleep 1; done In powerclamp and processor cooling device folder # while true; do cat cur_state ; sleep 1; done; Monitor by emulating 95C to x86_pkg_temp #echo 95000 > emul_temp First rapl power limit transition from 15W to 10W, then pstate percent drop from 100 to 10 and then power clamp curr_state from -1 to ~40 followed by the processor state. When temperature trip is recovered via #echo 94000 > emul_temp The device gets activated in the reverse fashion stepwise. Also try by directly emulating less than 20% drop. For example: echo 65000 > emul_temp This dectivates all the devices in one shot without going stepwise. 7. Multiple zones with the same target <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>15000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> <ThermalZone> <Type>auto_zone_2</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> Emulate temperature in both x86_pkg_temp and pch_skylake. -Set 43C to pch_skylake Resulting power 13000000 -Set 47C to x86_pkg_temp Resulting power 12000000 -Set 38C to pch_skylake Resulting power 12000000 - Set 38C to x86_pkg_temp Resulting power 25000 8. intel_powerclamp control <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_powerclamp</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> Run some busy workload emulate temp to 52C monitir the powerclamp cooling device state in the sysfs. it should start from -1 4 3 3 3 4 11 10 11 10 12 .. then emulate to 51C the curr_state slowly brought back to -1 Repeat by emulating to 30C after 52C instead of 51C the curr_state in set to -1 in one shot. 10. intel_pstate <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_pstate</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> ~ Like previous test repeat with emulating 52C, 51C and 30C. Activation: Dump of max_perf_pct and no_turbo 100 0 -- 100 0 -- 90 0 -- 90 0 -- 90 0 -- 90 0 -- 90 0 -- 70 1 -- 70 1 -- 70 1 -- 70 1 -- 70 1 -- 50 1 -- 50 1 -- 50 1 -- 50 1 -- 50 1 -- 10 1 -- Deactivation 10 1 -- 20 1 -- 30 1 -- 40 1 -- 50 1 -- 60 1 -- 70 1 -- 80 0 -- 90 0 -- 100 0 11. Processor cooling device <?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>Processor</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> Use the command while true; do for i in {0..7}; do cat cooling_device$i/cur_state; sleep 1; done; echo "--";done Same as previous step once temperature is emulated to 52C, monitor output of the above command. It starts with 0. Also measure the power in one window. Run a busy workload. The power should drop as the state increments. When deactivated the state prints should return to 0. 07070100000090000081ED00000000000000000000000166853FAC00000781000000000000000000000000000000000000004600000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal-debug-dump-fedora.sh#!/bin/bash echo "Installing Packages" dnf install -y automake autoconf-archive gcc gcc-c++ \ glib-devel dbus-glib-devel libxml2-devel \ gtk-doc upower-devel libevdev-devel kernel-tools \ stress-ng acpidump status=$? if [ $status -eq 0 ]; then echo "Packages installed successfully" else echo "Package install failed" exit 1 fi Day=`date +%d` Hour=`date +%H` Minute=`date +%M` Second=`date +%S` folder_name=$Day$Hour$Minute$Second echo $folder_name mkdir $folder_name grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > $folder_name/powercap_msr.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > $folder_name/powercap_mmio.txt grep . /sys/devices/system/cpu/cpu0/cpufreq/* > $folder_name/cpufreq.txt acpidump -o $folder_name/acpi_dump.out mkdir debug cd debug echo "Downloading latest thermald" git clone https://github.com/intel/thermal_daemon.git status=$? if [ $status -eq 0 ]; then echo "git clone successful" else echo "git clone failed" exit 1 fi cd thermal_daemon echo "Building thermald" ./autogen.sh prefix=/ make -j8 status=$? if [ $status -eq 0 ]; then echo "build successful" else echo "build failed" exit 1 fi systemctl stop thermald cd ../../$folder_name echo "Starting thermald" ../debug/thermal_daemon/thermald --no-daemon --loglevel=debug --adaptive --ignore-cpuid-check > thermald_log.txt& sleep 30 grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > powercap_msr_after.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > powercap_mmio_after.txt echo "Executing stress-ng" stress-ng --cpu -1 --io 4 --vm 2 --vm-bytes 128M --fork 4 --timeout 300s& turbostat --show Core,CPU,Busy%,Bzy_MHz,TSC_MHz -o turbostat.out& sleep 305 pkill thermald pkill turbostat systemctl start thermald cd .. echo -n "Creating archive:" echo $folder_name.tar.gz tar cvfz $folder_name.tar.gz $folder_name echo -n "Attach archive to debug:" echo $folder_name.tar.gz 07070100000091000081ED00000000000000000000000166853FAC00000793000000000000000000000000000000000000004600000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal-debug-dump-ubuntu.sh#!/bin/bash echo "Installing Packages" apt install -y git autoconf-archive g++ libglib2.0-dev libdbus-1-dev \ libdbus-glib-1-dev libxml2-dev gtk-doc-tools libupower-glib-dev \ libevdev-dev stress-ng linux-tools-common acpidump status=$? if [ $status -eq 0 ]; then echo "Packages installed successfully" else echo "Package install failed" exit 1 fi Day=`date +%d` Hour=`date +%H` Minute=`date +%M` Second=`date +%S` folder_name=$Day$Hour$Minute$Second echo $folder_name mkdir $folder_name grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > $folder_name/powercap_msr.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > $folder_name/powercap_mmio.txt grep . /sys/devices/system/cpu/cpu0/cpufreq/* > $folder_name/cpufreq.txt acpidump -o $folder_name/acpi_dump.out mkdir debug cd debug echo "Downloading latest thermald" git clone https://github.com/intel/thermal_daemon.git status=$? if [ $status -eq 0 ]; then echo "git clone successful" else echo "git clone failed" exit 1 fi cd thermal_daemon echo "Building thermald" ./autogen.sh prefix=/ make -j8 status=$? if [ $status -eq 0 ]; then echo "build successful" else echo "build failed" exit 1 fi systemctl stop thermald cd ../../$folder_name echo "Starting thermald" ../debug/thermal_daemon/thermald --no-daemon --loglevel=debug --adaptive --ignore-cpuid-check > thermald_log.txt& sleep 30 grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > powercap_msr_after.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > powercap_mmio_after.txt echo "Executing stress-ng" stress-ng --cpu -1 --io 4 --vm 2 --vm-bytes 128M --fork 4 --timeout 300s& turbostat --show Core,CPU,Busy%,Bzy_MHz,TSC_MHz -o turbostat.out& sleep 305 pkill thermald pkill turbostat systemctl start thermald cd .. echo -n "Creating archive:" echo $folder_name.tar.gz tar cvfz $folder_name.tar.gz $folder_name echo -n "Attach archive to debug:" echo $folder_name.tar.gz 07070100000092000081ED00000000000000000000000166853FAC00000568000000000000000000000000000000000000004C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal-dptf-tables-test-fedora.sh#!/bin/bash echo "This test will exit by printing error if there are no DPTF tables found" echo "Installing Packages" dnf install -y automake autoconf-archive gcc gcc-c++ \ glib-devel dbus-glib-devel libxml2-devel \ gtk-doc upower-devel libevdev-devel status=$? if [ $status -eq 0 ]; then echo "Packages installed successfully" else echo "Package install failed" exit 1 fi Day=`date +%d` Hour=`date +%H` Minute=`date +%M` Second=`date +%S` folder_name=$Day$Hour$Minute$Second echo $folder_name mkdir $folder_name grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > $folder_name/powercap_msr.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > $folder_name/powercap_mmio.txt grep . /sys/devices/system/cpu/cpu0/cpufreq/* > $folder_name/cpufreq.txt acpidump -o $folder_name/acpi_dump.out mkdir debug cd debug echo "Downloading latest thermald" git clone https://github.com/intel/thermal_daemon.git status=$? if [ $status -eq 0 ]; then echo "git clone successful" else echo "git clone failed" exit 1 fi cd thermal_daemon echo "Building thermald" ./autogen.sh prefix=/ make -j8 status=$? if [ $status -eq 0 ]; then echo "build successful" else echo "build failed" exit 1 fi systemctl stop thermald cd ../../$folder_name echo "Starting thermald" ../debug/thermal_daemon/thermald --no-daemon --loglevel=debug --adaptive --ignore-cpuid-check --test-mode 07070100000093000081ED00000000000000000000000166853FAC00000575000000000000000000000000000000000000004C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal-dptf-tables-test-ubuntu.sh#!/bin/bash echo "This test will exit by printing error if there are no DPTF tables found" echo "Installing Packages" apt install -y git autoconf-archive g++ libglib2.0-dev libdbus-1-dev \ libdbus-glib-1-dev libxml2-dev gtk-doc-tools libupower-glib-dev \ libevdev-dev status=$? if [ $status -eq 0 ]; then echo "Packages installed successfully" else echo "Package install failed" exit 1 fi Day=`date +%d` Hour=`date +%H` Minute=`date +%M` Second=`date +%S` folder_name=$Day$Hour$Minute$Second echo $folder_name mkdir $folder_name grep . /sys/class/powercap/intel-rapl/intel-rapl\:0/* > $folder_name/powercap_msr.txt grep . /sys/class/powercap/intel-rapl-mmio/intel-rapl-mmio\:0/* > $folder_name/powercap_mmio.txt grep . /sys/devices/system/cpu/cpu0/cpufreq/* > $folder_name/cpufreq.txt acpidump -o $folder_name/acpi_dump.out mkdir debug cd debug echo "Downloading latest thermald" git clone https://github.com/intel/thermal_daemon.git status=$? if [ $status -eq 0 ]; then echo "git clone successful" else echo "git clone failed" exit 1 fi cd thermal_daemon echo "Building thermald" ./autogen.sh prefix=/ make -j8 status=$? if [ $status -eq 0 ]; then echo "build successful" else echo "build failed" exit 1 fi systemctl stop thermald cd ../../$folder_name echo "Starting thermald" ../debug/thermal_daemon/thermald --no-daemon --loglevel=debug --adaptive --ignore-cpuid-check --test-mode 07070100000094000081A400000000000000000000000166853FAC0000059A000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.1<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 07070100000095000081A400000000000000000000000166853FAC00000822000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.2<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>25000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>51000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 07070100000096000081A400000000000000000000000166853FAC00000743000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.3<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>15000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 07070100000097000081A400000000000000000000000166853FAC00000C01000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.4<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <PPCC> <PowerLimitIndex>0</PowerLimitIndex> <PowerLimitMinimum>6000</PowerLimitMinimum> <PowerLimitMaximum>15000</PowerLimitMaximum> <TimeWindowMinimum>28000</TimeWindowMinimum> <TimeWindowMaximum>28000</TimeWindowMaximum> <StepSize>250</StepSize> </PPCC> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> <ThermalZone> <Type>auto_zone_2</Type> <TripPoints> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>38000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>0</SamplingPeriod> <TargetState>2147483647</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>43000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>13000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>47000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> <TargetState>12000000</TargetState> </CoolingDevice> </TripPoint> <TripPoint> <SensorType>x86_pkg_temp</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>B0D4</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 07070100000098000081A400000000000000000000000166853FAC00000436000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.5<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_powerclamp</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 07070100000099000081A400000000000000000000000166853FAC00000432000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.6<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>intel_pstate</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 0707010000009A000081A400000000000000000000000166853FAC0000042F000000000000000000000000000000000000003C00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_conf.xml.7<?xml version="1.0"?> <!-- BEGIN --> <ThermalConfiguration> <Platform> <Name> Auto generated </Name> <ProductName>HP Spectre x360 Convertible 15-ch0xx</ProductName> <Preference>QUIET</Preference> <ThermalZones> <ThermalZone> <Type>auto_zone_1</Type> <TripPoints> <TripPoint> <SensorType>pch_skylake</SensorType> <Temperature>52000</Temperature> <Type>Passive</Type> <CoolingDevice> <Type>Processor</Type> <SamplingPeriod>5</SamplingPeriod> </CoolingDevice> </TripPoint> </TripPoints> </ThermalZone> </ThermalZones> </Platform> </ThermalConfiguration> <!-- END --> 0707010000009B000081A400000000000000000000000166853FAC00000D98000000000000000000000000000000000000004000000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_config_dump.py# -*- coding: utf-8 -*- # script to dump thermal config from the running thermald via dbus # The caller must be in "power" group import dbus import os def dump_thermal_config(): if os.path.exists(file_name): os.remove(file_name) f_handle = open(file_name, 'a') system_bus = dbus.SystemBus() thd = system_bus.get_object('org.freedesktop.thermald', '/org/freedesktop/thermald') thd_intf = dbus.Interface(thd, dbus_interface='org.freedesktop.thermald') zone_count = thd_intf.GetZoneCount() string_buffer = 'zones count:' + str(zone_count) + '\n' f_handle.write(string_buffer) sensor_count = thd_intf.GetSensorCount() string_buffer = 'sensors count:' + str(sensor_count) + '\n' f_handle.write(string_buffer) f_handle.write('\n') for i in range(zone_count): zone = thd_intf.GetZoneInformation(i) string_buffer = 'zone id' + ': ' + str(i) + ''' ''' f_handle.write(string_buffer) string_buffer = '\tzone name' + ': ' + zone[0] + '\n' f_handle.write(string_buffer) string_buffer = '\tSensor Count' + ': ' + str(zone[1]) + '\n' f_handle.write(string_buffer) for j in range(zone[1]): sensor = thd_intf.GetZoneSensorAtIndex(i, j) string_buffer = '\t\t sensor: ' + sensor + '\n' f_handle.write(string_buffer) string_buffer = '\tTrip Count' + ': ' + str(zone[2]) + '\n' f_handle.write(string_buffer) for j in range(zone[2]): string_buffer = '\tTrip id' + ': ' + str(j) + '\n' f_handle.write(string_buffer) trip = thd_intf.GetZoneTripAtIndex(i, j) string_buffer = '\t\t\t trip temp' + ': ' + str(trip[0]) \ + '\n' f_handle.write(string_buffer) string_buffer = '\t\t\t trip type' + ': ' + str(trip[1]) \ + '\n' f_handle.write(string_buffer) string_buffer = '\t\t\t sensor id' + ': ' + str(trip[2]) \ + '\n' f_handle.write(string_buffer) string_buffer = ''' ''' f_handle.write(string_buffer) cdev_count = thd_intf.GetCdevCount() string_buffer = 'cdev count:' + str(cdev_count) + '\n' f_handle.write(string_buffer) f_handle.write('\n') for i in range(cdev_count): string_buffer = 'cdev id' + ': ' + str(i) + '\n' f_handle.write(string_buffer) cdev = thd_intf.GetCdevInformation(i) string_buffer = '\t\t type' + ': ' + str(cdev[0]) + '\n' f_handle.write(string_buffer) string_buffer = '\t\t min_state' + ': ' + str(cdev[1]) + '\n' f_handle.write(string_buffer) string_buffer = '\t\t max_state' + ': ' + str(cdev[2]) + '\n' f_handle.write(string_buffer) string_buffer = '\t\t current_state' + ': ' + str(cdev[3]) \ + '\n' f_handle.write(string_buffer) f_handle.close() def remove_space(string): return string.replace(' ', '') if __name__ == '__main__': product_name = '/sys/class/dmi/id/product_name' try: file_name = open(product_name, 'r').read() except: print ('Error opening ', file_name) sys.exit(2) file_name = '/tmp/' + file_name.strip() file_name = remove_space(file_name) + '.config' print ('File Name to dump config:', file_name) dump_thermal_config() 0707010000009C000081A400000000000000000000000166853FAC000008E5000000000000000000000000000000000000003E00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermal_temp_dump.py#!/usr/bin/python # -*- coding: utf-8 -*- # script to dump temperature samples every 10 seconds # The caller must be in "power" group to call the script import dbus import os import time def dump_temperature(): if os.path.exists(file_name): os.remove(file_name) f_handle = open(file_name, 'a') system_bus = dbus.SystemBus() thd = system_bus.get_object('org.freedesktop.thermald', '/org/freedesktop/thermald') thd_intf = dbus.Interface(thd, dbus_interface='org.freedesktop.thermald') sensor_count = thd_intf.GetSensorCount() string_buffer = 'sensor count:' + str(sensor_count) + '\n' f_handle.write(string_buffer) f_handle.write('\n') string_buffer = '' for i in range(sensor_count): sensor_info = thd_intf.GetSensorInformation(i) string_buffer = string_buffer + sensor_info[0] + ',' f_handle.write(string_buffer) f_handle.write('\n') string_buffer = '' f_lock = open('/tmp/thermal_temp_dump.lock', 'a') f_lock.close() while True: if os.path.exists('/tmp/thermal_temp_dump.lock') == False: break string_buffer = '' for i in range(sensor_count): sensor = thd_intf.GetSensorTemperature(i) string_buffer = string_buffer + str(sensor / 1000) + ',' f_handle.write(string_buffer) f_handle.write('\n') time.sleep(10) f_handle.close() def remove_spaces(string): return string.replace(' ', '') if __name__ == '__main__': product_name = '/sys/class/dmi/id/product_name' try: file_name = open(product_name, 'r').read() except: print ('Error opening ', file_name) sys.exit(2) file_name = '/tmp/' + file_name.strip() file_name = remove_spaces(file_name) + '.csv' print ('File Name to dump config:', file_name) print ('Before starting this script, do the following steps') print ('sudo touch /var/run/thermald/debug_mode') print ('sudo systemctl restart thermald') print ('This will also dump power data from rapl as samples') print ('This script will loop forever till user deletes file') print ('/tmp/thermal_temp_dump.lock') dump_temperature() 0707010000009D000081A400000000000000000000000166853FAC00001138000000000000000000000000000000000000004F00000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermald_optimization_with_dptfxtractProcedure to optimize thermal performance for Intel® Core™ 7th Generation and later This procedure outlines the steps required to tune a platform for optimal thermal performance. Prerequisites The latest release of dptfxtract tool from https://github.com/intel/dptfxtract The latest release of Linux thermal daemon (thermald) from https://github.com/intel/thermal_daemon (version 1.9 or later) Linux kernel version 5.3 or later (Refer to workaround option for older kernels) Step 1 Download the latest version of the tool "dptfxtract" from https://github.com/intel/dptfxtract Step 2 It is possible that your Linux distribution already enabled the Linux Thermal Daemon. Check the version of the thermald: # thermald --version If it is 1.9 or later, you can skip Step 3. Step 3 Clone or download the latest thermald from https://github.com/intel/thermal_daemon Follow the build procedure based on your distribution from https://github.com/intel/thermal_daemon/blob/master/README.txt It is possible that there is an existing older version of the thermald on your system; it is better to rename that to something else before calling "make install". # which thermald This shows the current location of your thermald, if it exists. Step 4 Check the status of the existing thermald service. #systemctl status thermald.service It may show that service is "Active and running". If it is, stop the service. #systemctl stop thermald.service Step 5 Run dptfxtract tool. #./dptfxtract This will print if any thermal configuration files are found. For example, I see the following output on the test system. #./dptfxtract DPTF Tables Extraction Utility Copyright (c) 2000 - 2019 Intel Corporation Output file is /etc/thermald/thermal-conf.xml.0 Output file is /etc/thermald/thermal-conf.xml.1 Output file is /etc/thermald/thermal-conf.xml.2 Output file is /etc/thermald/thermal-conf.xml.3 Output file is /etc/thermald/thermal-conf.xml.4 Output file is /etc/thermald/thermal-conf.xml.auto Step 6 Check the kernel version, using " uname -a". If the kernel version is 5.3 or above, then check #ls /sys/class/powercap/intel-rapl If this folder is not present or kernel version is 5.2 or lower, then add option "--workaround-enabled" to the thermald command line. It is fine to leave the "--workaround-enabled" option for later kernels as it will be ignored. But this option will only work on platforms with secure boot disabled and have access to /dev/mem. For some Lenovo systems, if the kernel version is less than 5.4 (best case for the kernel change to be accepted), also do #modprobe msr Step 7 Now run thermald from command line #./thermald --no-daemon --loglevel=info --dbus-enable --workaround-enabled It will print which configuration file is getting used. For example " [INFO]Using config file //etc/thermald/thermal-conf.xml.auto " Step 8 Run any workload you would like to, as long as it demands high performance. Or you can use "Stress". #stress -c 8 Run it for a while. If you don't get thermal shutdowns and if you are comfortable with the fan noise and heat it is generating, then the default configuration is right for you. If the default is not right for you, you have to manually find the best configuration file. For example to pick a configuration file generated by the dptfxtrcat tool. For example to pick " /etc/thermald/thermal-conf.xml.0": #./thermald --no-daemon --loglevel=info --dbus-enable --config-file /etc/thermald/thermal-conf.xml.0 Repeat this step until you are satisfied with the thermal performance with the configuration file. Step 9 If you want to monitor sensor temperatures in graphical display, there is a tool called "ThermalMonitor". This is submitted as part of the thermald. https://github.com/intel/thermal_daemon/tree/master/tools/thermal_monitor There is a procedure to build it in this folder. But since it involves Qt, this may be overwhelming for average users. So you can avoid this step. Step 10 If you decided to change the default configuration file, then you need to edit the systemd unit file for the thermald service. #systemctl status thermald.service This shows the file location. In my case /usr/lib/systemd/system/thermald.service You can add the --config-file with the desired config file to the line ExecStart= Then run #systemctl daemon-reload Then restart the thermald service and reboot. #systemctl start thermald.service 0707010000009E000081A400000000000000000000000166853FAC00001499000000000000000000000000000000000000004500000000thermal_daemon-2.5.7.17.git+fc7464a/test/thermald_test_kern_module.c/* * Test module to test thermald * * Copyright (C) 2015, Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ /* * To build add * obj-m += thermald_test_kern_module.o in drivers/thermal/Makefile */ #include <linux/module.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/kobject.h> #include <linux/sysfs.h> #include <linux/thermal.h> #define SENSOR_COUNT 10 #define CDEV_COUNT 10 struct thermald_sensor { struct thermal_zone_device *tzone; }; struct thermald_cdev { struct thermal_cooling_device *cdev; unsigned long max_state; unsigned long curr_state; }; static struct thermald_sensor *sensors[SENSOR_COUNT]; static struct thermald_cdev *cdevs[CDEV_COUNT]; static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) { *temp = 10000; return 0; } static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp) { *temp = 40000; return 0; } static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type *type) { *type = THERMAL_TRIP_PASSIVE; return 0; } static struct thermal_zone_device_ops tzone_ops = { .get_temp = sys_get_curr_temp, .get_trip_temp = sys_get_trip_temp, .get_trip_type = sys_get_trip_type, }; static struct thermald_sensor *create_test_tzone(int id) { struct thermald_sensor *sensor; char name[20]; sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); if (!sensor) return NULL; snprintf(name, sizeof(name), "thd_test_%d", id); sensor->tzone = thermal_zone_device_register(name, 1, 0, sensor, &tzone_ops, NULL, 0, 0); if (IS_ERR(sensor->tzone)) { kfree(sensor); return NULL; } return sensor; } static void destroy_test_tzone(struct thermald_sensor *sensor) { if (!sensor) return; thermal_zone_device_unregister(sensor->tzone); kfree(sensor); } static int get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct thermald_cdev *pcdev = cdev->devdata; *state = pcdev->max_state; return 0; } static int get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct thermald_cdev *pcdev = cdev->devdata; *state = pcdev->curr_state; return 0; } static int set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct thermald_cdev *pcdev = cdev->devdata; pcdev->curr_state = state; return 0; } static const struct thermal_cooling_device_ops cdev_ops = { .get_max_state = get_max_state, .get_cur_state = get_cur_state, .set_cur_state = set_cur_state, }; static struct thermald_cdev *create_test_cdev(int id) { struct thermald_cdev *cdev; char name[20]; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return NULL; snprintf(name, sizeof(name), "thd_cdev_%d", id); cdev->max_state = 10; cdev->cdev = thermal_cooling_device_register(name, cdev, &cdev_ops); if (IS_ERR(cdev->cdev)) { kfree(cdev); return NULL; } return cdev; } static void destroy_test_cdev(struct thermald_cdev *cdev) { if (!cdev) return; thermal_cooling_device_unregister(cdev->cdev); kfree(cdev); } static int sensor_temp; static ssize_t sensor_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", sensor_temp); } static ssize_t sensor_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { sscanf(buf, "%d", &sensor_temp); return count; } static int control_state; static ssize_t control_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", control_state); } static ssize_t control_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { sscanf(buf, "%d", &control_state); return count; } static struct kobj_attribute sensor_attribute = __ATTR(sensor_temp, 0644, sensor_show, sensor_store); static struct kobj_attribute control_attribute = __ATTR(control_state, 0644, control_show, control_store); static struct attribute *thermald_attrs[] = { &sensor_attribute.attr, &control_attribute.attr, NULL, }; static struct attribute_group attr_group = { .attrs = thermald_attrs, }; static struct kobject *thermal_control_kobj; static int __init thermald_init(void) { int i; int ret; thermal_control_kobj = kobject_create_and_add("thermald_test", kernel_kobj); if (!thermal_control_kobj) return -ENOMEM; ret = sysfs_create_group(thermal_control_kobj, &attr_group); if (ret) { kobject_put(thermal_control_kobj); return ret; } for (i = 0; i < SENSOR_COUNT; ++i) { sensors[i] = create_test_tzone(i); } for (i = 0; i < CDEV_COUNT; ++i) { cdevs[i] = create_test_cdev(i); } return 0; } static void __exit thermald_exit(void) { int i; for (i = 0; i < SENSOR_COUNT; ++i) { destroy_test_tzone(sensors[i]); } for (i = 0; i < CDEV_COUNT; ++i) { destroy_test_cdev(cdevs[i]); } kobject_put(thermal_control_kobj); } module_init(thermald_init) module_exit(thermald_exit) MODULE_LICENSE("GPL v2"); 0707010000009F000081A400000000000000000000000166853FAC0000002D000000000000000000000000000000000000003D00000000thermal_daemon-2.5.7.17.git+fc7464a/thermal_daemon_usage.txtThermal Daemon Usage: Use: man thermald 070701000000A0000081A400000000000000000000000166853FAC000000CA000000000000000000000000000000000000004400000000thermal_daemon-2.5.7.17.git+fc7464a/thermald-resource.gresource.xml<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/org/freedesktop/thermald"> <file preprocess="xml-stripblanks">src/thd_dbus_interface.xml</file> </gresource> </gresources>070701000000A1000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000002A00000000thermal_daemon-2.5.7.17.git+fc7464a/tools070701000000A2000041ED00000000000000000000000266853FAC00000000000000000000000000000000000000000000003A00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor070701000000A3000081A400000000000000000000000166853FAC0000894D000000000000000000000000000000000000004200000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/COPYING GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/licenses/why-not-lgpl.html>. 070701000000A4000081A400000000000000000000000166853FAC00000984000000000000000000000000000000000000004100000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/README/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ Compatibility with thermald -------------------------------------------- This tool requires thermald version "1.4.3" or later. Building the Thermal Monitor with Qt Creator -------------------------------------------- The project uses Qt 5 for the graphical user interface portion of the thermal monitor. A normal distribution of Qt includes Qt Creator, the IDE used for editing the forms and code. Using Creator Qt is the easiest way to build the project. Open Qt Creator, click File->Open File or Project, browse the project files to open ThermalMonitor.pro. The project files are displayed on the left side under Headers, Sources, and Forms. Build the project by clicking Build->Build Project (or Ctrl-b). A solid green status bar in the lower right corner indicates a successful build. Running the project ------------------- To communicate with thermald via dbus, the user has to be member of a group able to manage power. So make sure that there is a power related group the system and add your user id to this group. On some systems this group will be called "power" and on others it may be called "powerdev". Building the Thermal Monitor with make -------------------------------------- Normally Qt Creator handles the compilation of the project. If you wish to use the command line to compile, then you first have to create a makefile. Qmake will create the makefile for you based on the project file. Invoke with: qmake ThermalMonitor.pro After the makefile has been created, use 'make' to build the project. The object files and executable will be in the same directory as the source code, a different location than when using Qt Creator. Use 'make clean' to clean up everything except the executable. Dependency on qcustomplot To build you need to download qcustomplot-devel or libqcustomplot-dev depending on your distribution. 070701000000A5000081A400000000000000000000000166853FAC0000030D000000000000000000000000000000000000004D00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/ThermalMonitor.pro#------------------------------------------------- # # Project created by QtCreator 2015-05-13T11:28:27 # #------------------------------------------------- QT += core gui dbus greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport # Do not print qDebug() messages in release builds CONFIG(release): DEFINES += QT_NO_DEBUG_OUTPUT TARGET = ThermalMonitor TEMPLATE = app LIBS += -lQCustomPlot SOURCES += main.cpp\ mainwindow.cpp \ thermaldinterface.cpp \ pollingdialog.cpp \ sensorsdialog.cpp \ logdialog.cpp \ tripsdialog.cpp HEADERS += mainwindow.h \ thermaldinterface.h \ pollingdialog.h \ sensorsdialog.h \ logdialog.h \ tripsdialog.h FORMS += \ pollingdialog.ui \ logdialog.ui \ tripsdialog.ui 070701000000A6000081A400000000000000000000000166853FAC000006EE000000000000000000000000000000000000004800000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/logdialog.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "logdialog.h" #include "ui_logdialog.h" #include <QDebug> #include <QMessageBox> LogDialog::LogDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LogDialog) { ui->setupUi(this); } LogDialog::~LogDialog() { delete ui; } void LogDialog::setLoggingState(bool logging, bool visible_only, QString filename) { ui->checkBoxLogging->setChecked(logging); ui->checkBoxLogVisibleOnly->setChecked(visible_only); ui->lineEditLogFilename->setText(filename); // disable inputs if logging is not enabled on_checkBoxLogging_toggled(ui->checkBoxLogging->checkState()); } void LogDialog::on_checkBoxLogging_toggled(bool checked) { if (checked) { ui->checkBoxLogVisibleOnly->setEnabled(true); ui->lineEditLogFilename->setEnabled(true); } else { ui->checkBoxLogVisibleOnly->setEnabled(false); ui->lineEditLogFilename->setEnabled(false); } } void LogDialog::on_buttonBox_accepted() { // send signal to mainwindow with logging status emit setLogVariable(ui->checkBoxLogging->checkState(), ui->checkBoxLogVisibleOnly->checkState(), ui->lineEditLogFilename->text()); } 070701000000A7000081A400000000000000000000000166853FAC00000485000000000000000000000000000000000000004600000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/logdialog.h/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef LOGDIALOG_H #define LOGDIALOG_H #include <QDialog> namespace Ui { class LogDialog; } class LogDialog : public QDialog { Q_OBJECT public: explicit LogDialog(QWidget *parent = 0); ~LogDialog(); void setLoggingState(bool logging, bool visible_only, QString filename); signals: void setLogVariable(bool logging_enabled, bool log_visible_only, QString log_file_name); private slots: void on_checkBoxLogging_toggled(bool checked); void on_buttonBox_accepted(); private: Ui::LogDialog *ui; }; #endif // LOGDIALOG_H 070701000000A8000081A400000000000000000000000166853FAC000009D8000000000000000000000000000000000000004700000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/logdialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>LogDialog</class> <widget class="QDialog" name="LogDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Log</string> </property> <widget class="QDialogButtonBox" name="buttonBox"> <property name="geometry"> <rect> <x>30</x> <y>240</y> <width>341</width> <height>32</height> </rect> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> <widget class="QCheckBox" name="checkBoxLogging"> <property name="geometry"> <rect> <x>50</x> <y>40</y> <width>101</width> <height>27</height> </rect> </property> <property name="text"> <string>Enable</string> </property> </widget> <widget class="QLineEdit" name="lineEditLogFilename"> <property name="geometry"> <rect> <x>50</x> <y>90</y> <width>113</width> <height>29</height> </rect> </property> </widget> <widget class="QLabel" name="label"> <property name="geometry"> <rect> <x>170</x> <y>94</y> <width>111</width> <height>21</height> </rect> </property> <property name="text"> <string>Filename</string> </property> </widget> <widget class="QCheckBox" name="checkBoxLogVisibleOnly"> <property name="geometry"> <rect> <x>160</x> <y>40</y> <width>191</width> <height>27</height> </rect> </property> <property name="text"> <string>Log visible only</string> </property> </widget> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>LogDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>248</x> <y>254</y> </hint> <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>LogDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>316</x> <y>260</y> </hint> <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> </hints> </connection> </connections> </ui> 070701000000A9000081A400000000000000000000000166853FAC000006BA000000000000000000000000000000000000004300000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/main.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include <QApplication> #include <QMessageBox> #include "mainwindow.h" #include <unistd.h> #define ROOT_ID 0 int main(int argc, char *argv[]) { QApplication a(argc, argv); // Warn the user if not running as root uid_t id = getuid(); if (id == ROOT_ID){ QMessageBox::critical(0, "Running as root is not supported", "Running X11 applications as root is unsafe.\n" "Try invoking again without root privileges."); return(1); } ThermaldInterface thermaldInterface; if (!thermaldInterface.initialize()) { QMessageBox::critical(0, "Can't establish link with thermal daemon.", "Make sure that thermal daemon started with --dbus-enable option\n" "and that you're in the power management group ('power', 'powerdev' etc)."); return 1; } QCoreApplication::setOrganizationDomain("intel.com"); QCoreApplication::setOrganizationName("Intel"); QCoreApplication::setApplicationName("ThermalMonitor"); MainWindow w(&thermaldInterface); w.show(); return a.exec(); } 070701000000AA000081A400000000000000000000000166853FAC00004581000000000000000000000000000000000000004900000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/mainwindow.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include <QMessageBox> #include <QDesktopWidget> #include "mainwindow.h" #include "pollingdialog.h" #include "sensorsdialog.h" #include "logdialog.h" #include "tripsdialog.h" #define DEFAULT_SCREEN_POS_DIVISOR 4 #define DEFAULT_SCREEN_SIZE_DIVISOR 2 #define DEFAULT_SCREEN_ASPECT_RATIO_NUM 3 #define DEFAULT_SCREEN_ASPECT_RATIO_DEN 4 #define DEFAULT_TEMP_POLL_INTERVAL_MS 4000 #define SAMPLE_STORE_SIZE 100 #define DEFAULT_LOGFILE_NAME "log.txt" #define CUSTOMPLOT_YAXIS_RANGE 120 #define VERSION_NUMBER "1.3" MainWindow::MainWindow(ThermaldInterface *thermaldInterface) : QMainWindow(), temp_samples(SAMPLE_STORE_SIZE), currentTempsensorIndex(0), temp_poll_interval(DEFAULT_TEMP_POLL_INTERVAL_MS), m_thermaldInterface(thermaldInterface), logging_enabled(false), log_visible_only(false), m_plotWidget(new QCustomPlot) { // Build up a color vector for holding a good variety colors.append(Qt::red); colors.append(Qt::green); colors.append(Qt::blue); colors.append(Qt::cyan); colors.append(Qt::magenta); colors.append(Qt::black); colors.append(QColor(200,200,00)); colors.append(QColor(220,20,60)); colors.append(QColor(255,20,147)); colors.append(QColor(145,44,238)); colors.append(QColor(0,255,127)); colors.append(QColor(127,255,0)); colors.append(QColor(255,215,0)); colors.append(QColor(255,193,37)); colors.append(QColor(255,153,18)); colors.append(QColor(255,69,0)); colors.append(Qt::gray); colors.append(Qt::yellow); loadSettings(); for (int i = 0; i < SAMPLE_STORE_SIZE; ++i) { temp_samples[i] = i; } setupMenus(); setCentralWidget(m_plotWidget); setupPlotWidget(); statusBar()->show(); } void MainWindow::setupMenus() { QMenu *settingsMenu = menuBar()->addMenu("&Settings"); settingsMenu->addAction("Configure &logging", this, SLOT(configureLogging())); settingsMenu->addAction("Configure &trips", this, SLOT(configureTrips())); settingsMenu->addAction("Configure &polling interval", this, SLOT(configurePollingInterval())); settingsMenu->addAction("Configure &sensors", this, SLOT(configureSensors())); settingsMenu->addAction("&Clear settings and quit", this, SLOT(clearAndExit())); settingsMenu->addAction("&Quit", this, SLOT(close()), QKeySequence::Quit); QMenu *helpMenu = menuBar()->addMenu("&Help"); helpMenu->addAction("&About", this, SLOT(showAboutDialog())); } MainWindow::~MainWindow() { if (logging_enabled){ logging_file.close(); } } void MainWindow::closeEvent(QCloseEvent *event) { storeSettings(); event->accept(); } void MainWindow::setupPlotWidget() { QPen pen; int temp; // give the axes some labels: m_plotWidget->xAxis->setLabel("Samples"); m_plotWidget->yAxis->setLabel("Temperature (°C)"); // set axes ranges, so we see all data: m_plotWidget->xAxis->setRange(0, SAMPLE_STORE_SIZE); m_plotWidget->yAxis->setRange(0, CUSTOMPLOT_YAXIS_RANGE); m_plotWidget->legend->setVisible(true); pen.setWidth(5); currentTempsensorIndex = 0; int active_zone = 0; for (uint zone = 0; zone < m_thermaldInterface->getZoneCount(); zone++) { // Trips is indexed by zone. We need to make sure there is a tips item // (even if empty) for each zone. trips.append(QVector<QCPItemLine *>()); zoneInformationType *zone_info = m_thermaldInterface->getZone(zone); if (!zone_info) continue; pen.setColor(colors[active_zone % colors.count()]); if ( zone_info->active) active_zone++; int sensor_cnt_per_zone = m_thermaldInterface->getSensorCountForZone(zone); if (sensor_cnt_per_zone <= 0) break; pen.setStyle(Qt::SolidLine); for (int cnt = 0; zone_info->active && cnt < sensor_cnt_per_zone; cnt++) { QString sensor_type; QString sensor_name; sensorZoneInformationType sensor_info; if (m_thermaldInterface->getSensorTypeForZone(zone, cnt, sensor_type) < 0) continue; sensor_name.append(zone_info->name); sensor_name.append(":"); sensor_name.append(sensor_type); m_plotWidget->addGraph(); m_plotWidget->graph(currentTempsensorIndex)->setName(sensor_name); m_plotWidget->graph(currentTempsensorIndex)->setPen(pen); current_sample_index[currentTempsensorIndex] = 0; sensor_info.index = m_thermaldInterface->getSensorIndex(sensor_type); sensor_info.display_name = sensor_name; sensor_info.sensor_name = sensor_type; sensor_info.zone = zone; sensor_types.append(sensor_info); sensor_visibility.append(true); currentTempsensorIndex++; } pen.setStyle(Qt::DashLine); // Draw a dashed horz line for each min valid trip temperature QVector<QCPItemLine *> these_trips; int trip_count = m_thermaldInterface->getTripCountForZone(zone); if (trip_count > 0) { for (int trip = 0; trip < trip_count; trip++){ QCPItemLine *line = new QCPItemLine(m_plotWidget); temp = m_thermaldInterface->getTripTempForZone(zone, trip); line->start->setCoords(0, temp); line->end->setCoords(SAMPLE_STORE_SIZE - 1, temp); line->setPen(pen); if (zone_info->active && temp == m_thermaldInterface->getLowestValidTripTempForZone(zone)) { line->setVisible(true); m_thermaldInterface->setTripVisibility(zone, trip, true); } else { line->setVisible(false); m_thermaldInterface->setTripVisibility(zone, trip, false); } these_trips.append(line); } trips.last().swap(these_trips); } } #ifdef DISPLAY_UNKNOWN // Now display sensors which are not part of any zone. Users can use this and assign to some zone for (uint i = 0; i < m_thermaldInterface->getSensorCount(); ++i) { sensorInformationType info; QString name; bool found = false; name = m_thermaldInterface->getSensorName(i); if (!name.isEmpty()){ // search if this is already registered as part of a zone sensor for (int j = 0; j < sensor_types.count(); ++j) { sensorZoneInformationType sensor_info = sensor_types[j]; if (name == sensor_info.sensor_name) { found = true; break; } } if (!found) { sensorZoneInformationType sensor_info; QString sensor_name; sensor_name.append("UKWN:"); sensor_name.append(name); m_plotWidget->addGraph(); m_plotWidget->graph(currentTempsensorIndex)->setName(sensor_name); m_plotWidget->graph(currentTempsensorIndex)->setPen(pen); current_sample_index[currentTempsensorIndex] = 0; sensor_info.index = m_thermaldInterface->getSensorIndex(name); sensor_info.display_name = sensor_name; sensor_info.sensor_name = name; sensor_info.zone = -1; sensor_types.append(sensor_info); sensor_visibility.append(true); currentTempsensorIndex++; } } } #endif connect(&tempUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTemperatureDataSlot())); tempUpdateTimer.start(temp_poll_interval); } void MainWindow::updateTemperatureDataSlot() { for (int i = 0; i < sensor_types.count(); ++i) { int temperature = m_thermaldInterface->getSensorTemperature(sensor_types[i].index); addNewTemperatureTemperatureSample(i, (double)temperature/1000.0); } if(logging_enabled) { outStreamLogging << endl; } m_plotWidget->replot(); // Show any active cooling devices on the status bar QString str; for (uint i = 0; i < m_thermaldInterface->getCoolingDeviceCount(); i++) { int current = m_thermaldInterface->getCoolingDeviceCurrentState(i); int min = m_thermaldInterface->getCoolingDeviceMinState(i); if (current > min){ if (str.isEmpty()){ str += "Cooling: "; } else { str += ", "; } str += QString("%2%3 (%4/%5)") .arg(m_thermaldInterface->getCoolingDeviceName(i)) .arg(i) .arg(current) .arg(m_thermaldInterface->getCoolingDeviceMaxState(i)); } } statusBar()->showMessage(str); } int MainWindow::addNewTemperatureTemperatureSample(int index, double temperature) { if (temperature_samples[index].size() >= SAMPLE_STORE_SIZE) { int i; for (i = 0; i < temperature_samples[index].count() - 1; i++){ temperature_samples[index][i] = temperature_samples[index][i + 1]; } temperature_samples[index][i] = temperature; } else { temperature_samples[index].push_back(temperature); current_sample_index[index]++; } m_plotWidget->graph(index)->setData(temp_samples, temperature_samples[index]); if (logging_enabled) { if (m_plotWidget->graph(index)->visible() || !log_visible_only){ outStreamLogging << temperature << ", "; } } return 0; } void MainWindow::loadSettings() { QSettings settings; // restore the previous window geometry, if available if (settings.contains("mainWindowGeometry/size")) { resize(settings.value("mainWindowGeometry/size").toSize()); move(settings.value("mainWindowGeometry/pos").toPoint()); } else { // otherwise start with the default geometry calculated from the desktop size QDesktopWidget desktop; QRect screen = desktop.screenGeometry(); int width, height, x, y; width = screen.width() / DEFAULT_SCREEN_SIZE_DIVISOR; height = ((screen.width() / DEFAULT_SCREEN_SIZE_DIVISOR) * DEFAULT_SCREEN_ASPECT_RATIO_NUM) / DEFAULT_SCREEN_ASPECT_RATIO_DEN; x = screen.width() / DEFAULT_SCREEN_POS_DIVISOR; y = screen.height() / DEFAULT_SCREEN_POS_DIVISOR; resize(width, height); move(x, y); } // restore the previous window state, if available if (settings.contains("mainWindowState")) { restoreState(settings.value("mainWindowState").toByteArray()); } // restore the polling interval, if available if (settings.contains("updateInterval")) { temp_poll_interval = settings.value("updateInterval").toUInt(); } else { // otherwise load the default temp_poll_interval = DEFAULT_TEMP_POLL_INTERVAL_MS; } // restore the log file name, if available if (settings.contains("logFilename") && !settings.value("logFilename").toString().isEmpty()) { log_filename = settings.value("logFilename").toString(); } else { // otherwise load the default log_filename = QString(DEFAULT_LOGFILE_NAME); } // restore the log visible only setting, if available if (settings.contains("logVisibleOnly")) { log_visible_only = settings.value("logVisibleOnly").toBool(); } else { // otherwise load the default log_visible_only = false; } // start out with logging disabled logging_enabled = false; } void MainWindow::storeSettings() { QSettings settings; settings.setValue("mainWindowGeometry/size", size()); settings.setValue("mainWindowGeometry/pos", pos()); settings.setValue("mainWindowState", saveState()); settings.setValue("updateInterval", temp_poll_interval); if (!log_filename.isEmpty()) { settings.setValue("logFilename", log_filename); } else { // restore default log file name from empty string name settings.setValue("logFilename", QString(DEFAULT_LOGFILE_NAME)); } settings.setValue("logVisibleOnly", log_visible_only); } void MainWindow::changePollIntervalSlot(uint new_val) { temp_poll_interval = new_val; tempUpdateTimer.start(temp_poll_interval); } void MainWindow::changeGraphVisibilitySlot(uint index, bool visible) { if (index < (uint) sensor_types.count()) { m_plotWidget->graph(index)->setVisible(visible); sensor_visibility[index] = visible; if (index < (uint)sensor_types.count()) { sensorZoneInformationType sensor_info = sensor_types[index]; if (sensor_info.zone >= 0) setTripVisibility(sensor_info.zone, 0, visible); } } } void MainWindow::changeLogVariables(bool log_enabled, bool log_vis_only, QString log_file_name) { if (log_enabled && log_file_name.isEmpty()) { QMessageBox msgBox(this); msgBox.setText("Please enter a valid filename.\r\rLogging not enabled"); msgBox.exec(); return; } /* if logging has just been turned on, open the file to write, * then output the sensor name header */ if (!logging_enabled && log_enabled) { // first set the file and output stream logging_file.setFileName(log_file_name); QTextStream out(&logging_file); if (!logging_file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCritical() << "Cannot open file for writing: " << qPrintable(logging_file.errorString()) << endl; return; } outStreamLogging.setDevice(&logging_file); // now output the temperature sensor names as a header for(int i = 0; i < sensor_types.count(); i++) { if (!log_vis_only || m_plotWidget->graph(i)->visible()) { outStreamLogging << m_plotWidget->graph(i)->name() << ", "; } } outStreamLogging << endl; } // logging has been turned off, so close the file if (logging_enabled && !log_enabled){ logging_file.close(); } // copy the flags and filename to this logging_enabled = log_enabled; log_visible_only = log_vis_only; log_filename = log_file_name; } void MainWindow::setTripSetpoint(uint zone, uint trip, int temperature) { trips[zone][trip]->start->setCoords(0, temperature); trips[zone][trip]->end->setCoords(SAMPLE_STORE_SIZE - 1, temperature); m_thermaldInterface->setTripTempForZone(zone, trip, temperature); } void MainWindow::setTripVisibility(uint zone, uint trip, bool visibility) { m_thermaldInterface->setTripVisibility(zone, trip, visibility); trips[zone][trip]->setVisible(visibility); } void MainWindow::clearAndExit() { QSettings settings; settings.clear(); QCoreApplication::quit(); } void MainWindow::configurePollingInterval() { PollingDialog *p = new PollingDialog(this, temp_poll_interval); QObject::connect(p, SIGNAL(setPollInterval(uint)), this, SLOT(changePollIntervalSlot(uint))); p->show(); // non-modal } void MainWindow::configureSensors() { SensorsDialog dialog(this); // set the checkbox names to match the temperature sensor names for (int i = 0; i < sensor_visibility.count(); i++) { dialog.addSensor(sensor_types[i].display_name, sensor_visibility[i]); } if (dialog.exec() == QDialog::Rejected) { return; } sensor_visibility = dialog.getVisibilities(); for (int i=0; i<sensor_visibility.count(); i++) { changeGraphVisibilitySlot(i, sensor_visibility[i]); } } void MainWindow::configureLogging() { LogDialog *l = new LogDialog(this); QObject::connect(l, SIGNAL(setLogVariable(bool, bool, QString)), this, SLOT(changeLogVariables(bool, bool, QString))); l->setLoggingState(logging_enabled, log_visible_only, log_filename); l->show(); } void MainWindow::showAboutDialog() { QString str; str = QString("<h3>Thermal Monitor %1</h3>" "<p>GUI for Linux thermal daemon (thermald)</p>" "<p>Copyright (c) 2022, Intel Corporation</p>" "<p>This program comes with ABSOLUTELY NO WARRANTY</p>" "<p>This work is licensed under GPL v3</p>" "<p>Refer to https://www.gnu.org/licenses/gpl-3.0.txt</p>") .arg(QString(VERSION_NUMBER)); QMessageBox::about(this, "About Thermal Monitor", str); } void MainWindow::configureTrips() { tripsDialog *t = new tripsDialog(this, m_thermaldInterface); QObject::connect(t, SIGNAL(setTripVis(uint, uint, bool)), this, SLOT(setTripVisibility(uint, uint, bool))); QObject::connect(t, SIGNAL(changeTripSetpoint(uint, uint, int)), this, SLOT(setTripSetpoint(uint, uint, int))); for(uint i = 0; i < m_thermaldInterface->getZoneCount(); i++) { t->addZone(m_thermaldInterface->getZone(i)); } t->show(); } 070701000000AB000081A400000000000000000000000166853FAC00000A15000000000000000000000000000000000000004700000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/mainwindow.h/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QVector> #include <QLabel> #include <string> #include <qcustomplot.h> #include "thermaldinterface.h" typedef struct { QString display_name; QString sensor_name; int index; int zone; } sensorZoneInformationType; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(ThermaldInterface * thermaldInterface); ~MainWindow(); void setupPlotWidget(); int addNewTemperatureTemperatureSample(int index, double temperature); bool getVisibleState(int index); protected: virtual void closeEvent(QCloseEvent *event); public slots: void changePollIntervalSlot(uint new_val); void changeGraphVisibilitySlot(uint index, bool visible); void changeLogVariables(bool log_enabled, bool log_vis_only, QString log_file_name); void setTripSetpoint(uint zone, uint trip, int temperature); void setTripVisibility(uint zone, uint trip, bool visibility); void updateTemperatureDataSlot(); private slots: void clearAndExit(); void configurePollingInterval(); void configureSensors(); void configureLogging(); void showAboutDialog(); void configureTrips(); private: void setupMenus(); QTimer tempUpdateTimer; QVector<QColor> colors; QVector<double> temp_samples; int currentTempsensorIndex; QVector<double> temperature_samples[MAX_TEMP_INPUT_COUNT]; int current_sample_index[MAX_TEMP_INPUT_COUNT]; uint temp_poll_interval; QVector<bool> sensor_visibility; // QLabel *sensor_label; QVector<QVector<QCPItemLine *> > trips; QVector<sensorZoneInformationType>sensor_types; ThermaldInterface *m_thermaldInterface; bool logging_enabled; bool log_visible_only; QString log_filename; QFile logging_file; QTextStream outStreamLogging; QCustomPlot *m_plotWidget; void loadSettings(); void storeSettings(); }; #endif // MAINWINDOW_H 070701000000AC000081A400000000000000000000000166853FAC0000052C000000000000000000000000000000000000004C00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/pollingdialog.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "pollingdialog.h" #include "ui_pollingdialog.h" #include "mainwindow.h" PollingDialog::PollingDialog(QWidget *parent, uint current_interval) : QDialog(parent), ui(new Ui::PollingDialog), default_interval(current_interval) { ui->setupUi(this); ui->lineEdit->setValidator(new QIntValidator); ui->lineEdit->setText(QString("%1").arg(current_interval)); ui->label->setText("Set temperature polling interval (ms)"); ui->label_2->setText("ms"); } PollingDialog::~PollingDialog() { delete ui; } void PollingDialog::on_lineEdit_returnPressed() { emit setPollInterval(ui->lineEdit->text().toInt()); } void PollingDialog::on_setButton_clicked() { emit setPollInterval(ui->lineEdit->text().toInt()); } 070701000000AD000081A400000000000000000000000166853FAC00000426000000000000000000000000000000000000004A00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/pollingdialog.h/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef POLLINGDIALOG_H #define POLLINGDIALOG_H #include <QDialog> namespace Ui { class PollingDialog; } class PollingDialog : public QDialog { Q_OBJECT public: explicit PollingDialog(QWidget *parent, uint current_interval); ~PollingDialog(); signals: void setPollInterval(uint val); private slots: void on_lineEdit_returnPressed(); void on_setButton_clicked(); private: Ui::PollingDialog *ui; uint default_interval; }; #endif // POLLINGDIALOG_H 070701000000AE000081A400000000000000000000000166853FAC000009C8000000000000000000000000000000000000004B00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/pollingdialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>PollingDialog</class> <widget class="QDialog" name="PollingDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>440</width> <height>187</height> </rect> </property> <property name="windowTitle"> <string>Set Polling Interval</string> </property> <widget class="QDialogButtonBox" name="buttonBox"> <property name="geometry"> <rect> <x>-25</x> <y>150</y> <width>341</width> <height>32</height> </rect> </property> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> <widget class="QLabel" name="label"> <property name="geometry"> <rect> <x>10</x> <y>60</y> <width>391</width> <height>21</height> </rect> </property> <property name="text"> <string>TextLabel</string> </property> </widget> <widget class="QLineEdit" name="lineEdit"> <property name="geometry"> <rect> <x>90</x> <y>90</y> <width>113</width> <height>29</height> </rect> </property> </widget> <widget class="QLabel" name="label_2"> <property name="geometry"> <rect> <x>210</x> <y>100</y> <width>67</width> <height>21</height> </rect> </property> <property name="text"> <string>ms</string> </property> </widget> <widget class="QPushButton" name="setButton"> <property name="geometry"> <rect> <x>250</x> <y>90</y> <width>85</width> <height>29</height> </rect> </property> <property name="text"> <string>Set</string> </property> </widget> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>PollingDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>248</x> <y>254</y> </hint> <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>PollingDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>316</x> <y>260</y> </hint> <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> </hints> </connection> </connections> </ui> 070701000000AF000081A400000000000000000000000166853FAC00000A4B000000000000000000000000000000000000004C00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/sensorsdialog.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include "sensorsdialog.h" #include <QDebug> #include <QVector> #include <QVBoxLayout> #include <QHBoxLayout> #include <QDialogButtonBox> #include <QPushButton> SensorsDialog::SensorsDialog(QWidget *parent) : QDialog(parent) { setWindowTitle("Select visible sensors"); QVBoxLayout *dialogLayout = new QVBoxLayout; setLayout(dialogLayout); QHBoxLayout *mainLayout = new QHBoxLayout; dialogLayout->addLayout(mainLayout); m_checkboxLayout = new QVBoxLayout; mainLayout->addLayout(m_checkboxLayout); QPushButton *checkAllButton = new QPushButton("Show all"); QPushButton *uncheckAllButton = new QPushButton("Hide all"); connect(checkAllButton, SIGNAL(clicked(bool)), this, SLOT(checkAll())); connect(uncheckAllButton, SIGNAL(clicked(bool)), this, SLOT(uncheckAll())); QVBoxLayout *actionsLayout = new QVBoxLayout; mainLayout->addLayout(actionsLayout); actionsLayout->addWidget(checkAllButton); actionsLayout->addWidget(uncheckAllButton); actionsLayout->addStretch(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); dialogLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } SensorsDialog::~SensorsDialog() { } void SensorsDialog::addSensor(QString name, bool checked) { QCheckBox *checkbox = new QCheckBox(name); checkbox->setChecked(checked); m_checkboxes.append(checkbox); m_checkboxLayout->addWidget(checkbox); } QVector<bool> SensorsDialog::getVisibilities() { QVector<bool> visibilities; foreach(const QCheckBox *checkbox, m_checkboxes) { visibilities.append(checkbox->isChecked()); } return visibilities; } void SensorsDialog::uncheckAll() { foreach(QCheckBox *checkbox, m_checkboxes) { checkbox->setChecked(false); } } void SensorsDialog::checkAll() { foreach(QCheckBox *checkbox, m_checkboxes) { checkbox->setChecked(true); } } 070701000000B0000081A400000000000000000000000166853FAC0000041F000000000000000000000000000000000000004A00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/sensorsdialog.h/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef SENSORSDIALOG_H #define SENSORSDIALOG_H #include <QCheckBox> #include <QDialog> class SensorsDialog : public QDialog { Q_OBJECT public: explicit SensorsDialog(QWidget *parent = 0); ~SensorsDialog(); void addSensor(QString name, bool checked); QVector<bool> getVisibilities(); private slots: void uncheckAll(); void checkAll(); private: QVector<QCheckBox*> m_checkboxes; QLayout *m_checkboxLayout; }; #endif // SENSORSDIALOG_H 070701000000B1000081A400000000000000000000000166853FAC00004785000000000000000000000000000000000000005000000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/thermaldinterface.cpp/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include <QtDebug> #include <QTreeWidgetItem> #include "thermaldinterface.h" #include "tripsdialog.h" ThermaldInterface::ThermaldInterface() : iface(NULL) { } ThermaldInterface::~ThermaldInterface() { delete iface; } bool ThermaldInterface::initialize() { /* Create the D-Bus interface * Get the temperature sensor count and information from each sensor * Get the cooling device count and information from each device */ if (!QDBusConnection::systemBus().isConnected()) { qCritical() << "Cannot connect to the D-Bus session bus"; return false; } iface = new QDBusInterface(SERVICE_NAME, "/org/freedesktop/thermald", "org.freedesktop.thermald", QDBusConnection::systemBus()); if (!iface->isValid()) { qCritical() << "Cannot connect to interface" << SERVICE_NAME; return false; } // get temperature sensor count uint sensor_count; QDBusReply<uint> count = iface->call("GetSensorCount"); if (count.isValid()) { if (count <= MAX_TEMP_INPUT_COUNT){ sensor_count = count; } else { qCritical() << "error: input sensor count of" << count << "is larger than system maximum (" << MAX_TEMP_INPUT_COUNT << ")"; return false; } } else { qCritical() << "error from" << iface->interface() << "=" << count.error(); return false; } // Read in all the temperature sensor data from the thermal daemon for (uint i = 0; i < sensor_count; i++){ sensorInformationType new_sensor; if (getSensorInformation(i, new_sensor) >= 0){ sensors.append(new_sensor); } } if (sensor_count != (uint)sensors.count()){ qCritical() << "error: ThermaldInterface::getSensorCount() sensor_count != sensors.count())," << sensor_count << "vs" << sensors.count(); } // get cooling device count uint cooling_device_count; QDBusReply<uint> cdev_count = iface->call("GetCdevCount"); if (cdev_count.isValid()) { cooling_device_count = cdev_count; } else { qCritical() << "error from" << iface->interface() << "=" << cdev_count.error(); return false; } // Read in all the the cooling device data from the thermal daemon for (uint i = 0; i < cooling_device_count; i++){ coolingDeviceInformationType new_device; if (getCoolingDeviceInformation(i, new_device) >= 0){ cooling_devices.append(new_device); } } if (cooling_device_count != (uint)cooling_devices.count()){ qCritical() << "error: ThermaldInterface::getCoolingDeviceCount()" << " cooling_device_count != cooling_devices.count()," << cooling_device_count << "vs" << cooling_devices.count(); } // get zone count uint zone_count; QDBusReply<uint> z_count = iface->call("GetZoneCount"); if (z_count.isValid()) { zone_count = z_count; } else { qCritical() << "error from" << iface->interface() << "=" << z_count.error(); return false; } // Read in all the the zone data from the thermal daemon for (uint i = 0; i < zone_count; i++){ zoneInformationType new_zone; if (getZoneInformation(i, new_zone) >= 0){ zones.append(new_zone); } } if (zone_count != (uint)zones.count()){ qCritical() << "error: ThermaldInterface::getCoolingDeviceCount()" << " zone_count != zones.count()," << zone_count << "vs" << zones.count(); } // Read in all the the trip data from the thermal daemon for (uint i = 0; i < zone_count; i++){ uint lowest_valid_index = (uint)zones[i].trip_count; for (uint j = 0; j < (uint)zones[i].trip_count; j++){ tripInformationType new_trip; if (getTripInformation(i, j, new_trip) >= 0){ zones[i].trips.append(new_trip); // Figure out the lowest valid trip index if (new_trip.trip_type == CRITICAL_TRIP || new_trip.trip_type == PASSIVE_TRIP){ if (j < lowest_valid_index){ lowest_valid_index = j; } } } } // Store the actual number of trips, as opposed to the theoretical maximum zones[i].trip_count = zones[i].trips.count(); // Store the first valid trip temp for the zone if (lowest_valid_index < (uint)zones[i].trip_count){ zones[i].lowest_valid_index = lowest_valid_index; //trip_temps.append(zones[i].trips[lowest_valid_index].temp); } else { qCritical() << "error: ThermaldInterface::initialize()" << " could not find valid trip for zone" << i; } /* Yo! zones[i].trip_count is the theoretical count * but zones[i].trips.count() is the actual valid count * * So when you want the trip count for any given zone, * use zones[i].trips.count() */ } return true; } uint ThermaldInterface::getCoolingDeviceCount(){ return cooling_devices.count(); } QString ThermaldInterface::getCoolingDeviceName(uint index) { if (index < (uint)cooling_devices.count()){ return cooling_devices[index].name; } else { qCritical() << "error: ThermaldInterface::getCoolingDeviceName" << "index" << index << "is >= than cooling_device.count" << cooling_devices.count(); return QString(""); } } int ThermaldInterface::getCoolingDeviceMinState(uint index) { if (index < (uint)cooling_devices.count()){ return cooling_devices[index].min_state; } else { qCritical() << "error: ThermaldInterface::getCoolingMinState" << "index" << index << "is >= than cooling_device.count" << cooling_devices.count(); return -1; } } int ThermaldInterface::getCoolingDeviceMaxState(uint index) { if (index < (uint)cooling_devices.count()){ return cooling_devices[index].max_state; } else { qCritical() << "error: ThermaldInterface::getCoolingMaxState" << "index" << index << "is >= than cooling_device.count" << cooling_devices.count(); return -1; } } int ThermaldInterface::getCoolingDeviceCurrentState(uint index) { if (index < (uint)cooling_devices.count()){ coolingDeviceInformationType cdev; // The state may have changed, so poll it again getCoolingDeviceInformation(index, cdev); cooling_devices[index].current_state = cdev.current_state; return cooling_devices[index].current_state; } else { qCritical() << "error: ThermaldInterface::getCoolingCurrentState" << "index" << index << "is >= than cooling_device.count" << cooling_devices.count(); return -1; } } int ThermaldInterface::getCoolingDeviceInformation(uint index, coolingDeviceInformationType &info) { QDBusMessage result; result = iface->call("GetCdevInformation", index); if (result.type() == QDBusMessage::ReplyMessage) { info.name = result.arguments().at(0).toString(); info.min_state = result.arguments().at(1).toInt(); info.max_state = result.arguments().at(2).toInt(); info.current_state = result.arguments().at(3).toInt(); return 0; } else { qCritical() << "error from" << iface->interface() << result.errorMessage(); return -1; } } uint ThermaldInterface::getSensorCount() { return sensors.count(); } int ThermaldInterface::getSensorInformation(uint index, sensorInformationType &info) { QDBusMessage result; result = iface->call("GetSensorInformation", index); if (result.type() == QDBusMessage::ReplyMessage) { info.name = result.arguments().at(0).toString(); info.path = result.arguments().at(1).toString(); info.temperature = result.arguments().at(2).toInt(); return 0; } else { qCritical() << "error from" << iface->interface() << result.errorMessage(); return -1; } } QString ThermaldInterface::getSensorName(uint index){ if (index < (uint)sensors.size()){ return sensors[index].name; } else { return QString(""); } } QString ThermaldInterface::getSensorPath(uint index){ if (index < (uint)sensors.size()){ return sensors[index].path; } else { return QString(""); } } int ThermaldInterface::getSensorTemperature(uint index) { QDBusReply<uint> temp = iface->call("GetSensorTemperature", index); if (temp.isValid()) { return temp; } else { qCritical() << "error from" << iface->interface() << "=" << temp.error(); return -1; } } int ThermaldInterface::getTripInformation(uint zone_index, uint trip_index, tripInformationType &info) { QDBusMessage result; result = iface->call("GetZoneTripAtIndex", zone_index, trip_index); if (result.type() == QDBusMessage::ReplyMessage) { info.temp = result.arguments().at(0).toInt() / 1000; info.trip_type = result.arguments().at(1).toInt(); info.sensor_id = result.arguments().at(2).toInt(); info.cdev_size = result.arguments().at(3).toInt(); // info.cdev_ids = result.arguments().at(4).toList(); return 0; } else if (result.signature().isEmpty()){ // If we get and empty response, then ignore it, but return error code return -1; } else { qCritical() << "error from" << iface->interface() << result.errorMessage() << zone_index << trip_index; return -1; } } int ThermaldInterface::getLowestValidTripTempForZone(uint zone) { if (zone < (uint)zones.count()){ return zones[zone].trips[zones[zone].lowest_valid_index].temp; } else { qCritical() << "error: ThermaldInterface::getLowestValidTripTempForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } int ThermaldInterface::getSensorCountForZone(uint zone) { if (zone < (uint)zones.count()){ return (uint)zones[zone].sensor_count; } else { qCritical() << "error: ThermaldInterface::getSensorCountForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } int ThermaldInterface::getSensorTypeForZone(uint zone, uint index, QString &sensor_type) { QDBusMessage result; result = iface->call("GetZoneSensorAtIndex", zone, index); if (result.type() == QDBusMessage::ReplyMessage) { sensor_type = result.arguments().at(0).toString(); return 0; } else { qCritical() << "error from" << iface->interface() << result.errorMessage(); return -1; } } int ThermaldInterface::getSensorIndex(QString sensor_type) { for (uint i = 0; i < (uint)sensors.count(); i++){ if(sensor_type == sensors[i].name) return (int)i; } return -1; } int ThermaldInterface::getTripCountForZone(uint zone) { /* Yo! zones[i].trip_count is the theoretical count * but zones[i].trips.count() is the actual valid count * * So when you want the trip count for any given zone, * use zones[i].trips.count() */ if (zone < (uint)zones.count()){ return (uint)zones[zone].trips.count(); } else { qCritical() << "error: ThermaldInterface::getTripCountForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } int ThermaldInterface::getTripTempForZone(uint zone, uint trip) { if (zone < (uint)zones.count()){ if (trip < zones[zone].trip_count){ return zones[zone].trips[trip].temp; } else { qCritical() << "error: ThermaldInterface::getTripTempForZone" << "trip index" << trip << "is >= than trip count" << zones[zone].trip_count; return -1; } } else { qCritical() << "error: ThermaldInterface::getTripTempForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } int ThermaldInterface::getTripTypeForZone(uint zone, uint trip) { if (zone < (uint)zones.count()){ if (trip < zones[zone].trip_count){ return zones[zone].trips[trip].trip_type; } else { qCritical() << "error: ThermaldInterface::getTripTypeForZone" << "trip index" << trip << "is >= than trip count" << zones[zone].trip_count; return -1; } } else { qCritical() << "error: ThermaldInterface::getTripTypeForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } zoneInformationType* ThermaldInterface::getZone(uint zone) { if (zone < (uint)zones.count()){ return &zones[zone]; } else { qCritical() << "error: ThermaldInterface::getZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return NULL; } } uint ThermaldInterface::getZoneCount() { return (uint)zones.count(); } QString ThermaldInterface::getZoneName(uint zone) { if (zone < (uint)zones.count()){ return zones[zone].name; } else { qCritical() << "error: ThermaldInterface::getZoneName" << "zone index" << zone << "is >= than zone count" << zones.count(); return QString(""); } } int ThermaldInterface::getZoneInformation(uint index, zoneInformationType &info) { QDBusMessage result; result = iface->call("GetZoneInformation", index); if (result.type() == QDBusMessage::ReplyMessage) { info.name = result.arguments().at(0).toString(); info.sensor_count = result.arguments().at(1).toInt(); info.trip_count = result.arguments().at(2).toInt(); info.active = result.arguments().at(3).toInt(); info.lowest_valid_index = 0; info.id = index; return 0; } else { qCritical() << "error from" << iface->interface() << result.errorMessage(); return -1; } } int ThermaldInterface::setTripTempForZone(uint zone, uint trip, int temperature) { if (zone < (uint)zones.count()){ if (trip < zones[zone].trip_count){ zones[zone].trips[trip].temp = temperature; // tell thermald to change the temperature for max and passive if (zones[zone].trips[trip].trip_type == MAX_TRIP){ iface->call("SetUserMaxTemperature", zones[zone].name, (uint)(temperature * 1000)); return 0; } else if (zones[zone].trips[trip].trip_type == PASSIVE_TRIP){ iface->call("SetUserPassiveTemperature", zones[zone].name, (uint)(temperature * 1000)); // Call reinit from thermald in order to get the change to stick iface->call("Reinit"); return 0; } else { qCritical() << "error: ThermaldInterface::setTripTempForZone" << "trip type is not 'max' or 'passive'" << "zone" << zone << "trip" << trip; return -1; } } else { qCritical() << "error: ThermaldInterface::setTripTempForZone" << "trip index" << trip << "is >= than trip count" << zones[zone].trip_count; return -1; } } else { qCritical() << "error: ThermaldInterface::setTripTempForZone" << "zone index" << zone << "is >= than zone count" << zones.count(); return -1; } } void ThermaldInterface::setTripVisibility(uint zone, uint trip, bool visibility) { if (zone < (uint)zones.count()){ if (trip < zones[zone].trip_count){ zones[zone].trips[trip].visible = visibility; } else { qCritical() << "error: ThermaldInterface::setTripVisibility" << "trip index" << trip << "is >= than trip count" << zones[zone].trip_count; } } else { qCritical() << "error: ThermaldInterface::setTripVisibility" << "zone index" << zone << "is >= than zone count" << zones.count(); } } bool ThermaldInterface::tripVisibility(uint zone, uint trip) { if (zone < (uint)zones.count()){ if (trip < zones[zone].trip_count){ return zones[zone].trips[trip].visible; } else { qCritical() << "error: ThermaldInterface::tripVisibility" << "trip index" << trip << "is >= than trip count" << zones[zone].trip_count; return false; } } else { qCritical() << "error: ThermaldInterface::tripVisibility" << "zone index" << zone << "is >= than zone count" << zones.count(); return false; } } 070701000000B2000081A400000000000000000000000166853FAC00000C39000000000000000000000000000000000000004E00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/thermaldinterface.h/* * Thermal Monitor displays current temperature readings on a graph * Copyright (c) 2015, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 3 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #ifndef THERMALDINTERFACE_H #define THERMALDINTERFACE_H #include <QtCore/QCoreApplication> #include <QtDBus/QtDBus> #define SERVICE_NAME "org.freedesktop.thermald" #define MAX_TEMP_INPUT_COUNT 64 enum { CRITICAL_TRIP, HOT_TRIP, MAX_TRIP, PASSIVE_TRIP, ACTIVE_TRIP, POLLING_TRIP, INVALID_TRIP }; typedef struct { QString name; int min_state; int max_state; int current_state; } coolingDeviceInformationType; typedef struct { QString name; QString path; int temperature; } sensorInformationType; typedef struct { int temp; int trip_type; bool visible; int sensor_id; int cdev_size; QList<int> cdev_ids; // Not currently using } tripInformationType; typedef struct { uint id; QString name; uint sensor_count; uint trip_count; QVector<tripInformationType> trips; uint lowest_valid_index; int active; } zoneInformationType; class ThermaldInterface { public: ThermaldInterface(); ~ThermaldInterface(); bool initialize(); uint getCoolingDeviceCount(); QString getCoolingDeviceName(uint index); int getCoolingDeviceMinState(uint index); int getCoolingDeviceMaxState(uint index); int getCoolingDeviceCurrentState(uint index); uint getSensorCount(); QString getSensorName(uint index); QString getSensorPath(uint index); int getSensorTemperature(uint index); int getSensorIndex(QString sensor_type); int getSensorCountForZone(uint zone); int getSensorTypeForZone(uint zone, uint index, QString &sensor_type); int getLowestValidTripTempForZone(uint zone); int getTripCountForZone(uint zone); int getTripTempForZone(uint zone, uint trip); int getTripTypeForZone(uint zone, uint trip); zoneInformationType* getZone(uint zone); uint getZoneCount(); QString getZoneName(uint zone); int setTripTempForZone(uint zone, uint trip, int temperature); bool tripVisibility(uint zone, uint trip); void setTripVisibility(uint zone, uint trip, bool visibility); private: QDBusInterface *iface; QVector<coolingDeviceInformationType> cooling_devices; QVector<sensorInformationType> sensors; QVector<zoneInformationType> zones; int getCoolingDeviceInformation(uint index, coolingDeviceInformationType &info); int getSensorInformation(uint index, sensorInformationType &info); int getTripInformation(uint zone_index, uint trip_index, tripInformationType &info); int getZoneInformation(uint index, zoneInformationType &info); }; #endif 070701000000B3000081A400000000000000000000000166853FAC000017C8000000000000000000000000000000000000004A00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/tripsdialog.cpp#include "tripsdialog.h" #include "ui_tripsdialog.h" tripsDialog::tripsDialog(QWidget *parent, ThermaldInterface *therm) : QDialog(parent), ui(new Ui::tripsDialog) { thermal = therm; ui->setupUi(this); ui->label->setText(""); ui->label_2->setText(""); ui->lineEdit->setEnabled(false); ui->lineEdit->setValidator(new QIntValidator); this->last_item = NULL; this->last_trip = 0; this->last_zone = 0; } tripsDialog::~tripsDialog() { delete ui; } void tripsDialog::addZone(zoneInformationType *zone) { if (!zone->active) return; QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui->treeWidget); treeItem->setText(0, zone->name); for(uint i = 0; i < (uint)zone->trips.count(); i++){ QTreeWidgetItem *treeTripItem = new QTreeWidgetItem(treeItem); treeTripItem->setData(1, Qt::DisplayRole, zone->trips[i].temp); treeTripItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); zone_display_list.append(zone->id); QString str; switch(zone->trips[i].trip_type){ case CRITICAL_TRIP: str = "Critical"; break; case MAX_TRIP: str = "Max"; break; case PASSIVE_TRIP: str = "Passive"; break; case ACTIVE_TRIP: str = "Active"; break; case POLLING_TRIP: str = "Polling"; break; case INVALID_TRIP: str = "Invalid"; break; default: qCritical() << "tripsDialog::addZone" << "invalid trip type: " << zone->trips[i].trip_type; str = "I'm losing my mind Dave"; } treeTripItem->setText(2, str); setRowHighlighting(treeTripItem, zone->trips[i].visible); } // Sorting by temperature looks better, but we would have to track the trip index // treeItem->sortChildren(1, Qt::AscendingOrder); ui->treeWidget->setToolTip("Click on temperature to change.\n" "Double click to display or clear."); resizeColumns(); } void tripsDialog::resizeColumns() { for (int i = 0; i < ui->treeWidget->columnCount(); i++){ ui->treeWidget->resizeColumnToContents(i); } } void tripsDialog::setRowHighlighting(QTreeWidgetItem *item, bool visible) { if (visible){ item->setForeground(1, Qt::white); item->setBackground(1, Qt::black); item->setForeground(2, Qt::white); item->setBackground(2, Qt::black); item->setForeground(3, Qt::white); item->setBackground(3, Qt::black); item->setText(3, "Yes"); } else { item->setForeground(1, Qt::black); item->setBackground(1, Qt::white); item->setForeground(2, Qt::black); item->setBackground(2, Qt::white); item->setForeground(3, Qt::black); item->setBackground(3, Qt::white); item->setText(3, ""); } } void tripsDialog::on_treeWidget_clicked(const QModelIndex &index) { int zone; int trip; int col; // check to see if the user clicked on a zone root node if(index.parent().column() == -1){ zone = index.row(); col = index.column(); trip = -1; } else { // otherwise the user clicked on a trip zone = index.parent().row(); zone = zone_display_list[zone]; col = index.column(); trip = index.row(); } // Alternate the background color, black to display on the graph, white to ignore if (trip != -1 && col == 1){ // if the user clicks on a temperature ui->label->setText("Zone: " + index.parent().data().toString()); ui->label_2->setText("Type: " + index.parent().child(trip, col + 1).data().toString() + " (°C)"); ui->lineEdit->setText(index.data().toString()); // ACTIVE_TRIP modification not supported at this time if (thermal->getTripTypeForZone(zone, trip) == MAX_TRIP || thermal->getTripTypeForZone(zone, trip) == PASSIVE_TRIP){ ui->lineEdit->setEnabled(true); // store the zone and trip in case the user edits the trip temperature last_zone = zone; last_trip = trip; last_item = ui->treeWidget->currentItem(); } else { ui->lineEdit->setEnabled(false); } } else { ui->label->setText("Click on"); ui->label_2->setText("temperature"); ui->lineEdit->setText(""); ui->lineEdit->setEnabled(false); } resizeColumns(); } void tripsDialog::on_treeWidget_doubleClicked(const QModelIndex &index) { int zone; int trip; // check to see if the user clicked on a zone root node if(index.parent().column() == -1){ zone = index.row(); } else { // otherwise the user clicked on a trip zone = index.parent().row(); zone = zone_display_list[zone]; trip = index.row(); // Invert the fore and background colors to display trip on the graph // Look at column 1, the trip temperature, to determine the outcome if (ui->treeWidget->currentItem()->foreground(1).color() == Qt::black){ setRowHighlighting(ui->treeWidget->currentItem(), true); emit setTripVis(zone, trip, true); } else { setRowHighlighting(ui->treeWidget->currentItem(), false); emit setTripVis(zone, trip, false); } } resizeColumns(); } void tripsDialog::on_buttonBox_accepted() { } void tripsDialog::on_buttonBox_rejected() { } void tripsDialog::on_treeWidget_expanded() { resizeColumns(); } void tripsDialog::on_treeWidget_collapsed() { resizeColumns(); } void tripsDialog::on_lineEdit_editingFinished() { last_item->setText(1, ui->lineEdit->text()); if (ui->lineEdit->text().toInt() != thermal->getTripTempForZone(last_zone, last_trip)){ emit changeTripSetpoint(last_zone, last_trip, ui->lineEdit->text().toInt()); } } 070701000000B4000081A400000000000000000000000166853FAC0000045C000000000000000000000000000000000000004800000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/tripsdialog.h#ifndef TRIPSDIALOG_H #define TRIPSDIALOG_H #include <QDialog> #include "thermaldinterface.h" #include "ui_tripsdialog.h" namespace Ui { class tripsDialog; } class tripsDialog : public QDialog { Q_OBJECT public: explicit tripsDialog(QWidget *parent = 0, ThermaldInterface *therm = 0); ~tripsDialog(); void addZone(zoneInformationType *zone); signals: void changeTripSetpoint(uint zone, uint trip, int temperature); void setTripVis(uint zone, uint trip, bool visibility); private slots: void on_treeWidget_clicked(const QModelIndex &index); void on_treeWidget_doubleClicked(const QModelIndex &index); void on_buttonBox_accepted(); void on_buttonBox_rejected(); void on_treeWidget_expanded(); void on_treeWidget_collapsed(); void on_lineEdit_editingFinished(); private: Ui::tripsDialog *ui; ThermaldInterface *thermal; QVector<uint> zone_display_list; QTreeWidgetItem *last_item; uint last_trip; uint last_zone; void resizeColumns(); void setRowHighlighting(QTreeWidgetItem *item, bool visible); }; #endif // TRIPSDIALOG_H 070701000000B5000081A400000000000000000000000166853FAC00000A45000000000000000000000000000000000000004900000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermal_monitor/tripsdialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>tripsDialog</class> <widget class="QDialog" name="tripsDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>670</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Temperature Trips</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="3" column="1"> <widget class="QLineEdit" name="lineEdit"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> </item> <item row="0" column="1"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> <item row="1" column="1"> <widget class="QLabel" name="label"> <property name="text"> <string>TextLabel</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QLabel" name="label_2"> <property name="text"> <string>TextLabel</string> </property> </widget> </item> <item row="0" column="0" rowspan="4"> <widget class="QTreeWidget" name="treeWidget"> <column> <property name="text"> <string>Zone</string> </property> </column> <column> <property name="text"> <string>Trip</string> </property> </column> <column> <property name="text"> <string>Type</string> </property> </column> <column> <property name="text"> <string>Display</string> </property> </column> </widget> </item> </layout> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>tripsDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>248</x> <y>254</y> </hint> <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>tripsDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>316</x> <y>260</y> </hint> <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> </hints> </connection> </connections> </ui> 070701000000B6000081ED00000000000000000000000166853FAC00001237000000000000000000000000000000000000003F00000000thermal_daemon-2.5.7.17.git+fc7464a/tools/thermald_set_pref.sh#!/bin/bash echo "****thermald preference****" echo "0 : DEFAULT" echo "1 : PERFORMANCE" echo "2 : ENERGY_CONSERVE" echo "3 : DISABLED" echo "4 : CALIBRATE" echo "5 : SET USER DEFINED CPU MAX temp" echo "6 : SET USER DEFINED CPU PASSIVE temp" echo "7 : TERMINATE" echo "8 : REINIT" echo "A : Add sensor test" echo "B : Get Sensor Information" echo "C : Add zone test" echo "D : Set zone test" echo "E : Get zone test" echo "F : Delete zone test" echo "G : Add cdev test" echo "H : Get Sensor Count" echo "I : Get Zone Count" echo "J : Get Zone Information" echo "K : Get Zone Sensor Information" echo "L : Get Zone Trip Information" echo "M : Get cdev count" echo "N : Get cdev Information" arg="0" if [[ "$1" != "" ]]; then if [[ "$2" != "" ]]; then arg=$2 fi opt_no=$1 echo "Taking command line argument as choice!" else echo -n " Enter thermald preference [1..6]: " read opt_no fi case $opt_no in 0) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"FALLBACK" ;; 1) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"PERFORMANCE" ;; 2) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"ENERGY_CONSERVE" ;; 3) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"DISABLE" ;; 4) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Calibrate ;; 5) echo -n " Enter valid max temp in mill degree celsius " read max_temp dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetUserMaxTemperature string:cpu uint32:$max_temp ;; 6) echo -n " Enter valid passive temp in mill degree celsius " read psv_temp dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetUserPassiveTemperature string:cpu uint32:$psv_temp ;; 7) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.Terminate ;; 8) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.Reinit ;; A) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.AddSensor string:"TEST_ADD_SENSOR" string:"/sys/class/thermal/thermal_zone0/temp" ;; B) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetSensorInformation uint32:0 ;; C) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.AddZonePassive string:"TEST_ADD_ZONE" uint32:90000 string:"hwmon" string:"intel_pstate" ;; D) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetZoneStatus string:"TEST_ADD_ZONE" int32:0 ;; E) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetZoneStatus string:"TEST_ADD_ZONE" ;; F) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.DeleteZone string:"TEST_ADD_ZONE" ;; G) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.AddCoolingDevice string:"TEST_CDEV" string:"/sys/class/thermal/cooling_device0/cur_state" int32:0 int32:1 int32:1 ;; H) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetSensorCount ;; I) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetZoneCount ;; J) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetZoneInformation uint32:0 ;; K) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetZoneSensorAtIndex uint32:0 uint32:$arg ;; L) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetZoneTripAtIndex uint32:0 uint32:$arg ;; M) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetCdevCount ;; N) dbus-send --system --dest=org.freedesktop.thermald --print-reply /org/freedesktop/thermald org.freedesktop.thermald.GetCdevInformation uint32:$arg ;; *) echo "Invalid option" esac 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1716 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor