pax_global_header00006660000000000000000000000064136170503770014523gustar00rootroot0000000000000052 comment=ab788abf4936dfb44fbb408afc34af834230a64d TLP-1.3.1/000077500000000000000000000000001361705037700121645ustar00rootroot00000000000000TLP-1.3.1/.github/000077500000000000000000000000001361705037700135245ustar00rootroot00000000000000TLP-1.3.1/.github/Bug_Reporting_Howto.md000066400000000000000000000024211361705037700177730ustar00rootroot00000000000000## How to submit a bug report ### Before you report a bug Make shure you have: * Followed the appropiate [Installation instructions](https://linrunner.de/en/tlp/docs/tlp-linux-advanced-power-management.html#installation) * Read the [Settings guide](https://linrunner.de/en/tlp/docs/tlp-configuration.html) * Carefully checked the [FAQ](https://linrunner.de/en/tlp/docs/tlp-faq.html) * Checked [existing bug reports](https://github.com/linrunner/TLP/issues) * Tried to isolate the cause as described in [Troubleshooting](https://linrunner.de/en/tlp/docs/tlp-troubleshooting.html) ### What not to report * Missing packages (including tp-smapi and acpi_call) * Asking for help about installation, configuration and usage * Questions about your laptop's power consumption and how to optimize it * Deviations from powertop's recommendations * Hardware issues e.g. worn out or malfunctioning batteries Please use adequate Linux forums for help and support questions. ### Reporting a bug This project uses [GitHub issues](https://github.com/linrunner/TLP/issues) for bug reports and feature requests. When opening an issue, provide all the information requested by the template. Bug reports not providing the necessary information get flagged *incomplete* and may be closed without further notice. TLP-1.3.1/.github/CODE_OF_CONDUCT.md000066400000000000000000000062331361705037700163270ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org TLP-1.3.1/.github/CONTRIBUTING.md000066400000000000000000000016011361705037700157530ustar00rootroot00000000000000## How can I contribute to TLP? Contributing is not only about coding and pull requests. Volunteers helping with testing and support are always welcome! ### Testing You may install and test the master branch at any time. Feel free to [file bug reports](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md) against it. Beta packages are [published here](https://download.linrunner.de/packages/) once available. Please refer to the [developer docs](https://linrunner.de/en/tlp/docs/tlp-developer-documentation.html) for more information. ### Support Experienced users are welcome to help others with TLP in their Linux community: * Reddit * StackExchange * Forums * User groups * or else ### Code Read the [developer docs](https://linrunner.de/en/tlp/docs/tlp-developer-documentation.html) before developing code. Please base your pull requests on the master branch. TLP-1.3.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001361705037700157075ustar00rootroot00000000000000TLP-1.3.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013711361705037700204030ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- [x] I've read and accepted the [Bug Reporting Howto](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md) **Describe the bug** A clear and concise description of what the bug is. **Expected behavior** A clear and concise description of what you expected to happen. **To Reproduce** Steps to reproduce the unexpected behavior: 1. Does the problem occur on battery or AC or both? 2. Actions to reproduce the behaviour 3. Shell commands entered and their output 4. **Full output of `tlp-stat` via https://gist.github.com/ for *all* matching cases of 1.** **Additional context** Add any other context about the problem here. TLP-1.3.1/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000013361361705037700214370ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: feature request assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I repeatedly ran into [...] **Describe the solution you'd like** A clear and concise description of: * Your use case(s) * What you want to happen **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context and references about the feature request here: * New kernel interfaces: please provide links to the description and usage examples * Sample shell code TLP-1.3.1/.perlcriticrc000066400000000000000000000002331361705037700146500ustar00rootroot00000000000000[ValuesAndExpressions::ProhibitConstantPragma] severity = 1 [InputOutput::RequireBriefOpen] severity = 1 [Subroutines::RequireArgUnpacking] severity = 1 TLP-1.3.1/00-template.conf000066400000000000000000000002371361705037700150650ustar00rootroot00000000000000# 00-template.conf - Template for TLP drop-in customizations # See full explanation: https://linrunner.de/en/tlp/docs/tlp-configuration.html # # PARAM="value" TLP-1.3.1/AUTHORS000066400000000000000000000003041361705037700132310ustar00rootroot00000000000000Main author: Thomas Koch - Contributors: André Erdmann Pali Rohár https://github.com/linrunner/TLP/graphs/contributors TLP-1.3.1/COPYING000066400000000000000000000013411361705037700132160ustar00rootroot00000000000000Main Author: Thomas Koch Copyright: Copyright (c) 2020 Thomas Koch, André Erdmann, Pali Rohár See https://github.com/linrunner/TLP/ for additional contributors Some code and descriptions were adapted from: - laptop-mode-tools Copyright (c) 2004 by Bart Samwel, Kiko Piris, Micha Feigin, Andrew Morton, Herve Eychenne, Dax Kelson, Jan Topinski - https://thinkwiki.org Thinkpad ACPI Battery Control (tpacpi-bat): Copyright (c) 2011-2016 Elliot Wolk License: This software is licensed under the GPL v2 or later, see '/usr/share/common-licenses/GPL-2' tpacpi-bat is licensed under the GPL v3 or later, see '/usr/share/common-licenses/GPL-3' TLP-1.3.1/LICENSE000066400000000000000000000431031361705037700131720ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. TLP-1.3.1/Makefile000066400000000000000000000147731361705037700136400ustar00rootroot00000000000000# Makefile for TLP TLPVER := $(shell read _ver _dummy < ./VERSION; printf '%s' "$${_ver:-undef}") # Evaluate parameters TLP_SBIN ?= /usr/sbin TLP_BIN ?= /usr/bin TLP_TLIB ?= /usr/share/tlp TLP_FLIB ?= /usr/share/tlp/func.d TLP_ULIB ?= /lib/udev TLP_NMDSP ?= /etc/NetworkManager/dispatcher.d TLP_CONFUSR ?= /etc/tlp.conf TLP_CONFDIR ?= /etc/tlp.d TLP_CONFDEF ?= /usr/share/tlp/defaults.conf TLP_CONF ?= /etc/default/tlp TLP_SYSD ?= /lib/systemd/system TLP_SDSL ?= /lib/systemd/system-sleep TLP_SYSV ?= /etc/init.d TLP_ELOD ?= /lib/elogind/system-sleep TLP_SHCPL ?= /usr/share/bash-completion/completions TLP_MAN ?= /usr/share/man TLP_META ?= /usr/share/metainfo TLP_RUN ?= /run/tlp TLP_VAR ?= /var/lib/tlp TPACPIBAT ?= $(TLP_TLIB)/tpacpi-bat # Catenate DESTDIR to paths _SBIN = $(DESTDIR)$(TLP_SBIN) _BIN = $(DESTDIR)$(TLP_BIN) _TLIB = $(DESTDIR)$(TLP_TLIB) _FLIB = $(DESTDIR)$(TLP_FLIB) _ULIB = $(DESTDIR)$(TLP_ULIB) _NMDSP = $(DESTDIR)$(TLP_NMDSP) _CONFUSR = $(DESTDIR)$(TLP_CONFUSR) _CONFDIR = $(DESTDIR)$(TLP_CONFDIR) _CONFDEF = $(DESTDIR)$(TLP_CONFDEF) _CONF = $(DESTDIR)$(TLP_CONF) _SYSD = $(DESTDIR)$(TLP_SYSD) _SDSL = $(DESTDIR)$(TLP_SDSL) _SYSV = $(DESTDIR)$(TLP_SYSV) _ELOD = $(DESTDIR)$(TLP_ELOD) _SHCPL = $(DESTDIR)$(TLP_SHCPL) _MAN = $(DESTDIR)$(TLP_MAN) _META = $(DESTDIR)$(TLP_META) _RUN = $(DESTDIR)$(TLP_RUN) _VAR = $(DESTDIR)$(TLP_VAR) _TPACPIBAT = $(DESTDIR)$(TPACPIBAT) SED = sed \ -e "s|@TLPVER@|$(TLPVER)|g" \ -e "s|@TLP_SBIN@|$(TLP_SBIN)|g" \ -e "s|@TLP_TLIB@|$(TLP_TLIB)|g" \ -e "s|@TLP_FLIB@|$(TLP_FLIB)|g" \ -e "s|@TLP_ULIB@|$(TLP_ULIB)|g" \ -e "s|@TLP_CONFUSR@|$(TLP_CONFUSR)|g" \ -e "s|@TLP_CONFDIR@|$(TLP_CONFDIR)|g" \ -e "s|@TLP_CONFDEF@|$(TLP_CONFDEF)|g" \ -e "s|@TLP_CONF@|$(TLP_CONF)|g" \ -e "s|@TLP_RUN@|$(TLP_RUN)|g" \ -e "s|@TLP_VAR@|$(TLP_VAR)|g" \ -e "s|@TPACPIBAT@|$(TPACPIBAT)|g" INFILES = \ tlp \ tlp-func-base \ tlp-rdw-nm \ tlp-rdw.rules \ tlp-rdw-udev \ tlp-rdw \ tlp-rf \ tlp.rules \ tlp-readconfs \ tlp-run-on \ tlp.service \ tlp-stat \ tlp.upstart \ tlp-usb-udev MANFILES1 = \ bluetooth.1 \ run-on-ac.1 \ run-on-bat.1 \ wifi.1 \ wwan.1 MANFILES8 = \ tlp.8 \ tlp-stat.8 \ tlp.service.8 MANFILESRDW8 = \ tlp-rdw.8 SHFILES = \ tlp.in \ tlp-func-base.in \ func.d/* \ tlp-rdw.in \ tlp-rdw-nm.in \ tlp-rdw-udev.in \ tlp-rf.in \ tlp-run-on.in \ tlp-sleep \ tlp-sleep.elogind \ tlp-stat.in \ tlp-usb-udev.in PLFILES = \ tlp-pcilist \ tlp-readconfs.in \ tlp-usblist # Make targets all: $(INFILES) $(INFILES): %: %.in $(SED) $< > $@ clean: rm -f $(INFILES) install-tlp: all # Package tlp install -D -m 755 tlp $(_SBIN)/tlp install -D -m 755 tlp-rf $(_BIN)/bluetooth ln -sf bluetooth $(_BIN)/wifi ln -sf bluetooth $(_BIN)/wwan install -m 755 tlp-run-on $(_BIN)/run-on-ac ln -sf run-on-ac $(_BIN)/run-on-bat install -m 755 tlp-stat $(_BIN)/ install -D -m 755 --target-directory $(_TLIB)/func.d func.d/* install -m 755 tlp-func-base $(_TLIB)/ install -m 755 tlp-pcilist $(_TLIB)/ install -m 755 tlp-readconfs $(_TLIB)/ install -m 755 tlp-usblist $(_TLIB)/ ifneq ($(TLP_NO_TPACPI),1) install -D -m 755 tpacpi-bat $(_TPACPIBAT) endif install -D -m 755 tlp-usb-udev $(_ULIB)/tlp-usb-udev install -D -m 644 tlp.rules $(_ULIB)/rules.d/85-tlp.rules [ -f $(_CONFUSR) ] || install -D -m 644 tlp.conf $(_CONFUSR) install -d $(_CONFDIR) install -D -m 644 README.d $(_CONFDIR)/README install -D -m 644 00-template.conf $(_CONFDIR)/00-template.conf install -D -m 644 defaults.conf $(_CONFDEF) ifneq ($(TLP_NO_INIT),1) install -D -m 755 tlp.init $(_SYSV)/tlp endif ifneq ($(TLP_WITH_SYSTEMD),0) install -D -m 644 tlp.service $(_SYSD)/tlp.service install -D -m 755 tlp-sleep $(_SDSL)/tlp endif ifneq ($(TLP_WITH_ELOGIND),0) install -D -m 755 tlp-sleep.elogind $(_ELOD)/49-tlp-sleep endif ifneq ($(TLP_NO_BASHCOMP),1) install -D -m 644 tlp.bash_completion $(_SHCPL)/tlp ln -sf tlp $(_SHCPL)/tlp-stat ln -sf tlp $(_SHCPL)/bluetooth ln -sf tlp $(_SHCPL)/wifi ln -sf tlp $(_SHCPL)/wwan endif install -D -m 644 de.linrunner.tlp.metainfo.xml $(_META)/de.linrunner.tlp.metainfo.xml install -d -m 755 $(_VAR) install-rdw: all # Package tlp-rdw install -D -m 755 tlp-rdw $(_BIN)/tlp-rdw install -D -m 644 tlp-rdw.rules $(_ULIB)/rules.d/85-tlp-rdw.rules install -D -m 755 tlp-rdw-udev $(_ULIB)/tlp-rdw-udev install -D -m 755 tlp-rdw-nm $(_NMDSP)/99tlp-rdw-nm ifneq ($(TLP_NO_BASHCOMP),1) install -D -m 644 tlp-rdw.bash_completion $(_SHCPL)/tlp-rdw endif install-man-tlp: # manpages install -d -m 755 $(_MAN)/man1 cd man && install -m 644 $(MANFILES1) $(_MAN)/man1/ install -d -m 755 $(_MAN)/man8 cd man && install -m 644 $(MANFILES8) $(_MAN)/man8/ install-man-rdw: # manpages install -d -m 755 $(_MAN)/man8 cd man-rdw && install -m 644 $(MANFILESRDW8) $(_MAN)/man8/ install: install-tlp install-rdw install-man: install-man-tlp install-man-rdw uninstall-tlp: # Package tlp rm $(_SBIN)/tlp rm $(_BIN)/bluetooth rm $(_BIN)/wifi rm $(_BIN)/wwan rm $(_BIN)/run-on-ac rm $(_BIN)/run-on-bat rm $(_BIN)/tlp-stat rm $(_CONFDIR)/README rm $(_CONFDIR)/00-template.conf rm -r $(_TLIB) rm $(_ULIB)/tlp-usb-udev rm $(_ULIB)/rules.d/85-tlp.rules rm -f $(_SYSV)/tlp rm -f $(_SYSD)/tlp.service rm -f $(_SDSL)/tlp-sleep rm -f $(_ELOD)/49-tlp-sleep rm -f $(_SHCPL)/tlp-stat rm -f $(_SHCPL)/bluetooth rm -f $(_SHCPL)/wifi rm -f $(_SHCPL)/wwan rm -f $(_SHCPL)/tlp rm -f $(_META)/de.linrunner.tlp.metainfo.xml rm -r $(_VAR) uninstall-rdw: # Package tlp-rdw rm $(_BIN)/tlp-rdw rm $(_ULIB)/rules.d/85-tlp-rdw.rules rm $(_ULIB)/tlp-rdw-udev rm $(_NMDSP)/99tlp-rdw-nm rm -f $(_SHCPL)/tlp-rdw uninstall-man-tlp: # manpages cd $(_MAN)/man1 && rm -f $(MANFILES1) cd $(_MAN)/man8 && rm -f $(MANFILES8) uninstall-man-rdw: # manpages cd $(_MAN)/man8 && rm -f $(MANFILESRDW8) uninstall: uninstall-tlp uninstall-rdw uninstall-man: uninstall-man-tlp uninstall-man-rdw checkall: checkbashisms shellcheck perlcritic checkdupconst checkwip checkbashisms: @echo "+++ checkbashisms +++" @checkbashisms $(SHFILES) || true shellcheck: @echo "+++ shellcheck +++" @shellcheck -s dash $(SHFILES) || true perlcritic: @echo "+++ perlcritic +++" @perlcritic --severity 4 --verbose "%F: [%p] %m at line %l, column %c. (Severity: %s)\n" $(PLFILES) || true checkdupconst: @echo "+++ checkdupconst +++" @{ sed -n -r -e 's,^.*readonly\s+([A-Za-z_][A-Za-z_0-9]*)=.*$$,\1,p' $(SHFILES) | sort | uniq -d; } || true checkwip: @echo "+++ checkwip +++" @grep -E -n "### (DEBUG|DEVEL|TODO)" $(SHFILES) $(PLFILES) || true TLP-1.3.1/README.d000066400000000000000000000006211361705037700132650ustar00rootroot00000000000000This directory is intended to contain drop-in customizations for TLP. See full explanation: https://linrunner.de/en/tlp/docs/tlp-configuration.html The naming scheme is 00-name.conf, the files are read in lexical (aphabetical) order. You may also use /etc/tlp.conf directly, which will override any settings in this directory. After making changes, run "tlp start" to activate them without reboot. TLP-1.3.1/README.md000066400000000000000000000057731361705037700134570ustar00rootroot00000000000000# TLP - Linux Advanced Power Management TLP saves laptop battery power on Linux without the need to understand every technical detail. TLP comes with a default configuration already optimized for battery life, so you may just install and forget it. Nevertheless TLP is highly customizable to fulfil your specific requirements. TLP is a pure command line tool with automated background tasks. It does not contain a GUI. ## Features ### Power profiles Depending on the power source (AC or battery) the following settings are applied: - Kernel laptop mode and dirty buffer params - Processor frequency scaling including "turbo boost" / "turbo core" - Limit Intel CPU max/min P-state to control power dissipation (Intel P-state only) - Intel CPU energy/performance policies HWP.EPP (Intel P-state only) and EPB - Disk drive advanced power management level (APM) and spin down timeout - AHCI link power management (ALPM) with device blacklist - AHCI runtime power management for host controllers and disks (EXPERIMENTAL) - PCIe active state power management (PCIe ASPM) - Runtime power management for PCIe bus devices - Intel GPU frequency limits - Radeon graphics power management (KMS and DPM) - Wifi power saving mode - Enable/disable integrated radio devices (excluding connected devices) - Power off optical drive in UltraBay/MediaBay - Audio power saving mode ### Additional - I/O scheduler (per disk) - USB autosuspend with device blacklist/whitelist (input devices excluded automatically) - Enable or disable integrated radio devices upon system startup and shutdown - Restore radio device state on system startup (from previous shutdown) - Radio device wizard: switch radios upon network connect/disconnect and dock/undock - Disable Wake On LAN - Integrated WWAN and bluetooth state is restored after suspend/hibernate - Battery charge thresholds and recalibration - ThinkPads only ## Installation TLP packages are available for all major Linux distributions; see [Installation](https://linrunner.de/en/tlp/docs/tlp-linux-advanced-power-management.html#installation). ## Configuration The default configuration provides optimized power saving out of the box. Settings are stored in `/etc/tlp.conf`; see [Configuration](https://linrunner.de/en/tlp/docs/tlp-configuration.html) for details. ## Documentation Read the the full documentation at the website: - Or take a look at the manpages: - tlp (apply settings) - tlp-rdw (control the radio device wizard) - tlp-stat (display tlp status and active settings) - wifi, bluetooth, wwan (switch wireless devices on/off) - run-on-ac, run-on-bat ## Support Please use adequate Linux forums for help and support questions. ## Bug reports Refer to the [Bug Reporting Howto](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md). ## Contributing Contributing is not only about coding and pull requests. Volunteers helping with testing and support are always welcome! See [Contributing](https://github.com/linrunner/TLP/blob/master/.github/CONTRIBUTING.md). TLP-1.3.1/VERSION000066400000000000000000000000061361705037700132300ustar00rootroot000000000000001.3.1 TLP-1.3.1/changelog000066400000000000000000000761261361705037700140520ustar00rootroot00000000000000+++ 1.3.1 --- 06.02.2020 +++ * Bugfixes Processor: - Issue #460: default CPU_ENERGY_PERF_POLICY_ON_BAT=power too aggressive +++ 1.3.0 --- 31.01.2020 +++ * Features New configuration scheme: - /etc/default/tlp is replaced by /etc/tlp.conf - Settings are read in the following order: 1. Intrinsic defaults 2. /etc/tlp.d/*.conf - Drop-in customization snippets 3. /etc/tlp.conf - User configuration In case of identical parameters, the last occurence has precedence - Parse config files instead of sourcing --> no more shell expansion Battery Features, tlp-stat -b: - Charge thresholds: better checks for command line and configuration; clearer error messages - tlp discharge: error message "check your hardware" when battery wasn't completely discharged (Issue #438) - Distinguish between "no kernel support" for natacpi (< 4.17) and "laptop not supported" (>= 4.17) - Supplement battery status "Unknown" with "threshold may prevent charging" when thresholds are available only General: - systemd: replace tlp-sleep.service with /lib/systemd/system-sleep/tlp Operation Mode AC/BAT: - TLP_PS_IGNORE: power supply class to ignore when determining operation mode; workaround for laptops reporting incorrect AC or battery status (Issue #446) PCI(e) devices: - PCIE_ASPM_ON_AC/BAT: add method 'powersupersave' (Issue #425) Processor: - CPU_ENERGY_PERF_POLICY_ON_AC/BAT: backward compatible merge of settings for Intel energy vs. performance policies EPB (ENERGY_PERF_POLICY_ON_AC/BAT) and HWP.EPP (CPU_HWP_ON_AC/BAT); when HWP.EPP is available, EPB is not set; validate parameters; eliminate external tool x86_energy_perf_policy for kernel >= 5.2 tlp-stat: - Configuration: show file where the parameter comes from - System Info: - Show SELinux status - Show RDW as 'disabled' when TLP is disabled as a whole - Indicate persistent mode - Intel CPU: don't show EPB values when HWP.EPP is active (see above) - PCIe ASPM: show available policies - Undervolting: remove "PHC kernel not available" message * Bugfixes Battery Features: - Issue #415: ThinkPad X240 discharge BAT1 malfunction when BAT0 is not installed Disks: - Fix NVMe detection on Linux 4.15 tlp-stat: - Issue #430: ignore hid device batteries +++ 1.2.2 --- 04.05.2019 +++ * Bugfixes Battery Features, tlp-stat -b: - Show data for batteries named other than BAT0/BAT1 (non-ThinkPads) - Issue #395: ThinkPad X1C6 discharge malfunction - Separate checks for charge thresholds and recalibration - Intelligible recommendation for tp-smapi on ThinkPad X220/T420 et al. - Explain "Battery Features: Charge Thresholds and Recalibrate" Disks: - Fix type determination Operation Mode AC/BAT: - MacBookPro 5.3: workaround for false AC offline status Processor, tlp-stat -p: - Fix read of /sys/devices/system/cpu/cpufreq/boost - x86_energy_perf_policy: detect unsupported CPUs in newer versions Radio Devices: - Issue #404: make RESTORE_DEVICE_STATE_ON_STARTUP=1 persistent again +++ 1.2.1 --- 18.03.2019 +++ * Bugfix - Issue #391: unknown command "chargeonce" +++ 1.2 --- 11.03.2019 +++ * Features Disks: - Support for NVMe devices - Support for removable drives e.g. USB / IEE1394 devices - Improve support for multi queue I/O schedulers (blk-mq) General: - tlp bat/ac: keep manual power settings until tlp start (Issue #349) - Remove all pm-utils scripts (Issue #363) - tlp/tlp-stat: Temporarily overwrite configuration for one program invocation only: -- PARAM=value ... - Document intrinsic defaults in config file (Issue #353) - Code verified with ShellCheck Graphics: - INTEL_GPU_MIN_FREQ_ON_AC/BAT, INTEL_GPU_MAX_FREQ_ON_AC/BAT, INTEL_GPU_BOOST_FREQ_ON_AC/BAT: Intel GPU frequency limits Radio Devices: - tlp-rdw: new command to disable RDW actions temporarily (until reboot) - Support ThinkPad Pro Dock CS18 (17ef:306f) - USB_BLACKLIST_WWAN: disable by default - Retire compatibility with Network Manager 0.9.8 (Ubuntu 14.04 EOL) PCI(e) devices: - RUNTIME_PM_BLACKLIST: add mei_me, pcieport ThinkPad Battery: - New native kernel API for battery features "natacpi" (Issue #321); requires kernel 4.17; enabled by default - NATACPI_ENABLE, TPACPI_ENABLE, TPSMAPI_ENABLE: make all battery feature drivers switchable - tlp discharge/recalibrate: exclude multiple simultaneous invocations - Support ThinkPad 25, *80 (Coffee Lake) and all newer models tlp-stat: - Check systemd-rfkill.socket masked status - Disks: show all configured devices (consider default) - Intel GPU: show frequency limits and available frequencies - Rename "Suggestions" section to "Recommendations" - Remove invocation via 'tlp stat' USB: - Exclude scanners managed by libsane from autosuspend - Remove long deprecated level/autosuspend attributes * Bugfixes - Issue #193: do not try to start NetworkManager (systemd) - Issue #319: get_disk_dev logic is not compatible with NVMe devices - Issue #320: AC mode not detected with USB charger - Issue #331: Process '/usr/bin/tlp auto' failed with exit code 4 - Issue #332: zsh parse error in tlp diskid - Issue #343, #362, #375: circumvent broken AC/charger detection - Issue #344: keep ASPM default to enable deeper C-states on AC - Issue #356: fix writing sequence for start/stop charge thresholds - Issue #368: recognize Think*p*ad P50 - tlp-stat: filter HWP lines from x86_energy_perf_policy output +++ 1.1 --- 24.01.2018 +++ * Features Disks: - SATA_LINKPWR_ON_AC/BAT: try multiple values to support new recommended ALPM policy "med_power_with_dipm" in kernel 4.15 Processor: - Issue #297: ENERGY_PERF_POLICY_ON_AC/BAT: support changed values performance, balance-performance, default, balance-power, power ThinkPad Battery: - Support ThinkPad 13 1st & 2nd Gen, E130; new tpacpi-bat version - tlp-stat --psup: show ASLbase for tpacpi-bat (in device/path) - tlp discharge: show state of battery and force_discharge USB: - USB_BLACKLIST_PRINTER: exclude printers from autosuspend * Bugfixes - Issue #271: intercept link_power_management_policy write error - Issue #283: fix AC power detection for MacBook Pro 2017 - Issue #298: move runtime data from /var/run/tlp to /run/tlp - Issue #301: DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth" not working as expected - Issue #313: don't detect wireless input devices' batteries as power supply +++ 1.0 --- 25.05.2017 +++ * Features General: - TLP_PERSISTENT_DEFAULT: use TLP_DEFAULT_MODE regardless of the actual power source Device Bays: - BAY_POWEROFF_ON_AC: power off optical drive not only on bat (Issue #243) Graphics: - RUNTIME_PM_DRIVER_BLACKLIST: when unconfigured default to "amdgpu nouveau nvidia radeon"; driver default control=auto allows PRIME/Bumblebee to turn the dGPU off and prevents accidential power-on during boot as well - Exclude Nvidia dGPU from runtime power management even when no driver is associated (improve compatibility with Bumblebee) PCI(e) devices - RUNTIME_PM_ALL removed (default to RUNTIME_PM_ALL=1 internally) Processor: - CPU_HWP_ON_AC/BAT: HWP energy performance hints; needs kernel 4.10 and Intel Skylake CPU or newer, intel_pstate only ThinkPad Battery: - RESTORE_THRESHOLDS_ON_BAT: restore charge thresholds on battery - Detect ThinkPad *70 models (Kaby Lake) - Detect ThinkPad 13 (1st and 2nd Gen) as unsupported (Issue #227) - Change texts "ThinkPad (extended) battery functions" to "ThinkPad battery features" - tlp-stat: - Show "Charge total %" when more than one battery is present - Show battery temperature (tp-smapi only) - Show "unsupported" instead of "not installed" for tp-smapi incapable hardware USB: - USB_BLACKLIST_BTUSB: exclude bluetooth devices from autosuspend (Issue #239) - USB_BLACKLIST_PHONE: exclude phone devices from autosuspend (Issue #188) tlp-stat: - -g|--graphics: show graphics card data only - i915: explain enable_psr modes - --psup: show power supply diagnostic - SMART attribute G-Sense_Error_Rate (191) - -v: additional battery voltages - Workqueue power efficient status * Bugfixes - Issue #237: init start: apply powersave regardless of previous state - Issue #256: tlp-stat: intercept non-existing or invalid charge values - tlp-stat: show Intel graphics parameters for Ubuntu's i915_bpo module +++ 0.9 --- 18.08.2016 +++ * Features General: - Block concurrent invocation of tlp Disks: - AHCI runtime power management for host controllers and attached disks - SATA_LINKPWR_BLACKLIST: exclude SATA host devices from power management Radio Devices: - Finally remove deprecated DEVICES_TO_ENABLE_ON_RADIOSW code (works with Ubuntu 12.04/Kernel 3.2 only) - Change WIFI_PWR_ON_AC/BAT default config values to off/on for better usability (1/5 is still supported for backwards compatibility) Radio Device Wizard (tlp-rdw): - Add another ThinkPad OneLink Pro Dock USB LAN (17ef:304) (ThinkPad) Battery: - tlp-stat: - Distinguish incompatible hardware from load errors (Issue #160) - Display battery charge and capacity values in % - More selective suggestions for tp-smapi/acpi-call kernel modules tlp-stat: - Intel graphics: include i915 parameters enable_dc, enable_psr, modeset - --pev: monitor power supply udev events (Issue #172) - Processor: - Display available scaling governors - intel_pstate: display turbo_pct, num_pstates - Storage Devices: - Recognize blk-mq I/O scheduler - SMART attributes Power_Cycle_Count, Unused_Rsvd_Blk_Cnt_Tot, NAND_Writes_1GiB - Suggestions: add *60 models for tp-smapi/acpi-call - System Info: display boot mode UEFI / BIOS - TLP Status: - Display time and mode of tlp's last invocation - Issue warning when systemd-rfkill[@].service is not masked - Wireless: display type in interface list * Bugfixes - Issue #163: handle kernel with built-in acpi_call module properly - Issue #170: battery discharge does not terminate on ThinkPad E-series - Issue #172: TLP does not notice power source change - Issue #175: do not touch wifi on shutdown when unconfigured - Issue #176: optimize rdw locking heuristics - tlp-stat: fix ata error count (filter "DevExch" caused by bay inserts) - tp-smapi: do not load on unsupported ThinkPads - Fix bash completion +++ 0.8 --- 05.08.2015 +++ * Features General: - TLP_DEFAULT_MODE: change default operation mode to AC when no power supply can be detected (concerns some desktop and embedded hardware only) Radio Devices: - Resume: restore bluetooth state (derball2008) Radio Device Wizard (tlp-rdw): - Support ThinkPad OneLink Dock Basic/Pro for dock/undock events - Detect systemd "predictable network interface names" for WWAN ThinkPad Battery: - tpacpi-bat: new upstream version 2.2: get ASL path from /sys/class/power_supply/*/device/path; avoids unnecessary "AE_NOT_FOUND" kernel messages - tlp-stat: - Show "No batteries detected." - Explain battery status "Unknown" as "threshold effective" - Show battery cell group voltages (verbose mode, tp-smapi only) - Show acpi-call suggestion for ThinkPad *40, *50, X1 models USB: - Remove USB_DRIVER_BLACKLIST (because of issues #147, #149, see below) tlp-stat: - Show warnings for ata errors by default * Bugfixes - Issue #123: tlp-stat: detect kernel config with PM_RUNTIME=N - Issue #124: tlp recalibrate: fix exitcode check - Issue #133: USB autosuspend: write power/control if actually changing only - Ignore missing files in /proc/sys/fs/xfs (Gijs Hillenius) - Issues #147, #149: fix udev event processing for AC/BAT switching and USB hotplugging (required for udevd v221 and higher) +++ 0.7 --- 29.01.2015 +++ * Features Processor: - Limit max/min P-state to control power dissipation of the CPU (intel_pstate only) - Set performance versus energy savings policy (x86_energy_perf_policy) USB: - USB_DRIVER_BLACKLIST: exclude devices assigned to the listed drivers from USB autosuspend; default: usbhid - USB_BLACKLIST_WWAN: match internal driver list instead of USB vendor ids - USB_WHITELIST: include devices into USB autosuspend even if already excluded by the driver or WWAN blacklists Radio devices: - DEVICES_TO_ENABLE_ON_AC/DISABLE_ON_BAT/DISABLE_ON_BAT_NOT_IN_USE: enable/disable radio devices upon change of power source (excluding connected devices) ThinkPad Battery: - Issue #105: provide proper return codes for all battery functions - Issue #106: setcharge allowed on battery power - Do not activate new thresholds with force_discharge anymore (tp-smapi) - tpacpi-bat: support for ThinkPad E325 charge thresholds Misc: - RUNTIME_PM_DRIVER_BLACKLIST: exclude PCI(e) devices assigned to drivers; default: radeon, nouveau (solves issue #94) - Support Dell Latitude docks tlp-stat: - New options -d|--disk, -e|--pcie, -p|--processor, -s|--system, -u|--usb - Show driver and connection state for all enabled radio devices - Show driver name in Runtime PM PCI(e) device list - Show type and state of docks and device bays - Show type of init system - Check if TLP's systemd services are enabled (zenox) * Bugfixes Udev: - Run change of power source in a detached subshell to avoid blocking udev - Fix dock/undock event processing for ThinkPad Adv Mini Dock and Ultrabase - Make USB device event processing more robust - Run in a detached subshell to avoid blocking udev - Wait 2s for subdevices to populate for proper black/whitelist detection Misc: - Support for NM 0.9.10 (Debian Jessie) - Issue #109: tlp-stat: report "unknown" power source when no AC detected - Issue #98: do not change autosuspend_delay_ms, keep kernel default settings (garyp) - Exclude pseudo usb disks (Raphaël Halimi) * Misc - Add AUTHORS file +++ 0.6 --- 06.10.2014 +++ * Features - Set systemd service type to simple, allows tlp service to start asynchronously in the background (Timofey) - Remove DISABLE_TPACPIBAT from configuration - Remove DEVICES_TO_ENABLE_ON_RADIOSW from configuration because it's deprecated: works with Ubuntu 12.04/Kernel 3.2 only - Enable RUNTIME_PM_ALL by default - Do not touch kernel settings if param is empty or commented: DISK_IDLE_SECS_ON, MAX_LOST_WORK_SECS_ON, SCHED_POWERSAVE_ON, NMI_WATCHDOG, SATA_LINKPWR_ON, PCIE_ASPM_ON, RADEON_DPM_STATE_ON, RADEON_POWER_PROFILE_ON, WIFI_PWR_ON, SOUND_POWER_SAVE_ON, RUNTIME_PM_ON. - DISK_APM_LEVEL_ON, DISK_SPINDOWN_TIMEOUT_ON, DISK_IOSCHED: use _ or keep to skip the setting for a particular disk - tlp-stat - Consider changed sysfs paths for i915 enable_rc6/fbc as of kernel 3.15 (M@C) - Consider changed sysfs paths for hwmon coretemp/sensors as of kernel 3.15/3.16 - Report speed of all fans, not just the first one - Show warning for kernel sata errors (possibly) caused by SATA_LINKPWR_ON_BAT/AC=min/medium power - Retrieve trace output from systemd journal if present - Do not disable TLP when laptop-mode-tools is detected, just output a warning about possible conflicts * Bugfixes - Issue #74: Makefile: remove tlp-nop in uninstall-tlp target (beatinho, peterkenji94) - Issue #86: tlp-stat: don't suggest tp-smapi on non-ThinkPad hardware with thinkpad_acpi loaded (sayantandas) - tlp-stat: do not show /proc/acpi/ibm/fan on Lenovo non-ThinkPad models (Qasim) +++ 0.5 --- 24.03.2014 +++ * Features - tpacpi-bat: auto detection of all ThinkPad models (v2.1) - tlp-stat: include newer models in tpacpi-bat suggestions - tlp-rdw: support newer docks - Handle special case where BAT1 = main battery (Thinkpad Edge/L/S series) - Issue #61: sound power save depending on power source ac/bat - Issue #62: don't touch devices in RUNTIME_PM_BLACKLIST or excluded by RUNTIME_PM_ALL=0 * Bugfixes - run-on-ac/bat: check if command exists - Issue #59: do not write sata link power when not configured - Fix RESTORE_DEVICE_STATE_ON_STARTUP (fabio) - Restore bay power state upon resume only when on bat power and the setting is active (xudabit) - Use nmcli before rfkill to change radio state; re-enable wifi on shutdown when not explicitly configured (Ubuntu 14.04) * Packaging - Create symlinks instead of hardlinks for bluetooth/wifi/wwan, run-on-ac/bat - Makefile: new params TLP_* - tlp.init: remove requirement $all +++ 0.4.1 --- 02.01.2014 +++ * Bugfix version (_not_ for Ubuntu/Debian) * Features - tpacpi-bat: support ThinkPad E431 * Bugfixes - Bug #43: tlp-rdw not working with NM 0.9.9/Fedora 20 (wofritz) - Bug #44: run-on-ac|bat: remove dependency on pm-utils/on_ac_power +++ 0.4 --- 17.09.2013 +++ * Features - New radeon dynamic power management (dpm); needs Kernel >= 3.11 (Pali Rohár) - RUNTIME_PM_BLACKLIST: exclude listed pci(e) device addresses from Runtime PM (wofritz) - USB_BLACKLIST_WWAN: exclude wwan devices from usb autosuspend; works for ids 05c6:* 0bdb:* 1199:* only - Apply ac settings for faster shutdown +++ 0.3.11 --- 10.09.2013 +++ *** Testing version (for Arch Linux) *** * Bugfixes - Issue #42: - Remove dependency to on_ac_power (part of pm-utils in Arch Linux) - Fix udev rule to detect power source change ac - bat * Packaging - Pull request #40: systemd: start tlp.service after local-fs.target instead of graphical.target (cprussin) +++ 0.3.10 --- 17.08.2013 +++ *** Testing version (for Arch Linux) *** * Architecture - Issue #36: detect change of power source via udev instead of being called by pm-powersave - Handle suspend/resume w/o pm-utils in systemd environments: - Encapsulate suspend/resume tasks as a tlp subcommand - Add tlp-sleep.service to call tlp suspend/resume - Remove 48tlp-rdw.lock because it doesn't work as expected * Features - Issue #31: detect and use intel_pstate driver to control turbo mode (ValdikSS) - Disable wol for all ethernet devices i.e. non-eth0 (blafoo) - tpacpi-bat: - merge upstream support for ThinkPad T430u, Edge E335/E530 * Bugfixes - Issue #28: do not touch dirty_(background_)ratio anymore, i.e. revert setting to kernel defaults * Packaging - debian/control: remove ${shlibs:Depends} +++ 0.3.9 --- 02.05.2013 +++ * Features - tpacpi-bat: - merge upstream w/ support for ThinkPad (Edge) S430 - add support for ThinkPad L530 - tlp-stat: - Subtract offset 128 from threshold values on ThinkPad Edge S430 - Show /sys/class/power_supply/BATx/cycle_count = 0 as "(unsupported)" * Bugfixes - Issue #14: tlp recalibrate fails when /bin/sh -> bash (slyon) - Bug #42: X121e battery functions not working (Jlp) - Set more reasonable values for dirty_ratio/dirty_background_ratio - Reverse order of writing the thresholds upon system start to stop - start, to achieve a consistent tlp-stat output between tlp init/start and tlp setcharge on quirky Edge and L series. - tlp-stat: - Fix threshold output trailing empty line +++ 0.3.8.1-3 --- 07.04.2013 +++ * Packaging - Fix #41: postinst/postrm fails without acpid (Petit Carlin) - recommends: acpid - postinst/rm: ignore missing acpid +++ 0.3.8.1 --- 29.03.2013 *** * Packaging - Remove obsolete desktop autostart hook - New format for debian/copyright - Add dummy case construct to tlp.init to make lintian happy - Rename tlp-init.service to tlp.service - postinst/postrm: restart acpid for thinkpad-radiosw event - Move smartmontools to "recommends:" * Features - New options CPU_BOOST_ON_* for cpu turbo boost (Linux 3.7 or later) - New option DEVICES_TO_ENABLE_ON_RADIOSW to enable only selected radios when wireless radio switch is turned on (Ubuntu + ThinkPad only) - [EXPERIMENTAL] New option RUNTIME_PM_ALL to activate runtime pm for all PCI(e) devices - tpacpi-bat: new upstream version (25.03.2013, commit dd5a682) - add support for X121e, L430, E420s, S420 - tlp chargeonce: charge battery to upper threshold once - tlp discharge: show current power consumption - tlp-stat: - Nicer output, code refactored - Remove dmidecode – get DMI data from /sys/class/dmi/id/ - When ASPM policy is not writable, show "using bios prefs" - Show interpretation for i915 params - Show disk status - Show tp-smapi, tpacpi-bat availability and status - Show cpu model - Resolve all pci device classes (new subcommand tlp-pcilist) - Show suggestions to install missing kernel modules/tools - Use iw for wifi power save if available, iwconfig is considered deprecated - Remove obsolete tlp wifi subcommand - Remove 2s delay in applying settings upon change of the power source * Bugfixes - tlp-stat: - Exclude usb media from "Storage Devices" section - Fix display of data in /sys/class/power_supply/BAT?/ +++ 0.3.7.1 --- 17.08.2012 +++ * Bug fixes - #39: tlp-stat: /sys/devices/platform/coretemp.0/temp1_input does not exist (Laurent Bonnaud) +++ 0.3.7 --- 13.08.2012 +++ * Packaging - implement startup/shutdown code as a command: tlp init - systemd support: tlp-init.service - deb recommends: tlp-rdw; suggests: acpi-call, tp-smapi * Features - Battery charge thresholds for Sandy Bridge and later models (X220/T420/...) by means of tpacpi-bat - Use tpacpi-bat even when tp-smapi is not available; for Ivy Bridge models (X230/T430/...) - DEVICES_TO_ENABLE_ON_SHUTDOWN to prevent other operating systems from missing radios - DEVICES_TO_ENABLE_ON_STARTUP - tlp-stat: - show TRIM capabilty for SSDs - add SMART attributes (179, 241) - new cmdline options -r, -t, -T - show cpu temp, fan speed even if /proc/acpi/ibm/{thermal|fan} are not available - show tp_smapi/power_avg * Bug fixes - #34: system start hangs in Fedora 17 (DigitalFlow) - #35: shutdown results in reboot; new config param USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN (Thubo) - #38: wifi on/off not working with ipw2100/2200 (kristatos) +++ 0.3.6-2 --- 24.03.2012 +++ * Packaging: - Fix tlp.postinst for systems without upstart dir /etc/init/ +++ 0.3.6 --- 22.03.2014 +++ * Features - handle usb autosuspend via udev events - usb hid detection overhauled (based on subdev/bInterfaceClass) - Restrict runtime pm to a safe subclass of pci devices (from Ubuntu Precise's implementation of pm-utils) - Restore radio device state on system startup (from previous shutdown) - Radio device wizard: switch radios upon network connect/disconnect and dock/undock events (samba) - Set cpu scaling governor and mix/max frequencies (Alex Lochmann) - tlp-stat: add smart attributes for samsung ssd - tlp-stat: show settings * Packaging - postinst/postrm: - disable power.d/harddrive, pci_devices, readahead, usb_bluetooth (Package pm-utils, Ubuntu 12.04) - disable conflicting upstart jobs (Package rfkill, Ubuntu 12.04) - split package - tlp: power save functions - tlp-rdw: radio device wizard (depends on network manager) * Bug Fixes - tlp-usblist: cleanup code, add pragmas "strict" and "warnings" (dywisor) - Remove setting of ext3/4 fs commit timeout (see LP #900923) +++ 0.3.5 --- 19.12.2011 +++ * Features - tlp recalibrate = fullcharge + discharge - tlp-stat: show thinkpad fan speed, battery model, power_now, i915: powersave, lvds_downclock - tlp-stat: usb output refactored, new subcommand tlp-usblist - tlp-stat: show kernel cmdline - added non-rfkill device ipw2100 (kristatos) * Bug Fixes - #27: tlp-stat complains about missing /proc/acpi/ibm/thermal and start_charge_thresh on X220/T420(s) et al. (Esc) - Check if start_charge_thresh, stop_charge_thresh, force_discharge are writable - #28: further mitigate race with gdm when disabling radios in init script (blackbox) - #29: tlp-stat: remove smartctl garbage output (SirVival) - #30: suppress dmidecode error output (kristatos) - iterate over all sched_powersave instances - i915: rc6/fbc features removed - Start upowerd in init script - #32: show error message suggesting to uninstall latop-mode-tools if present (Kuzoku) +++ 0.3.4 --- 05.12.2014 +++ * Features - Intel graphics: rc6 power save mode, frame buffer compression +++ 0.3.3 --- 19.09.2011 +++ * Features - tlp-stat: show hdd temp SMART values (bassplayer) - enable/disable kernel NMI watchdog * Other changes - set_charge_thresholds(): check for undefined thresh values - set_extfs_commit(): skip bind mounts (Fedora sandbox) - zztlp: check param; show help text * Bug Fixes - #24: openSUSE 11.4/2.6.37: writing to autosuspend_delay_ms fails, fallback to autosuspend - #25: fix sched_mc_power_savings on bat - #26: tlp-stat complains about missing dmidecode (Sara) +++ 0.3.2-2 --- 11.07.2011 +++ * Bug Fix - #23: init.d script not linked/unlinked by install/purge (LePatron) +++ 0.3.2 --- 04.07.2011 +++ * Bug Fix - #22: runtime pm causes shutdown to fail, reboots instead (fabio) disabled by default +++ 0.3.1 --- 23.06.2011 +++ * Changes to ease porting to other distros - removed system utils absolute paths - added PATH debug output in tlp, tlp.init/tlp.upstart - manpages moved from debian/ to man/ * Features, other changes - runtime pm (ccyx) - set/disable hard disk spindown timeout (enrico65, hausmarke86) - use power/autosuspend_delay_ms (kernel >= 2.6.38) - tlp-stat: now runs with root privilege only, show intel ssd specific smart values, check for pcie aspm disabled by kernel - bluetooth/wifi/wwan: when using rfkill, check for root privilege or /dev/rfkill user-writable - tlp/bluetooth/wifi/wwan: bash completion * Bug Fixes - #18: tlp start (ac): incorrect ouptut "started in bat mode" fixed (yatpu) - #19: tlp-stat: incorrect wifi power mgmt detection for wl driver (DrPaulaner) - #20: handle disabled pcie aspm in kernel 2.6.39 gracefully (Schmitti, g3eB4Y) - #21: battery attributes /sys/class/power_supply/BAT?/charge_* not recognized (tanjapetri) +++ 0.3.0-2 --- 20.03.2011 +++ * Bug Fixes - DEVICES_TO_DISABLE_ON_STARTUP (Debian): startup code fixed; SysV-script depends on $syslog now *** 0.3.0 --- 18.03.2011 *** * Bug Fixes - Switch wwan off before suspend (workaround for kernel/network-manager quirk) * Features - Specify DISK_DEVICES with id's from /dev/disk/by-id (egalus) - tlp diskid: show disk id's - DISK_IOSCHED: set i/o scheduler (egalus) - PCIe ASPM - Do not set START_CHARGE_THRESH on tp_smapi-capable ThinkPad Edge - SCHED_POWERSAVE: cpu/sched_*_power_savings - Set radeon clock speed via /sys/class/drm/card*/device/power_profile * Packaging - Move startup code from upstart back to init.d - Move symlinking in /etc/pm/power.d/ to postinst/postrm - Move /usr/lib/tlp/ to /usr/lib/tlp-pm/ *** 0.2.8 --- 25.09.2010 *** * Features - USB_AUTOSUSPEND: exclude input devices (usbhid) w/o blacklist - tlp-stat: indicate drivers in usb device list - DISK_APM_LEVEL: support multiple devices (Stifflers_mom) - maverick: override pm-utils power.d/ scripts with own functionality *** 0.2.7 --- 11.09.2010 *** * Bug fixes - usb autosuspend/tlp-stat not showing all usb devices - #15: tlp-stat abort w/ ipw2200 (agape) - #16: PHC_CONTROL written to all cpus/cores (pumpe et al.) * Features - charge thresholds: new command tlp setcharge (crishu) - DEVICES_TO_DISABLE_ON_STARTUP: handle bluetooth in upstart job (previously via desktop login) - set usb autosuspend for wwan devices on ifup *** 0.2.6 --- 17.07.2010 *** * Bugfixes - tlp-stat: error checking get_ctrl_device, tlp-stat batinfo (mikar) - #14: delayed login window (greeter) w/ USB_AUTOSUSPEND=1 (steveurkel, fishmac, saubaer) * Features - tlp fullcharge - set_charge_thresholds on startup only, not on shutdown - ext3/ext4 fs commit depending on MAX_LOST_WORK_SECS - tlp-stat: check wifi power mgmt capability - tlp-stat: display wifi driver *** 0.2.5-2 --- 17.05.2010 *** * Bugfix/Package change - Conflicts: pm-utils-powersave-policy - powersave-policy-sata-link-power breaks pm-powersave w/ sata controllers in compatible mode an pata controllers (LP# 514881). - TLP implements same functionality as conflicting package anyway ... *** 0.2.5 --- 03.05.2010 *** * Bugfixes - #11: excessive boottime (+40s) w/ USB_SUSPEND=1 & USB_BLACKLIST="" - tlp-stat: display hard disk w/o apm as "none/disabled" * Features - bluetooth/wifi/wwan: toggle (#12, thatgui) - changed usb autosuspend default: on - wifi power management re-enabled on 2.6.32 w/ some adapters - trace feature, output to syslog/debug (TLP_DEBUG) - new variable BAY_DEVICE *** 0.2.4 --- 10.03.2010 *** * Bugfixes - #8: tlp-rf-func warnings on ThinkPad w/o bluetooth and wwan (woelffchen) - #9: bayoff: ultrabay power on again after resume (linrunner) -> script sleep.d/49bay added * Features - tlp: force battery discharge - run-on-ac/run-on-bat *** 0.2.3 --- 07.03.2010 *** * Bugfixes - #7: bayoff - media not unmounted, drives != sr0 not recognized (linrunner) *** 0.2.2 --- 04.03.2010 *** * Bugfixes - #3: cannot re-enable bluetooth after disabling (M@C) - #5: autoload tp_smapi (Starko) * Features - upstart integration - tlp-stat: error checking improved - poweroff ultrabay optical drive on battery - support for ipw2200 radio enable/disable (karlitos) *** 0.2.1 --- 31.01.2010 *** * Bugfixes - #1: pm-suspend/pm-hibernate hang w/o wwan device (Zaphod_42) - #2: error messages from set_sata_link_power() w/o sata-ahci or ide (quarf) * tlp-stat: more info *** 0.2.0 --- 30.01.2010 *** * Initial public release TLP-1.3.1/de.linrunner.tlp.metainfo.xml000066400000000000000000000032131361705037700177070ustar00rootroot00000000000000 de.linrunner.tlp MIT TLP Save battery power on laptops

TLP is an advanced power management tool for Linux. It comes with a default configuration already optimized for battery life. At the same time it is highly customizable to fulfil specific user requirements.

TLP supplies separate settings profiles for AC and battery power and can enable or disable bluetooth, WiFi and WWAN radio devices upon system startup.

For ThinkPads it provides a unified way to configure charging thresholds and recalibrate the battery for all models which support it (via tp-smapi or acpi-call).

TLP is a pure command line tool with automated background tasks, it does not contain a GUI.

System GPL-2.0+ Thomas Koch https://linrunner.de/tlp dmi:*:ct8:* dmi:*:ct9:* dmi:*:ct10:* acpi:PNP0C0A:*
TLP-1.3.1/defaults.conf000066400000000000000000000027031361705037700146440ustar00rootroot00000000000000# /usr/share/tlp/defaults.conf - TLP intrinsic defaults # IMPORTANT: do not edit this file, put your settings in /etc/tlp.conf or # /etc/tlp.d/*.conf instead! TLP_ENABLE=1 TLP_PERSISTENT_DEFAULT=0 DISK_IDLE_SECS_ON_AC=0 DISK_IDLE_SECS_ON_BAT=2 MAX_LOST_WORK_SECS_ON_AC=15 MAX_LOST_WORK_SECS_ON_BAT=60 CPU_ENERGY_PERF_POLICY_ON_AC=balance_performance CPU_ENERGY_PERF_POLICY_ON_BAT=balance_power SCHED_POWERSAVE_ON_AC=0 SCHED_POWERSAVE_ON_BAT=1 NMI_WATCHDOG=0 DISK_DEVICES="nvme0n1 sda" DISK_APM_LEVEL_ON_AC="254 254" DISK_APM_LEVEL_ON_BAT="128 128" DISK_IOSCHED="keep keep" SATA_LINKPWR_ON_AC="med_power_with_dipm max_performance" SATA_LINKPWR_ON_BAT="med_power_with_dipm min_power" AHCI_RUNTIME_PM_TIMEOUT=15 PCIE_ASPM_ON_AC=default PCIE_ASPM_ON_BAT=default RADEON_POWER_PROFILE_ON_AC=default RADEON_POWER_PROFILE_ON_BAT=default RADEON_DPM_PERF_LEVEL_ON_AC=auto RADEON_DPM_PERF_LEVEL_ON_BAT=auto WIFI_PWR_ON_AC=off WIFI_PWR_ON_BAT=on WOL_DISABLE=Y SOUND_POWER_SAVE_ON_AC=0 SOUND_POWER_SAVE_ON_BAT=1 SOUND_POWER_SAVE_CONTROLLER=Y BAY_POWEROFF_ON_AC=0 BAY_POWEROFF_ON_BAT=0 BAY_DEVICE="sr0" RUNTIME_PM_ON_AC=on RUNTIME_PM_ON_BAT=auto RUNTIME_PM_DRIVER_BLACKLIST="amdgpu mei_me nouveau nvidia pcieport radeon" USB_AUTOSUSPEND=1 USB_BLACKLIST_BTUSB=0 USB_BLACKLIST_PHONE=0 USB_BLACKLIST_PRINTER=1 USB_BLACKLIST_WWAN=0 USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN=0 RESTORE_DEVICE_STATE_ON_STARTUP=0 RESTORE_THRESHOLDS_ON_BAT=0 NATACPI_ENABLE=1 TPACPI_ENABLE=1 TPSMAPI_ENABLE=1 TLP-1.3.1/func.d/000077500000000000000000000000001361705037700133415ustar00rootroot00000000000000TLP-1.3.1/func.d/05-tlp-func-pm000066400000000000000000000156731361705037700156640ustar00rootroot00000000000000#!/bin/sh # tlp-func-pm - Device Power Management Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly ETHTOOL=ethtool readonly PCID=/sys/bus/pci/devices readonly PCIDRV=/sys/bus/pci/drivers # ---------------------------------------------------------------------------- # Functions # --- PCI(e) Devices set_runtime_pm () { # set runtime power management # $1: 0=ac mode, 1=battery mode local address class ccontrol control device driver drv_bl local pci_bl_adr pci_bl_drv type vendor if [ "$1" = "1" ]; then ccontrol=${RUNTIME_PM_ON_BAT:-} else ccontrol=${RUNTIME_PM_ON_AC:-} fi if [ -z "$ccontrol" ]; then # do nothing if unconfigured echo_debug "pm" "set_runtime_pm($1).not_configured" return 0 fi # driver specific blacklist: # - undefined = use intrinsic default from /usr/share/tlp/defaults.conf # - empty = disable feature drv_bl="$RUNTIME_PM_DRIVER_BLACKLIST" # pci address blacklisting pci_bl_adr=${RUNTIME_PM_BLACKLIST:-} # pci driver blacklisting: corresponding pci addresses pci_bl_drv="" # cumulate pci addresses for devices with blacklisted drivers for driver in $drv_bl; do # iterate list if [ -n "$driver" ] && [ -d $PCIDRV/$driver ]; then # driver is active --> iterate over assigned devices for device in $PCIDRV/$driver/0000:*; do # get short device address address=${device##/*/0000:} # add to list when not already contained if ! wordinlist "$address" "$pci_bl_drv"; then pci_bl_drv="$pci_bl_drv $address" fi done # for device fi # if driver done # for driver # iterate pci(e) devices for type in $PCID; do for device in $type/*; do if [ -f $device/power/control ]; then # get short device address, class address=${device##/*/0000:} class=$(read_sysf $device/class) if wordinlist "$address" "$pci_bl_adr"; then # device is in address blacklist echo_debug "pm" "set_runtime_pm($1).bloack_address: $device [$class]" elif wordinlist "$address" "$pci_bl_drv"; then # device is in driver blacklist echo_debug "pm" "set_runtime_pm($1).black_driver: $device [$class]" else control=$ccontrol # check for Nvidia gpu's if wordinlist "nouveau" "$drv_bl" || wordinlist "nvidia" "$drv_bl"; then # driver nouveau or nvidia is in blacklist # --> blacklist depending on vendor and class vendor=$(read_sysf $device/vendor) if [ "$vendor" = "0x10de" ]; then # vendor nvidia # --> check for display or 3d controller class case $class in "0x030000"|"0x030200") control="black_nvgpu" ;; esac fi fi # if nouveau | nvidia blacklisted case $control in auto|on) write_sysf $control $device/power/control echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]; rc=$?" ;; *) # black_* --> do nothing echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]" ;; esac fi # if blacklist fi # if control done # for device done # for type return 0 } set_pcie_aspm () { # set pcie active state power management # $1: 0=ac mode, 1=battery mode local pwr if [ "$1" = "1" ]; then pwr=${PCIE_ASPM_ON_BAT:-} else pwr=${PCIE_ASPM_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_pcie_aspm($1).not_configured" return 0 fi if [ -f /sys/module/pcie_aspm/parameters/policy ]; then if write_sysf "$pwr" /sys/module/pcie_aspm/parameters/policy; then echo_debug "pm" "set_pcie_aspm($1): $pwr" else echo_debug "pm" "set_pcie_aspm($1).disabled_by_kernel" fi else echo_debug "pm" "set_pcie_aspm($1).not_available" fi return 0 } # -- Audio Devices set_sound_power_mode () { # set sound chip power modes # $1: 0=ac mode, 1=battery mode local pwr cpwr # new config param if [ "$1" = "1" ]; then pwr=${SOUND_POWER_SAVE_ON_BAT:-} else pwr=${SOUND_POWER_SAVE_ON_AC:-} fi # when unconfigured consider legacy config param [ -z "$pwr" ] && pwr=${SOUND_POWER_SAVE:-} if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_sound_power_mode($1).not_configured" return 0 fi cpwr="$SOUND_POWER_SAVE_CONTROLLER" check_sysfs "set_sound_power_mode" "/sys/module" if [ -d /sys/module/snd_hda_intel ]; then write_sysf "$pwr" /sys/module/snd_hda_intel/parameters/power_save echo_debug "pm" "set_sound_power_mode($1).hda: $pwr; rc=$?" if [ "$pwr" = "0" ]; then write_sysf "N" /sys/module/snd_hda_intel/parameters/power_save_controller echo_debug "pm" "set_sound_power_mode($1).hda_controller: N controller=$cpwr; rc=$?" else write_sysf "$cpwr" /sys/module/snd_hda_intel/parameters/power_save_controller echo_debug "pm" "set_sound_power_mode($1).hda_controller: $cpwr; rc=$?" fi fi if [ -d /sys/module/snd_ac97_codec ]; then write_sysf "$pwr" /sys/module/snd_ac97_codec/parameters/power_save echo_debug "pm" "set_sound_power_mode($1).ac97: $pwr; rc=$?" fi return 0 } # --- LAN Devices get_ethifaces () { # get all eth devices -- retval: $_ethifaces local ei eic _ethifaces="" for eic in $NETD/*/device/class; do if [ "$(read_sysf $eic)" = "0x020000" ] \ && [ ! -d "${eic%/class}/ieee80211" ]; then ei=${eic%/device/class}; ei=${ei##*/} _ethifaces="$_ethifaces $ei" fi done _ethifaces="${_ethifaces# }" return 0 } disable_wake_on_lan () { # disable WOL local ei if [ "$WOL_DISABLE" = "Y" ]; then get_ethifaces for ei in $_ethifaces; do $ETHTOOL -s "$ei" wol d > /dev/null 2>&1 echo_debug "pm" "disable_wake_on_lan: $ei; rc=$?" done else echo_debug "pm" "disable_wake_on_lan.not_configured" fi return 0 } TLP-1.3.1/func.d/10-tlp-func-cpu000066400000000000000000000331041361705037700160200ustar00rootroot00000000000000#!/bin/sh # tlp-func-cpu - Processor Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly CPU_BOOST_ALL_CTRL=/sys/devices/system/cpu/cpufreq/boost readonly INTEL_PSTATED=/sys/devices/system/cpu/intel_pstate readonly CPU_MIN_PERF_PCT=$INTEL_PSTATED/min_perf_pct readonly CPU_MAX_PERF_PCT=$INTEL_PSTATED/max_perf_pct readonly CPU_TURBO_PSTATE=$INTEL_PSTATED/no_turbo readonly CPU_INFO=/proc/cpuinfo readonly ENERGYPERF=x86_energy_perf_policy # ---------------------------------------------------------------------------- # Functions # --- Scaling Governor check_intel_pstate () { # detect intel_pstate driver -- rc: 0=present/1=absent # Note: intel_pstate requires Linux 3.9 or higher [ -d $INTEL_PSTATED ] } set_cpu_scaling_governor () { # set CPU scaling governor -- $1: 0=ac mode, 1=battery mode local gov cpu ec if [ "$1" = "1" ]; then gov=${CPU_SCALING_GOVERNOR_ON_BAT:-} else gov=${CPU_SCALING_GOVERNOR_ON_AC:-} fi if [ -n "$gov" ]; then ec=0 for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do if [ -f "$cpu" ] && ! write_sysf "$gov" $cpu; then echo_debug "pm" "set_cpu_scaling_governor($1).write_error: $cpu $gov; rc=$?" ec=$((ec+1)) fi done echo_debug "pm" "set_cpu_scaling_governor($1): $gov; ec=$?" else echo_debug "pm" "set_cpu_scaling_governor($1).not_configured" fi return 0 } set_cpu_scaling_min_max_freq () { # set CPU scaling limits -- $1: 0=ac mode, 1=battery mode local minfreq maxfreq cpu ec local conf=0 if [ "$1" = "1" ]; then minfreq=${CPU_SCALING_MIN_FREQ_ON_BAT:-} maxfreq=${CPU_SCALING_MAX_FREQ_ON_BAT:-} else minfreq=${CPU_SCALING_MIN_FREQ_ON_AC:-} maxfreq=${CPU_SCALING_MAX_FREQ_ON_AC:-} fi if [ -n "$minfreq" ] && [ "$minfreq" != "0" ]; then conf=1 ec=0 for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do if [ -f "$cpu" ] && ! write_sysf "$minfreq" $cpu; then echo_debug "pm" "set_cpu_scaling_min_max_freq($1).min.write_error: $cpu $minfreq; rc=$?" ec=$((ec+1)) fi done echo_debug "pm" "set_cpu_scaling_min_max_freq($1).min: $minfreq; ec=$ec" fi if [ -n "$maxfreq" ] && [ "$maxfreq" != "0" ]; then conf=1 ec=0 for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do if [ -f "$cpu" ] && ! write_sysf "$maxfreq" $cpu; then echo_debug "pm" "set_cpu_scaling_min_max_freq($1).max.write_error: $cpu $maxfreq; rc=$?" ec=$((ec+1)) fi echo_debug "pm" "set_cpu_scaling_min_max_freq($1).max: $maxfreq; ec=$ec" done fi [ $conf -eq 1 ] || echo_debug "pm" "set_cpu_scaling_min_max_freq($1).not_configured" return 0 } # --- Performance Policies supports_intel_cpu_epb () { # rc: 0=CPU supports EPB/1=false grep -E -q -m 1 '^flags.+epb' $CPU_INFO } supports_intel_cpu_epp () { # rc: 0=CPU supports HWP.EPP/1=false grep -E -q -m 1 '^flags.+hwp_epp' $CPU_INFO } set_intel_cpu_perf_policy () { # set Intel CPU energy vs. performance policies # $1: 0=ac mode, 1=battery mode # # depending on the CPU model the values # performance, balance_performance, default, balance_power, power # in CPU_ENERGY_PERF_POLICY_ON_AC/BAT are applied to: # (1) energy-performance preference (HWP.EPP) in MSR_IA32_HWP_REQUEST # (2) energy-performance bias (EPB) in MSR_IA32_ENERGY_PERF_BIAS # when HWP.EPP is available, EPB is not set (unless forced) # # full backwards compatibility for the replaced parameters: # (1) CPU_HWP_ON_AC/BAT: no translation needed # (2) ENERGY_PERF_POLICY_ON_AC/BAT: translate # balance-performance --> balance_perfomance # normal --> default # balance-power --> balance_power # # References: # - https://www.kernel.org/doc/html/latest/admin-guide/pm/intel_pstate.html#energy-vs-performance-hints # - https://www.kernel.org/doc/html/latest/admin-guide/pm/intel_epb.html # - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4beec1d7519691b4b6c6b764e75b4e694a09c5f7 # - http://manpages.ubuntu.com/manpages/disco/man8/x86_energy_perf_policy.8.html local cnt cpuf ec perf pnum if [ "$1" = "1" ]; then perf=${CPU_ENERGY_PERF_POLICY_ON_BAT:-${ENERGY_PERF_BIAS_ON_BAT:-${CPU_HWP_ON_BAT:-}}} else perf=${CPU_ENERGY_PERF_POLICY_ON_AC:-${ENERGY_PERF_BIAS_ON_AC:-${CPU_HWP_ON_AC:-}}} fi # translate alphanumeric values from EPB to HWP.EPP syntax case "$perf" in 'balance-performance') perf='balance_performance' ;; 'normal') perf='default' ;; 'balance-power') perf='balance_power' ;; 'powersave') perf='power' ;; esac if [ -z "$perf" ]; then echo_debug "pm" "set_intel_cpu_perf_policy($1).not_configured" return 0 fi if check_intel_pstate && supports_intel_cpu_epp; then # validate HWP.EPP setting case "$perf" in performance|balance_performance|default|balance_power|power) # OK --> continue ;; *) # invalid setting echo_debug "pm" "set_intel_cpu_perf_policy($1).hwp_epp.invalid: perf=$perf" return 0 ;; esac # apply HWP.EPP setting cnt=0 ec=0 for cpuf in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do if [ -f $cpuf ]; then cnt=$((cnt+1)) if ! write_sysf "$perf" $cpuf; then echo_debug "pm" "set_intel_cpu_perf_policy($1).hwp_epp.write_error: $perf $cpuf; rc=$?" ec=$((ec+1)) fi fi done if [ $cnt -gt 0 ]; then echo_debug "pm" "set_intel_cpu_perf_policy($1).hwp_epp: $perf; ec=$ec" # HWP.EPP active and applied, quit unless EPB is forced [ "$X_FORCE_EPB" = "1" ] || return 0 else echo_debug "pm" "set_intel_cpu_perf_policy($1).hwp_epp.not_available" fi else echo_debug "pm" "set_intel_cpu_perf_policy($1).hwp_epp.unsupported_cpu" fi if supports_intel_cpu_epb; then # translate HWP.EPP alphanumeric to EPB numeric values for native kernel support # and x86_energy_perf_policy backwards compatibility; validate numeric values case "$perf" in performance) pnum=0 ;; balance_performance) pnum=4 ;; default) pnum=6 ;; balance_power) pnum=8 ;; power) pnum=15 ;; 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15) pnum="$perf" ;; *) # invalid setting echo_debug "pm" "set_intel_cpu_perf_policy($1).epb.invalid: perf=$perf" return 0 ;; esac # apply EPB setting # try native kernel API first (5.2 and later) cnt=0 ec=0 for cpuf in /sys/devices/system/cpu/cpu*/power/energy_perf_bias; do if [ -f $cpuf ]; then cnt=$((cnt + 1)) if ! write_sysf "$pnum" $cpuf; then echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_kernel.write_error: $perf($pnum) $cpuf; rc=$?" ec=$((ec+1)) fi fi done if [ $cnt -gt 0 ]; then # native kernel API actually detected echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_kernel: $perf($pnum); ec=$ec" return 0 fi # no native kernel support, try x86_energy_perf_policy if ! cmd_exists $ENERGYPERF; then # x86_energy_perf_policy not installed echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_x: $perf($pnum); x86_energy_perf_policy not installed" else # x86_energy_perf_policy needs kernel module 'msr' load_modules $MOD_MSR $ENERGYPERF $pnum > /dev/null 2>&1 rc=$? case $rc in 0) echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_x: $perf($pnum); rc=$?" ;; 1) echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_x: $perf($pnum); rc=$? (unsupported cpu)" ;; 2) echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_x: $perf($pnum); rc=$? (kernel specific x86_energy_perf_policy missing)" ;; *) echo_debug "pm" "set_intel_cpu_perf_policy($1).epb_x: $perf($pnum); rc=$? (unknown)" ;; esac return $rc fi else echo_debug "pm" "set_intel_cpu_perf_policy($1).epb.unsupported_cpu" fi return 0 } set_intel_cpu_perf_pct () { # set Intel P-state performance limits # $1: 0=ac mode, 1=battery mode local min max if ! check_intel_pstate; then echo_debug "pm" "set_intel_cpu_perf_pct($1).no_intel_pstate" return 0 fi if [ "$1" = "1" ]; then min=${CPU_MIN_PERF_ON_BAT:-} max=${CPU_MAX_PERF_ON_BAT:-} else min=${CPU_MIN_PERF_ON_AC:-} max=${CPU_MAX_PERF_ON_AC:-} fi if [ ! -f $CPU_MIN_PERF_PCT ]; then echo_debug "pm" "set_intel_cpu_perf_pct($1).min.not_supported" elif [ -n "$min" ]; then write_sysf "$min" $CPU_MIN_PERF_PCT echo_debug "pm" "set_intel_cpu_perf_pct($1).min: $min; rc=$?" else echo_debug "pm" "set_intel_cpu_perf_pct($1).min.not_configured" fi if [ ! -f $CPU_MAX_PERF_PCT ]; then echo_debug "pm" "set_intel_cpu_perf_pct($1).max.not_supported" elif [ -n "$max" ]; then write_sysf "$max" $CPU_MAX_PERF_PCT echo_debug "pm" "set_intel_cpu_perf_pct($1).max: $max; rc=$?" else echo_debug "pm" "set_intel_cpu_perf_pct($1).max.not_configured" fi return 0 } set_cpu_boost_all () { # $1: 0=ac mode, 1=battery mode # global cpu boost behavior control based on the current power mode # # Relevant config option(s): CPU_BOOST_ON_{AC,BAT} with values {'',0,1} # # Note: # * needs commit #615b7300717b9ad5c23d1f391843484fe30f6c12 # (linux-2.6 tree), "Add support for disabling dynamic overclocking", # => requires Linux 3.7 or later local val if [ "$1" = "1" ]; then val=${CPU_BOOST_ON_BAT:-} else val=${CPU_BOOST_ON_AC:-} fi if [ -z "$val" ]; then # do nothing if unconfigured echo_debug "pm" "set_cpu_boost_all($1).not_configured" return 0 fi if check_intel_pstate; then # use intel_pstate sysfiles, invert value if write_sysf "$((val ^ 1))" $CPU_TURBO_PSTATE; then echo_debug "pm" "set_cpu_boost_all($1).intel_pstate: $val" else echo_debug "pm" "set_cpu_boost_all($1).intel_pstate.cpu_not_supported" fi elif [ -f $CPU_BOOST_ALL_CTRL ]; then # use acpi_cpufreq sysfiles # simple test for attribute "w" doesn't work, so actually write if write_sysf "$val" $CPU_BOOST_ALL_CTRL; then echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq: $val" else echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq.cpu_not_supported" fi else echo_debug "pm" "set_cpu_boost_all($1).not_available" fi return 0 } set_sched_powersave () { # set multi-core/-thread powersave policy # $1: 0=ac mode, 1=battery mode local pwr pool sdev local avail=0 if [ "$1" = "1" ]; then pwr=${SCHED_POWERSAVE_ON_BAT:-} else pwr=${SCHED_POWERSAVE_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_sched_powersave($1).not_configured" return 0 fi for pool in mc smp smt; do sdev="/sys/devices/system/cpu/sched_${pool}_power_savings" if [ -f "$sdev" ]; then write_sysf "$pwr" $sdev echo_debug "pm" "set_sched_powersave($1): ${sdev##/*/} $pwr; rc=$?" avail=1 fi done [ "$avail" = "1" ] || echo_debug "pm" "set_sched_powersave($1).not_available" return 0 } # --- Misc set_nmi_watchdog () { # enable/disable nmi watchdog local nmiwd=${NMI_WATCHDOG:-} if [ -z "$nmiwd" ]; then # do nothing if unconfigured echo_debug "pm" "set_nmi_watchdog.not_configured" return 0 fi if [ -f /proc/sys/kernel/nmi_watchdog ]; then if write_sysf "$nmiwd" /proc/sys/kernel/nmi_watchdog; then echo_debug "pm" "set_nmi_watchdog: $nmiwd; rc=$?" else echo_debug "pm" "set_nmi_watchdog.disabled_by_kernel: $nmiwd" fi else echo_debug "pm" "set_nmi_watchdog.not_available" fi return 0 } set_phc_controls () { # set core voltages local control local ctrl_avail="0" phc_controls=${PHC_CONTROLS:-} if [ -z "$phc_controls" ]; then # do nothing if unconfigured echo_debug "pm" "set_phc_controls.not_configured" return 0 fi for control in /sys/devices/system/cpu/cpu*/cpufreq/phc_controls; do if [ -f "$control" ]; then write_sysf "$phc_controls" $control echo_debug "pm" "set_phc_controls: $control $phc_controls; rc=$?" ctrl_avail="1" fi done [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_phc_controls.not_available" return 0 } TLP-1.3.1/func.d/15-tlp-func-disk000066400000000000000000000327211361705037700161740ustar00rootroot00000000000000#!/bin/sh # tlp-func-disk - Storage Device and Filesystem Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly AHCID=$PCID'/*/ata*' readonly BLOCKD='/sys/block/nvme* /sys/block/sd*' readonly DISK_TYPES_NO_APM_CHANGE="usb ieee1394" readonly DISK_NOP_WORDS="_ keep" # ---------------------------------------------------------------------------- # Functions # --- Device Helpers get_disk_dev () { # translate disk id to device (sdX) # $1: id or dev; # retval: $_disk_dev, $_disk_id, $_disk_type local bus dpath path if [ -h /dev/disk/by-id/$1 ]; then # $1 is disk id _disk_id=$1 _disk_dev=$(printf '%s' "$_disk_id" | sed -r 's/-part[1-9][0-9]*$//') _disk_dev=$(readlink /dev/disk/by-id/$_disk_dev) _disk_dev=${_disk_dev##*/} else # $1 is disk dev _disk_dev=$1 _disk_id="" fi # determine device type (bus) if [ -b /dev/$_disk_dev ]; then path="$($UDEVADM info -q property /dev/$_disk_dev 2>/dev/null | sed -n 's/^ID_PATH=//p')" bus="$($UDEVADM info -q property /dev/$_disk_dev 2>/dev/null | sed -n 's/^ID_BUS=//p')" dpath= case "$path" in pci-*-nvme-*) _disk_type="nvme" ;; pci-*-ata-*) _disk_type="ata" ;; pci-*-usb-*) _disk_type="usb" ;; pci-*-ieee1394-*) _disk_type="ieee1394" ;; *) case "$bus" in nvme) _disk_type="nvme" ;; ata) _disk_type="ata" ;; usb) _disk_type="usb" ;; ieee1394) _disk_type="ieee1394" ;; *) dpath="$($UDEVADM info -q property /dev/$_disk_dev 2>/dev/null | sed -n 's/^DEVPATH=//p')" dpath=${dpath##*/} case "$dpath" in nvme*) _disk_type="nvme" ;; *) _disk_type="unknown" ;; esac ;; esac esac echo_debug "disk" "get_disk_dev($1): dev=$_disk_dev; type=$_disk_type; path=$path; bus=$bus; dpath=$dpath" return 0 else _disk_type="none" echo_debug "disk" "get_disk_dev($1).missing" return 1 fi } check_disk_apm () { # check if disk device supports advanced pm # $1: dev; rc: 0=yes/1=no if $HDPARM -i $1 2> /dev/null | grep -q 'AdvancedPM=yes'; then return 0 else return 1 fi } show_disk_ids () { # show disk id's local dev { # iterate SATA and NVMe disks # shellcheck disable=SC2010 for dev in $(ls /dev/disk/by-id/ | grep -E '^(ata|ieee1394|nvme|usb)' | grep -E -v '(^nvme-eui|\-part[1-9]+)'); do if [ -n "$dev" ]; then get_disk_dev $dev && printf '%s: %s\n' "$_disk_dev" "$_disk_id" fi done } | sort return 0 } # --- Disk APM Features set_disk_apm_level () { # set disk apm level # $1: 0=ac mode, 1=battery mode local pwrmode="$1" local dev log_message # quit when disabled if [ -z "$DISK_DEVICES" ]; then echo_debug "disk" "set_disk_apm_level($pwrmode).disabled" return 0 fi # set @argv := apmlist (blanks removed - relying on a sane $IFS) if [ "$pwrmode" = "1" ]; then set -- $DISK_APM_LEVEL_ON_BAT else set -- $DISK_APM_LEVEL_ON_AC fi # quit if empty apmlist if [ $# -eq 0 ]; then echo_debug "disk" "set_disk_apm_level($pwrmode).not_configured" return 0 fi echo_debug "disk" "set_disk_apm_level($pwrmode)" # pairwise iteration DISK_DEVICES[1,n], apmlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], apmlist[min(j,m)] # for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_APM_LEVEL list handling} if get_disk_dev $dev; then log_message="set_disk_apm_level($pwrmode): $_disk_dev [$_disk_id] $1" if ! check_disk_apm /dev/$_disk_dev || wordinlist "$_disk_type" "$DISK_TYPES_NO_APM_CHANGE"; then echo_debug "disk" "${log_message}; not supported" elif wordinlist "$1" "$DISK_NOP_WORDS"; then echo_debug "disk" "${log_message}; keep as is" else $HDPARM -B $1 /dev/$_disk_dev > /dev/null 2>&1 echo_debug "disk" "${log_message}; rc=$?" fi fi # last entry in apmlist applies to all remaining disks [ $# -lt 2 ] || shift done return 0 } set_disk_spindown_timeout () { # set disk spindown timeout # $1: 0=ac mode, 1=battery mode local pwrmode="$1" local dev log_message # quit when disabled if [ -z "$DISK_DEVICES" ]; then echo_debug "disk" "set_disk_spindown_timeout($pwrmode).disabled" return 0 fi # set @argv := timeoutlist if [ "$pwrmode" = "1" ]; then set -- $DISK_SPINDOWN_TIMEOUT_ON_BAT else set -- $DISK_SPINDOWN_TIMEOUT_ON_AC fi # quit if empty timeoutlist if [ $# -eq 0 ]; then echo_debug "disk" "set_disk_spindown_timeout($pwrmode).not_configured" return 0 fi echo_debug "disk" "set_disk_spindown_timeout($pwrmode)" # pairwise iteration DISK_DEVICES[1,n], timeoutlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], timeoutlist[min(j,m)] # for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_SPINDOWN_TIMEOUT list handling} if get_disk_dev $dev; then log_message="set_disk_spindown_timeout($pwrmode): $_disk_dev [$_disk_id] $1" if wordinlist "$1" "$DISK_NOP_WORDS"; then echo_debug "disk" "${log_message}; keep as is" else $HDPARM -S $1 /dev/$_disk_dev > /dev/null 2>&1 echo_debug "disk" "${log_message}; rc=$?" fi fi # last entry in timeoutlist applies to all remaining disks [ $# -lt 2 ] || shift done return 0 } spindown_disk () { # stop spindle motor -- $1: dev $HDPARM -y /dev/$1 > /dev/null 2>&1 return 0 } set_disk_iosched () { # set disk io scheduler local dev log_message # quit when disabled if [ -z "$DISK_DEVICES" ]; then echo_debug "disk" "set_disk_iosched.disabled" return 0 fi # set @argv := schedlist set -- $DISK_IOSCHED # quit if empty schedlist if [ $# -eq 0 ]; then echo_debug "disk" "set_disk_iosched.not_configured" return 0 fi echo_debug "disk" "set_disk_iosched" # pairwise iteration DISK_DEVICES[1,n], schedlist[1,m]; m > 0 # for j in [1,min(n,m)] : disk_dev[j], schedlistj] # for j in [min(n,m)+1,n] : disk_dev[j], %keep for dev in $DISK_DEVICES; do local sched schedctrl if get_disk_dev $dev; then # get sched from argv, use "keep" when list is too short sched=${1:-keep} schedctrl="/sys/block/$_disk_dev/queue/scheduler" log_message="set_disk_iosched: $_disk_dev [$_disk_id] $sched" if [ ! -f $schedctrl ]; then echo_debug "disk" "${log_message}; not supported" elif wordinlist "$sched" "$DISK_NOP_WORDS"; then echo_debug "disk" "${log_message}; keep as is" else write_sysf "$sched" $schedctrl echo_debug "disk" "${log_message}; rc=$?" fi fi # using "keep" when argv is empty [ $# -eq 0 ] || shift done return 0 } # --- Power Saving set_sata_link_power () { # set ahci link power management # $1: 0=ac mode, 1=battery mode local pm="$1" local host host_bl hostid linkpol pwr rc local pwrlist="" local ctrl_avail="0" if [ "$pm" = "1" ]; then pwrlist=${SATA_LINKPWR_ON_BAT:-} else pwrlist=${SATA_LINKPWR_ON_AC:-} fi if [ -z "$pwrlist" ]; then # do nothing if unconfigured echo_debug "pm" "set_sata_link_power($pm).not_configured" return 0 fi # ALPM blacklist host_bl=${SATA_LINKPWR_BLACKLIST:-} # copy configured values to args set -- $pwrlist # iterate SATA hosts for host in /sys/class/scsi_host/host* ; do linkpol=$host/link_power_management_policy if [ -f $linkpol ]; then hostid=${host##*/} if wordinlist "$hostid" "$host_bl"; then # host blacklisted --> skip echo_debug "pm" "set_sata_link_power($pm).black: $host" ctrl_avail="1" else # host not blacklisted --> iterate all configured values for pwr in "$@"; do write_sysf "$pwr" $linkpol; rc=$? echo_debug "pm" "set_sata_link_power($pm).$pwr: $host; rc=$rc" if [ $rc -eq 0 ]; then # write successful --> goto next host ctrl_avail="1" break else # write failed --> don't use this value for remaining hosts # and try next value shift fi done fi fi done [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_sata_link_power($pm).not_available" return 0 } set_ahci_runtime_pm () { # set ahci runtime power management # $1: 0=ac mode, 1=battery mode local control device ec timeout rc if [ "$1" = "1" ]; then control=${AHCI_RUNTIME_PM_ON_BAT:-} else control=${AHCI_RUNTIME_PM_ON_AC:-} fi # calc timeout in millisecs timeout="$AHCI_RUNTIME_PM_TIMEOUT" [ -z "$timeout" ] || timeout=$((timeout * 1000)) # check values case "$control" in on|auto) ;; *) control="" ;; # invalid input --> unconfigured esac if [ -z "$control" ]; then # do nothing if unconfigured echo_debug "pm" "set_ahci_runtime_pm($1).not_configured" return 0 fi # when timeout is unconfigured we're done here if [ -z "$timeout" ]; then echo_debug "pm" "set_ahci_runtime_pm($1).timeout_not_configured" return 0 fi # iterate disks ec=0 for device in $BLOCKD; do if [ -f ${device}/device/power/control ]; then # write timeout first because writing "auto" with the default # timeout -1 still active will lockup the machine! rc=0 if write_sysf "$timeout" ${device}/device/power/autosuspend_delay_ms; then # writing timeout was successful --> proceed with activation; if ! write_sysf "$control" ${device}/device/power/control; then # writing control failed rc=2 ec=$((ec+1)) fi else # writing timeout failed (or file not present) rc=1 ec=$((ec+1)) fi echo_debug "pm" "set_ahci_runtime_pm($1).$control: disk=$device timeout=$timeout; rc=$rc" fi done if [ $ec -gt 0 ]; then # quit: do not touch hosts if write failed for at least one disk echo_debug "pm" "set_ahci_runtime_pm($1).quit; ec=$ec" return 0 fi # iterate hosts for device in $AHCID; do write_sysf "$control" ${device}/power/control echo_debug "pm" "set_ahci_runtime_pm($1).$control: host=$device; rc=$?" done return 0 } # --- Filesystem Parameters set_laptopmode () { # set kernel laptop mode -- $1: 0=ac mode, 1=battery mode check_sysfs "set_laptopmode" "/proc/sys/vm/laptop_mode" local isec if [ "$1" = "1" ]; then isec="$DISK_IDLE_SECS_ON_BAT" else isec="$DISK_IDLE_SECS_ON_AC" fi # replace with empty string if non-numeric chars are contained isec=$(printf '%s' "$isec" | grep -E '^[0-9]+$') if [ -z "$isec" ]; then # do nothing if unconfigured or non numeric value echo_debug "pm" "set_laptopmode($1).not_configured" return 0 fi write_sysf "$isec" /proc/sys/vm/laptop_mode echo_debug "pm" "set_laptopmode($1): $isec; rc=$?" return 0 } set_dirty_parms () { # set filesystem buffer params # $1: 0=ac mode, 1=battery mode # concept from laptop-mode-tools local age cage df ec check_sysfs "set_dirty_parms" "/proc/sys/vm" if [ "$1" = "1" ]; then age=${MAX_LOST_WORK_SECS_ON_BAT:-0} else age=${MAX_LOST_WORK_SECS_ON_AC:-0} fi # calc age in centisecs, non numeric values result in "0" cage=$((age * 100)) if [ "$cage" = "0" ]; then # do nothing if unconfigured or invalid age echo_debug "pm" "set_dirty_parms($1).not_configured" return 0 fi ec=0 for df in /proc/sys/vm/dirty_writeback_centisecs \ /proc/sys/vm/dirty_expire_centisecs \ /proc/sys/fs/xfs/age_buffer_centisecs \ /proc/sys/fs/xfs/xfssyncd_centisecs; do if [ -f "$df" ] && ! write_sysf "$cage" $df; then echo_debug "pm" "set_dirty_parms($1).write_error: $df $cage; rc=$?" ec=$((ec+1)) fi done # shellcheck disable=SC2043 for df in /proc/sys/fs/xfs/xfsbufd_centisecs; do if [ -f "$df" ] && ! write_sysf "3000" $df; then echo_debug "pm" "set_dirty_parms($1).write_error: $df 3000; rc=$?" ec=$((ec+1)) fi done echo_debug "pm" "set_dirty_parms($1): $cage; ec=$ec" return 0 } TLP-1.3.1/func.d/20-tlp-func-usb000066400000000000000000000240531361705037700160260ustar00rootroot00000000000000#!/bin/sh # tlp-func-usb - USB Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086,SC2155 # ---------------------------------------------------------------------------- # Constants readonly USBD=/sys/bus/usb/devices readonly USB_TIMEOUT=2 readonly USB_TIMEOUT_MS=2000 readonly USB_WWAN_VENDORS="0bdb 05c6 1199 2cb7" readonly USB_DONE=usb_done # ---------------------------------------------------------------------------- # Functions # --- USB Autosuspend usb_suspend_device () { # enable/disable usb autosuspend for a single device # except input and blacklisted # $1: device syspath, $2: batch/udev, $3: auto=enable/on=disable local usbdev=$1 if [ -f $usbdev/power/autosuspend_delay_ms ]; then # device is autosuspendable local vendor="$(read_sysf $usbdev/idVendor)" local usbid="$vendor:$(read_sysf $usbdev/idProduct)" local busdev="Bus $(read_sysf $usbdev/busnum) Dev $(read_sysf $usbdev/devnum)" local dclass="$(read_sysf $usbdev/bDeviceClass)" local control="${3:-auto}" local caller="$2" local exc="" local chg=0 rc1=0 rc2=0 local drvlist="" # trace only: get drivers for all subdevices if [ "$X_USB_DRIVER_TRACE" = "1" ]; then local dl drvlist=$(for dl in $usbdev/*:*/driver; do readlink "$dl" | \ sed -r 's/.+\///'; done | sort -u | tr '\n' ' ') drvlist="(${drvlist% })" fi if wordinlist "$usbid" "$USB_WHITELIST"; then # device is in whitelist -- whitelist always wins control="auto" exc="_dev_white" elif wordinlist "$usbid" "$USB_BLACKLIST"; then # device is in blacklist control="on" exc="_dev_black" else local subdev # udev: wait for subdevices to populate [ "$caller" = "udev" ] && sleep 0.5 # check for hid subdevices for subdev in $usbdev/*:*; do if [ "$(read_sysf $subdev/bInterfaceClass)" = "03" ]; then control="on" exc="_hid_black" break fi done if [ -z "$exc" ]; then # check for bluetooth devices if [ "$USB_BLACKLIST_BTUSB" = "1" ] \ && [ "$dclass" = "e0" ] \ && [ "$(read_sysf $usbdev/bDeviceSubClass)" = "01" ] \ && [ "$(read_sysf $usbdev/bDeviceProtocol)" = "01" ]; then control="on" exc="_btusb_black" fi fi # bluetooth if [ -z "$exc" ]; then # check for scanners: # libsane_matched envvar is set by libsane's udev rules # shellcheck disable=SC2154 if [ "$libsane_matched" = "yes" ] || [ "$2" = "batch" ] \ && $UDEVADM info -q property $usbdev 2>/dev/null | grep -q 'libsane_matched=yes'; then # do not touch this device control="black" exc="_libsane" fi fi if [ -z "$exc" ]; then # check for phone devices if [ "$USB_BLACKLIST_PHONE" = "1" ]; then if [ "$vendor" = "0fca" ]; then # RIM if [ "$dclass" = "ef" ]; then # RIM / BlackBerry control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(read_sysf $subdev/interface)" = "BlackBerry" ]; then # Blackberry control="on" exc="_phone_black" break fi fi done fi elif [ "$vendor" = "045e" ] && [ "$dclass" = "ef" ]; then # Windows Phone control="on" exc="_phone_black" elif [ "$vendor" = "05ac" ] && [ "$(read_sysf $usbdev/product)" = "iPhone" ]; then # iPhone control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then # class defined at interface level, iterate subdevices for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(read_sysf $subdev/interface)" = "MTP" ]; then # MTP: mostly Android control="on" exc="_phone_black" break elif [ "$(read_sysf $subdev/bInterfaceClass)" = "ff" ] \ && [ "$(read_sysf $subdev/bInterfaceSubClass)" = "42" ] \ && [ "$(read_sysf $subdev/bInterfaceProtocol)" = "01" ]; then # ADB: Android control="on" exc="_phone_black" break elif [ "$(read_sysf $subdev/bInterfaceClass)" = "06" ] \ && [ "$(read_sysf $subdev/bInterfaceSubClass)" = "01" ] \ && [ "$(read_sysf $subdev/bInterfaceProtocol)" = "01" ]; then # PTP: iPhone, Lumia et al. # caveat: may also be a camera control="on" exc="_phone_black" break fi fi done fi # dclass 00 fi # blacklist phone fi # phone if [ -z "$exc" ]; then # check for printers if [ "$USB_BLACKLIST_PRINTER" = "1" ]; then if [ "$dclass" = "00" ]; then # check for printer subdevices for subdev in $usbdev/*:*; do if [ "$(read_sysf $subdev/bInterfaceClass)" = "07" ]; then control="on" exc="_printer_black" break fi done fi fi fi # printer if [ -z "$exc" ]; then # check for wwan devices if [ "$USB_BLACKLIST_WWAN" = "1" ]; then if [ "$dclass" != "00" ]; then # check for cdc subdevices for subdev in $usbdev/*:*; do if [ "$(read_sysf $subdev/bInterfaceClass)" = "0a" ]; then control="on" exc="_wwan_black" break fi done fi if [ -z "$exc" ]; then # check for vendors if wordinlist "$vendor" "$USB_WWAN_VENDORS"; then control="on" exc="_wwan_black" fi fi fi # blacklist wwan fi # wwan fi # !device blacklist if [ "$(read_sysf $usbdev/power/control)" != "$control" ]; then # set control, write actual changes only case $control in auto|on) write_sysf "$control" $usbdev/power/control; rc1=$? chg=1 ;; black) # do not touch blacklisted device ;; esac fi if [ "$X_TLP_USB_SET_AUTOSUSPEND_DELAY" = "1" ]; then # set autosuspend delay write_sysf $USB_TIMEOUT_MS $usbdev/power/autosuspend_delay_ms; rc2=$? echo_debug "usb" "usb_suspend_device.$caller.$control$exc: $busdev ID $usbid $usbdev [$drvlist]; control: rc=$rc1; delay: rc=$rc2" elif [ $chg -eq 1 ]; then # default: change control but not autosuspend_delay, i.e. keep kernel default setting echo_debug "usb" "usb_suspend_device.$caller.$control$exc: $busdev ID $usbid $usbdev [$drvlist]; control: rc=$rc1" else # we didn't change anything actually echo_debug "usb" "usb_suspend_device.$caller.$control$exc.no_change: $busdev ID $usbid $usbdev [$drvlist]" fi fi # autosuspendable return 0 } set_usb_suspend () { # enable/disable usb autosuspend for all devices # $1: 0=silent/1=report result; $2: auto=enable/on=disable local usbdev check_sysfs "set_usb_suspend" "$USBD" if [ "$USB_AUTOSUSPEND" = "1" ]; then # autosuspend is configured --> iterate devices for usbdev in $USBD/*; do case "$usbdev" in *:*) ;; # colon in device name --> do nothing *) usb_suspend_device "$usbdev" "batch" $2 ;; esac done [ "$1" = "1" ] && echo "USB autosuspend settings applied." echo_debug "usb" "set_usb_suspend.done" # set "startup completion" flag for tlp-usb-udev set_run_flag $USB_DONE else [ "$1" = "1" ] && echo "Error: USB autosuspend is disabled. Set USB_AUTOSUSPEND=1 in ${CONF_USR}." 1>&2 echo_debug "usb" "set_usb_suspend.not_configured" fi return 0 } TLP-1.3.1/func.d/25-tlp-func-rf000066400000000000000000000127421361705037700156530ustar00rootroot00000000000000#!/bin/sh # tlp-func-rf - Radio Device Checks and PM Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly IWC=iwconfig readonly IW=iw readonly BLUETOOTHD=/sys/class/bluetooth # ---------------------------------------------------------------------------- # Functions # --- Wifi Device Checks get_wifi_ifaces () { # get all wifi devices -- retval: $_wifaces local wi wiu _wifaces="" for wiu in $NETD/*/uevent; do if grep -q -s 'DEVTYPE=wlan' $wiu ; then wi=${wiu%/uevent}; wi=${wi##*/} _wifaces="$_wifaces $wi" fi done _wifaces="${_wifaces# }" return 0 } get_wifi_driver () { # get driver associated with interface # $1: iface; retval: $_wifidrv local drvl _wifidrv="" if [ -d $NETD/$1 ]; then drvl=$(readlink $NETD/$1/device/driver) # shellcheck disable=SC2034 [ -n "$drvl" ] && _wifidrv=${drvl##*/} fi return 0 } wireless_in_use () { # check if wifi or wwan device is in use -- $1: iface if [ -f $NETD/$1/carrier ]; then if [ "$(read_sysf $NETD/$1/carrier)" = "1" ]; then return 0 fi fi return 1 } any_wifi_in_use () { # check if any wifi device is in use local iface get_wifi_ifaces for iface in $_wifaces; do wireless_in_use $iface && return 0 done return 1 } # --- Wifi Power Management set_wifi_power_mode () { # set wifi power save mode -- $1: 0=ac mode, 1=battery mode local pwr iface local rc=0 local cmdx=0 if [ "$1" = "1" ]; then pwr=${WIFI_PWR_ON_BAT:-} else pwr=${WIFI_PWR_ON_AC:-} fi # check values, translate obsolete syntax case "$pwr" in off|on) ;; 0|1|N) pwr="off" ;; 2|3|4|5|6|Y) pwr="on" ;; *) pwr="" ;; # invalid input --> unconfigured esac if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_wifi_power_mode($1).not_configured" return 0 fi get_wifi_ifaces if [ -z "$_wifaces" ]; then echo_debug "pm" "set_wifi_power_mode($1).no_ifaces" return 0 fi for iface in $_wifaces; do if [ -n "$iface" ]; then if [ "$X_DONT_USE_IW" != "1" ] && cmd_exists $IW; then # try with iw first $IW dev $iface set power_save $pwr > /dev/null 2>&1 rc=$? echo_debug "pm" "set_wifi_power_mode($1, $iface).iw: $pwr; rc=$rc" cmdx=1 # set flag: iw found and called fi if cmd_exists $IWC; then if [ $rc -ne 0 ] || [ $cmdx -eq 0 ]; then # iw did not succeed or iw not installed -> try with iwconfig $IWC $iface power $pwr > /dev/null 2>&1 rc=$? echo_debug "pm" "set_wifi_power_mode($1, $iface).iwconfig: $pwr; rc=$rc" cmdx=1 # set flag: iwconfig found and called fi fi if [ $cmdx -eq 0 ]; then # flag not set: neither iw nor iwconfig installed --> no way echo_debug "pm" "set_wifi_power_mode($1, $iface).no_tool" fi fi done return 0 } # --- WWAN Device Checks get_wwan_ifaces () { # get all wwan devices -- retval: $_wanifaces local wi wiu _wanifaces="" for wiu in $NETD/*/uevent; do if grep -q -s 'DEVTYPE=wwan' $wiu ; then wi=${wiu%/uevent}; wi=${wi##*/} _wanifaces="$_wanifaces $wi" fi done _wanifaces="${_wanifaces# }" return 0 } any_wwan_in_use () { # check if any wwan device is in use local iface get_wwan_ifaces for iface in $_wanifaces; do wireless_in_use $iface && return 0 done return 1 } get_wwan_driver () { # get driver associated with interface # $1: iface; retval: $_wwandrv local drvl _wwandrv="" if [ -d $NETD/$1 ]; then drvl=$(readlink $NETD/$1/device/driver) # shellcheck disable=SC2034 [ -n "$drvl" ] && _wwandrv=${drvl##*/} fi return 0 } # --- Bluetooth Device Checks get_bluetooth_ifaces () { # get all bluetooth interfaces -- retval: $_bifaces # enumerate symlinks only _bifaces="$(for i in $BLUETOOTHD/*; do [ -h $i ] && echo ${i##/*/}; done | grep -v ':')" return 0 } get_bluetooth_driver () { # get driver associated with interface -- $1: iface; retval: $_btdrv local drvl # shellcheck disable=SC2034 _btdrv="" if [ -d $BLUETOOTHD/$1 ]; then drvl=$(readlink $BLUETOOTHD/$1/device/driver) # shellcheck disable=SC2034 [ -n "$drvl" ] && _btdrv=${drvl##*/} fi return 0 } bluetooth_in_use () { # check if bluetooth interface is in use -- $1: iface local uev # when devices are connected to an interface its sysdir is populated with # subdevices like : where the uevent file contains a line # "DEVTYPE=link" for uev in $BLUETOOTHD/$1/$1:*/uevent; do grep -q -s 'DEVTYPE=link' $uev && return 0 done return 1 } any_bluetooth_in_use () { # check if any bluetooth interface is in use local i get_bluetooth_ifaces for i in $_bifaces; do bluetooth_in_use "$i" && return 0 done return 1 } TLP-1.3.1/func.d/30-tlp-func-rf-sw000066400000000000000000000357501361705037700163020ustar00rootroot00000000000000#!/bin/sh # tlp-func-rf-sw - Radio Switch Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, tlp-func-rf # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly NMCLI=nmcli readonly RFKILL="rfkill" readonly RFKD="/dev/rfkill" readonly ALLDEV="bluetooth wifi wwan" readonly RDW_NM_LOCK="rdw_nm" readonly RDW_DOCK_LOCK="rdw_dock" readonly RDW_LOCKTIME=2 readonly RDW_KILL="rdw_kill" readonly RFSTATEFILE=$VARDIR/rfkill_saved # ---------------------------------------------------------------------------- # Functions get_devc () { # get control device for radio type # $1: rftype bluetooth/wifi/wwan # retval $_devc: sysdev, $_devs: device state # $_rfkdev: 1/0=is/is not an rfkill device, # $_devon, $_devoff: value to write directly to the sysdev # to achieve the desired switch state local i check_sysfs "get_devc" "/sys/class/rfkill" # preset retvals _devc="" _devs=254 _rfkdev="1" _devon="1" _devoff="0" case $1 in wwan|bluetooth) for i in /sys/class/rfkill/rfkill* ; do if [ "$(read_sysf $i/type)" = "$1" ]; then _devc="$i/state" echo_debug "rf" "get_devc($1) = $_devc" return 0 fi done ;; wifi) for i in /sys/bus/pci/drivers/ipw2?00/*/rf_kill; do if [ -f "$i" ]; then _devc="$i" _rfkdev="0" _devon="0" _devoff="1" echo_debug "rf" "get_devc($1) = $_devc" return 0 fi done for i in /sys/class/rfkill/rfkill* ; do if [ "$(read_sysf $i/type)" = "wlan" ]; then _devc="$i/state" echo_debug "rf" "get_devc($1) = $_devc" return 0 fi done ;; *) echo "Error: unknown device type \"$1\"" 1>&2 echo_debug "rf" "get_devc($1).unknown_type" return 0 ;; esac echo_debug "rf" "get_devc($1).not_present" return 0 } get_devs () { # get radio device state -- $1: rftype; retval $_devs: 0=off/1=on if [ -n "$_devc" ]; then _devs="$(read_sysf $_devc)" case "$_devs" in 0|1) # invert state when not a rfkill device [ "$_rfkdev" = "0" ] && _devs=$((_devs ^ _devoff)) ;; 2) ;; # hard blocked device *) _devs=3 # invalid state esac fi echo_debug "rf" "get_devs($1) = $_devs" return 0 } err_no_root_priv () { # check root privilege echo "Error: missing root privilege." 1>&2 echo_debug "rf" "$1.missing_root_privilege" return 0 } test_rfkill_perms () { # test if either root priv or rfkill device writable test_root || [ -w $RFKD ] } check_nm () { # test if NetworkManager is running and nmcli is installed cmd_exists $NMCLI } invoke_nmcli () { # call nmcli with radio option according to the program version # $1: rftype, $2: on/off, $3: caller; rc: last nmcli rc local rc check_nm || return 0 # return if NetworkManager not running $NMCLI radio $1 $2 > /dev/null 2>&1; rc=$? echo_debug "rf" "invoke_nmcli($1, $2).radio: rc=$rc" return $rc } device_state () { # get radio type state -- $1: rftype; retval $_devc, $_devs: 0=off/1=on echo_debug "rf" "device_state($1)" get_devc $1 get_devs $1 } device_switch () { # switch radio type state # $1: rftype, $2: 1/on/0/off/toggle # $3: lock id, $4: lock duration # rc: 0=switched/1=invalid device or operation/ # 2=hard blocked/3=invalid state/4=no change # retval $_devc, $_devs: 0=off/1=on local curst newst devn echo_debug "rf" "device_switch($1, $2, $3, $4)" get_devc $1 # quit if no device if [ -z "$_devc" ]; then echo_debug "rf" "device_switch($1, $2).no_device: rc=1" return 1 fi # quit if invalid operation if ! wordinlist $2 "on 1 off 0 toggle"; then echo_debug "rf" "device_switch($1, $2).invalid_op: rc=1" return 1 fi # get current device state get_devs $1 curst=$_devs # quit if device state is hard blocked or invalid if [ $_devs -ge 2 ]; then case $_devs in 2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;; *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;; esac return $_devs fi # determine desired device state case $2 in 1|on) newst=1 ;; 0|off) newst=0 ;; toggle) newst=$((curst ^ 1)) ;; esac # wifi, wwan: before rfkill (only if X_configured) -- notify NM of desired state if [ "$X_USE_NMCLI" = "1" ] && [ "$1" != "bluetooth" ]; then case $newst in 1) invoke_nmcli $1 on ;; 0) invoke_nmcli $1 off ;; esac # record device state after nmcli get_devs $1 # update current state curst=$_devs # quit if device state is hard blocked or invalid if [ $_devs -ge 2 ]; then case $_devs in 2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;; *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;; esac return $_devs fi fi # compare current and desired device state if [ "$curst" = "$newst" ]; then # desired matches current state --> do nothing echo_debug "rf" "device_switch($1, $2).desired_state" else # desired does not match current state --> do switch # set timed lock if required [ -n "$3" ] && [ -n "$4" ] && [ "$1" != "bluetooth" ] && \ set_timed_lock $3 $4 # determine value for direct write case $newst in 1) devn=$_devon ;; 0) devn=$_devoff ;; esac # switch device state when either rfkill isn't disabled # or it's a bluetooth device if [ "$X_USE_RFKILL" != "0" ] || [ "$1" != "bluetooth" ]; then if [ "$_rfkdev" = "1" ] && cmd_exists $RFKILL ; then if test_rfkill_perms ; then # use rfkill echo_debug "rf" "device_switch($1, $2).rfkill" case $newst in 1) $RFKILL unblock $1 > /dev/null 2>&1 ;; 0) $RFKILL block $1 > /dev/null 2>&1 ;; *) ;; esac # record device state after rfkill get_devs $1 else # missing permission to rfkill err_no_root_priv "device_switch($1, $2).rfkill" fi else # use direct write if test_root ; then write_sysf "$devn" $_devc echo_debug "rf" "device_switch($1, $2).devc: rc=$?" # record device state after direct write get_devs $1 else err_no_root_priv "device_switch($1, $2).devc" fi fi fi # rfkill not disabled or bluetooth fi # states did not match # wifi, wwan: after rkfill (default) -- notify NM of desired state if [ "$X_USE_NMCLI" != "0" ] && [ "$1" != "bluetooth" ]; then case $newst in 1) invoke_nmcli $1 on ;; 0) invoke_nmcli $1 off ;; esac # record final device state get_devs $1 fi # quit if device state is hard blocked or invalid if [ $_devs -ge 2 ]; then case $_devs in 2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;; *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;; esac return $_devs fi # compare old and new device state if [ "$curst" = "$_devs" ]; then # state did not change echo_debug "rf" "device_switch($1, $2).no_change: rc=4" return 4 else echo_debug "rf" "device_switch($1, $2).ok: rc=0" return 0 fi } echo_device_state () { # print radio type state -- $1: rftype, $2: state case $1 in bluetooth) devstr="bluetooth" ;; wifi) devstr="wifi " ;; wwan) devstr="wwan " ;; *) devstr=$1 ;; esac case $2 in 0) echo "$devstr = off (software)" ;; 1) echo "$devstr = on" ;; 2) echo "$devstr = off (hardware)" ;; 254) echo "$devstr = none (no device)" ;; *) echo "$devstr = invalid state" esac return 0 } # shellcheck disable=SC2120 save_device_states () { # save radio states -- $1: list of rftypes # rc: 0=ok/1=create failed/2=write failed local dev local devlist="${1:-$ALLDEV}" # when arg empty -> use all local rc=0 # create empty state file if { : > $RFSTATEFILE; } 2> /dev/null; then # iterate over all possible devices -> save state in file for dev in $devlist; do device_state $dev { printf '%s\n' "$dev $_devs" >> $RFSTATEFILE; } 2> /dev/null || rc=2 done else # create failed rc=1 fi echo_debug "rf" "save_device_states($devlist): $RFSTATEFILE; rc=$rc" return $rc } restore_device_states () { # restore radio type states # rc: 0=ok/1=state file nonexistent local sline local rc=0 if [ -f $RFSTATEFILE ]; then # read state file # shellcheck disable=SC2162 while read sline; do set -- $sline # read dev, state into $1, $2 device_switch $1 $2 done < $RFSTATEFILE else # state file nonexistent rc=1 fi echo_debug "rf" "restore_device_states: $RFSTATEFILE; rc=$rc" return $rc } set_radio_device_states () { # set/initialize all radio states # $1: start/stop/1/0/radiosw # called from init scripts or upon change of power source local dev devs2disable devs2enable restore local quiet=0 # save/restore mode is disabled by default if [ "$1" != "radiosw" ]; then restore="$RESTORE_DEVICE_STATE_ON_STARTUP" else restore=0 fi if [ "$restore" = "1" ]; then # "save/restore" mode echo_debug "rf" "set_radio_device_states($1).restore" case $1 in start) if restore_device_states; then echo "Radio device states restored." else echo "No saved radio device states found." fi ;; stop) # shellcheck disable=SC2119 save_device_states echo "Radio device states saved." ;; esac else # "disable/enable on startup/shutdown or bat/ac" or "radiosw" mode case $1 in start) # system startup devs2disable="$DEVICES_TO_DISABLE_ON_STARTUP" devs2enable="$DEVICES_TO_ENABLE_ON_STARTUP" ;; stop) # system shutdown devs2disable="$DEVICES_TO_DISABLE_ON_SHUTDOWN" devs2enable="$DEVICES_TO_ENABLE_ON_SHUTDOWN" if [ "$X_WIFI_ON_SHUTDOWN" != "0" ]; then # NM workaround: if # 1. disable wifi is configured somehow, and # 2. wifi is not explicitly configured for shutdown # then re-enable wifi on shutdown to prepare for startup if wordinlist "wifi" "$DEVICES_TO_DISABLE_ON_BAT $DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE $DEVICES_TO_DISABLE_ON_LAN_CONNECT $DEVICES_TO_DISABLE_ON_WIFI_CONNECT $DEVICES_TO_DISABLE_ON_WWAN_CONNECT" && \ ! wordinlist "wifi" "$devs2disable $devs2enable"; then devs2enable="wifi $devs2enable" fi fi ;; 1) # battery power --> build disable list quiet=1 # do not display progress devs2enable="" devs2disable="${DEVICES_TO_DISABLE_ON_BAT:-}" # check configured list for connected devices for dev in ${DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE:-}; do # if device is not connected and not in list yet --> add to disable list { case $dev in bluetooth) any_bluetooth_in_use ;; wifi) any_wifi_in_use ;; wwan) any_wwan_in_use ;; esac } || wordinlist $dev "$devs2disable" || devs2disable="$dev $devs2disable" done devs2disable="${devs2disable# }" ;; 0) # AC power --> build enable list quiet=1 # do not display progress devs2enable="${DEVICES_TO_ENABLE_ON_AC:-}" devs2disable="" ;; radiosw) devs2disable="" devs2enable="$DEVICES_TO_ENABLE_ON_RADIOSW" ;; esac echo_debug "rf" "set_radio_device_states($1): enable=$devs2enable disable=$devs2disable" # Disable configured radios if [ -n "$devs2disable" ]; then [ "$quiet" = "1" ] || echo -n "Disabling radios:" for dev in $devs2disable; do [ "$quiet" = "1" ] || printf ' %s' "$dev" device_switch "$dev" off done [ "$quiet" = "1" ] || echo "." fi # Enable configured radios if [ -n "$devs2enable" ]; then if [ "$1" = "radiosw" ]; then # radiosw mode: disable radios not listed for dev in bluetooth wifi wwan; do if ! wordinlist "$dev" "$devs2enable"; then device_switch $dev off fi done else # start mode: enable listed radios [ "$quiet" = "1" ] || echo -n "Enabling radios:" for dev in $devs2enable; do [ "$quiet" = "1" ] || echo -n " $dev" device_switch $dev on done [ "$quiet" = "1" ] || echo "." fi fi # clean up: discard state file rm -f $RFSTATEFILE 2> /dev/null fi return 0 } TLP-1.3.1/func.d/35-tlp-func-batt000066400000000000000000001112711361705037700161740ustar00rootroot00000000000000#!/bin/sh # tlp-func-batt - [ThinkPad] Battery Feature Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly MODINFO=modinfo readonly TPACPIDIR=/sys/devices/platform/thinkpad_acpi readonly SMAPIBATDIR=/sys/devices/platform/smapi readonly ACPIBATDIR=/sys/class/power_supply # ---------------------------------------------------------------------------- # Functions # --- Battery Feature Support check_battery_features () { # determine which battery feature APIs/tools are # supported by hardware and running kernel. # # 1. check for native kernel acpi (Linux 4.17 or higher required) # --> retval $_natacpi: # 0=thresholds and discharge/ # 1=thresholds only/ # 32=disabled/ # 128=no kernel support/ # 253=laptop not supported # 254=ThinkPad not supported # # 2. check for acpi-call external kernel module and test with integrated # tpacpi-bat [ThinkPads only] # --> retval $_tpacpi: # 0=supported/ # 32=disabled/ # 64=acpi_call module not loaded/ # 127=tpacpi-bat not installed/ # 128=acpi_call module not installed/ # 253=laptop not supported/ # 254=ThinkPad not supported/ # 255=superseded by natacpi # # 3. check for tp-smapi external kernel module [ThinkPads only] # --> retval $_tpsmapi: # 0=supported/ # 1=readonly/ # 32=disabled/ # 64=tp_smapi module not loaded/ # 128=tp_smapi module not installed/ # 253=laptop not supported/ # 254=ThinkPad not supported/ # # 4. determine best method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi/tpacpi/tpsmapi # # 5. define threshold boundary conditions # minimum start value --> retval $_tc_start_min # maximum start value / factory default --> retval $_tc_start_max # minimum stop value --> retval $_tc_stop_min # maximum stop value / factory default --> retval $_tc_stop_max # minimum difference start - stop --> retval $_tc_diff # factory default alias --> retval $_tc_default # applicable only when $_bm_thresh != "none" # # prerequisite: check_thinkpad() # replaces: check_tpsmapi, check_tpacpi # preset: natacpi takes it all _natacpi=128 _tpacpi=255 _tpsmapi=255 _bm_read="natacpi" _bm_thresh="none" _bm_dischg="none" # threshold boundary presets, atm ThinkPads only _tc_start_min=1 _tc_stop_min=5 _tc_start_max=96 _tc_stop_max=100 _tc_diff=4 _tc_default=0 # --- 1. check for native kernel ACPI (Linux 4.17 or higher required) local ps for ps in $ACPIBATDIR/*; do if [ "$(read_sysf $ps/present)" = "1" ]; then # battery detected if [ -f $ps/charge_start_threshold ]; then # sysfile for native acpi support detected _natacpi=253 if readable_sysf $ps/charge_start_threshold; then # charge_start_threshold exists and is actually readable if [ "$NATACPI_ENABLE" = "1" ]; then _natacpi=1 _bm_thresh="natacpi" else _natacpi=32 fi fi if [ $_natacpi != 32 ] && readable_sysf $ps/force_discharge; then # force_discharge exists and is actually readable _natacpi=0 _bm_dischg="natacpi" fi elif kernel_version_ge 4.17; then # kernel capable of native acpi support _natacpi=253 fi break # quit loop on first battery detected fi done if [ $_natacpi -eq 253 ] && is_thinkpad; then # map code for ThinkPads _natacpi=254 fi echo_debug "bat" "check_battery_features.natacpi: $_natacpi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)" # when not a Thinkpad --> we're done here if ! is_thinkpad; then # laptop not supported _tpacpi=253 _tpsmapi=253 return 0 fi # --- 2. check for acpi-call external kernel module and test with integrated tpacpi-bat [ThinkPads only] if ! supports_tpacpi; then _tpacpi=254 elif [ $_natacpi -eq 0 ]; then # tpacpi-bat superseded by natacpi: _tpacpi=255 from above : elif [ ! -e /proc/acpi/call ]; then # call API not present if $MODINFO acpi_call > /dev/null 2>&1; then # module installed but not loaded _tpacpi=64 else # module neither installed nor builtin _tpacpi=128 fi else # call API present --> try tpacpi-bat $TPACPIBAT -g FD 1 > /dev/null 2>&1 _tpacpi=$? if [ $_tpacpi -eq 0 ] && [ "$TPACPI_ENABLE" = "0" ]; then # tpacpi disabled by configuration _tpacpi=32 fi if [ $_tpacpi -eq 0 ]; then # tpacpi available --> fill in methods depending on natacpi results case $_natacpi in 1) # discharge needed _bm_dischg="tpacpi" ;; *) # thresholds and discharge needed _bm_thresh="tpacpi" _bm_dischg="tpacpi" ;; esac fi fi echo_debug "bat" "check_battery_features.tpacpi: $_tpacpi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)" # --- 3. check for tp-smapi external kernel module [ThinkPads only] if [ -d $SMAPIBATDIR ]; then # module loaded --> tp-smapi available if [ "$TPSMAPI_ENABLE" = "0" ]; then # tpsmapi disabled by configuration _tpsmapi=32 elif supports_tpsmapi_and_tpacpi; then # readonly _tpsmapi=1 else # enabled (default) _tpsmapi=0 # fill in missing methods [ "$_bm_thresh" = "none" ] && _bm_thresh="tpsmapi" [ "$_bm_dischg" = "none" ] && _bm_dischg="tpsmapi" fi # reading battery data via tpsmapi is preferred over natacpi # because it provides cycle count and more _bm_read="tpsmapi" elif ! supports_tpsmapi_only && ! supports_tpsmapi_and_tpacpi || supports_no_tp_bat_funcs; then # not tp-smapi capable models _tpsmapi=254 elif $MODINFO tp_smapi > /dev/null 2>&1; then # module installed but not loaded _tpsmapi=64 else # module neither installed nor builtin _tpsmapi=128 fi echo_debug "bat" "check_battery_features.tpsmapi: $_tpsmapi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)" return 0 } # --- Battery Detection battery_present () { # check battery presence and return tpacpi-bat index # $1: BAT0/BAT1/DEF/other # global param: $_bm_read # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1; # $_bat_idx: 1/2; # $_bd_read: directory with battery data sysfiles; # $_bf_start: sysfile for start threshold; # $_bf_stop: sysfile for stop threshold; # $_bf_dischg: sysfile for force discharge # defaults local rc=1 # bat nonexistent _bat_idx=0 # no index _bat_str="" # no bat _bd_read="" # no directories _bf_start="" _bf_stop="" _bf_dischg="" local blist bs bsd # load modules and check prerequisites check_thinkpad check_battery_features # validate param case $1 in DEF) blist="BAT0 BAT1" ;; *) blist="$1" ;; esac case $_bm_read in natacpi) # note: includes tpacpi for bs in $blist; do bsd="$ACPIBATDIR/$bs" # check acpi name space if printf '%s\n' "$bs" | grep -E -q "$RE_PS_IGNORE"; then rc=2 # atypical battery ignored elif [ "$(read_sysf $bsd/present)" = "1" ] \ && [ "$(read_sysf $bsd/type)" = "Battery" ]; then rc=0 # battery detected # determine tpacpi-bat index case $bs in BAT0) _bat_str="$bs" _bd_read="$bsd" # tpacpi: BAT0 is always assumed main battery [ $_tpacpi -eq 0 ] && _bat_idx=1 ;; BAT1) _bat_str="$bs" _bd_read="$bsd" if [ $_tpacpi -eq 0 ]; then # tpacpi: try to read start threshold for index 2 if $TPACPIBAT -g ST 2 2> /dev/null 1>&2 ; then _bat_idx=2 # BAT1 is aux else _bat_idx=1 # BAT1 is main fi fi ;; *) # non-featured battery --> data read only _bat_str="$bs" _bd_read="$bsd" ;; esac break # quit loop on first battery detected fi done ;; # natacpi tpsmapi) rc=1 for bs in $blist; do bsd="$SMAPIBATDIR/$bs" # check tp-smapi name space if [ "$(read_sysf $bsd/installed)" = "1" ]; then rc=0 # battery detected case $bs in BAT0) _bat_str="$bs"; _bd_read="$bsd" ; _bat_idx=1 ;; BAT1) _bat_str="$bs"; _bd_read="$bsd" ; _bat_idx=2 ;; esac break # quit loop on first battery detected fi done ;; # tpsmapi esac if [ -n "$_bat_str" ]; then case $_bm_thresh in natacpi) _bf_start="$ACPIBATDIR/$_bat_str/charge_start_threshold" _bf_stop="$ACPIBATDIR/$_bat_str/charge_stop_threshold" ;; tpsmapi) _bf_start="$SMAPIBATDIR/$_bat_str/start_charge_thresh" _bf_stop="$SMAPIBATDIR/$_bat_str/stop_charge_thresh" ;; esac case $_bm_dischg in natacpi) _bf_dischg="$ACPIBATDIR/$_bat_str/force_discharge" ;; tpsmapi) _bf_dischg="$SMAPIBATDIR/$_bat_str/force_discharge" ;; esac fi case $rc in 0) echo_debug "bat" "battery_present($1): bm_read=$_bm_read; bat_str=$_bat_str; bat_idx=$_bat_idx; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop; bf_dischg=$_bf_dischg; rc=$rc" ;; 1) echo_debug "bat" "battery_present($1).not_detected: bm_read=$_bm_read; rc=$rc" ;; 2) echo_debug "bat" "battery_present($1).ignored: bm_read=$_bm_read; rc=$rc" ;; esac return $rc } # --- Battery Charge Thresholds read_threshold () { # read and echo charge threshold # $1: start/stop # global param: $_bm_thresh, $_bat_idx, $_bf_start, $_bf_stop # rt: threshold (1..100, "none"=error) local bsys rt tprc case $_bm_thresh in natacpi|tpsmapi) case $1 in start) bsys=$_bf_start ;; stop) bsys=$_bf_stop ;; esac # get effective threshold, "none" if not readable/non-existent rt=$(read_sysf $bsys "none") ;; # natacpi, tpsmapi tpacpi) # use tpacpi-bat if [ $_bat_idx -ne 0 ]; then # bat index is valid case $1 in start) rt=$($TPACPIBAT -g ST $_bat_idx 2> /dev/null | cut -f1 -d' ') ;; stop) rt=$($TPACPIBAT -g SP $_bat_idx 2> /dev/null | cut -f1 -d' ') ;; esac tprc=$? if [ $tprc -eq 0 ] && is_uint "$rt"; then if [ $rt -ge 128 ]; then # Remove offset of 128 for Edge S430 et al. rt=$((rt - 128)) fi else rt="none" fi else # bat index is invalid rt="none" fi ;; # tpacpi *) # invalid threshold method rt="none" ;; esac # replace 0 with factory default values if [ $rt = "0" ]; then case $1 in start) rt=96 ;; stop) rt=100 ;; esac fi # "return" threshold if [ "$X_THRESH_READ_NONE" != "1" ]; then echo $rt else echo "none" fi echo_debug "bat" "read_threshold($1): bm_thresh=$_bm_thresh; bat_idx=$_bat_idx; thresh=$rt" return 0 } write_thresholds () { # write both charge thresholds for a battery, # use pre-determined method from global parms, set by battery_present() # $1: BAT0/BAT1, # $2: new start treshold, $3: new stop threshold, # $4: 0=quiet/1=output progress and error messages # global param: $_bm_thresh, $_bat_str, $_bat_idx, $_bf_start, $_bf_stop # rc: 0=ok/ # 16=write error/ # 254=thresholds not supported # # prerequisite: check_battery_features() local verb=${4:-0} local new_start new_stop old_start old_stop tseq # read active threshold values old_start=$(read_threshold start) old_stop=$(read_threshold stop) if [ "$old_start" != "none" ] && [ "$old_stop" != "none" ] \ && [ $old_start -ge $old_stop ]; then # invalid threshold reading, happens on ThinkPad E/L series old_start="none" old_stop="none" fi # evaluate threshold args: replace empty string with "none", # which means don't change this threshold new_start=${2:-none} new_stop=${3:-none} # determine write sequence because driver's intrinsic boundary conditions # must be met in all write stages: # - natacpi: start <= stop (write fails if not met) # - tpacpi: nothing (maybe BIOS enforces something) # - tpsmapi: start <= stop - $_tc_diff (changes value for compliance) if [ "$new_start" != "none" ] && [ "$old_stop" != "none" ] \ && [ $new_start -gt $((old_stop - _tc_diff)) ]; then tseq="stop start" else tseq="start stop" fi # write new thresholds in determined sequence local rc=0 step steprc if [ "$verb" = "1" ]; then echo "Setting temporary charge thresholds for $_bat_str:" fi for step in $tseq; do local old_thresh new_thresh case $step in start) old_thresh=$old_start new_thresh=$new_start ;; stop) old_thresh=$old_stop new_thresh=$new_stop ;; esac [ "$new_thresh" = "none" ] && continue # don't change this threshold if [ "$old_thresh" != "$new_thresh" ]; then # new threshold differs from effective one --> write it case $_bm_thresh in natacpi|tpsmapi) case $step in start) write_sysf "$new_thresh" $_bf_start ;; stop) write_sysf "$new_thresh" $_bf_stop ;; esac steprc=$?; [ $steprc -ne 0 ] && [ $rc -eq 0 ] && rc=16 ;; # natacpi, tpsmapi tpacpi) # replace factory default values with 0 for tpacpi-bat local nt ts case $step in start) ts="ST" if [ $new_thresh -eq 96 ]; then nt=0 else nt=$new_thresh fi ;; stop) ts="SP" if [ $new_thresh -eq 100 ]; then nt=0 else nt=$new_thresh fi ;; esac $TPACPIBAT -s $ts $_bat_idx $nt > /dev/null 2>&1; steprc=$?; [ $steprc -ne 0 ] && [ $rc -eq 0 ] && rc=16 ;; # tpacpi *) # invalid threshold method --> abort rc=254 break ;; esac echo_debug "bat" "write_thresholds($1, $2, $3, $4).$step.write: old=$old_thresh; new=$new_thresh; steprc=$steprc" if [ "$verb" = "1" ]; then if [ $steprc -eq 0 ]; then printf " %-5s = %3d\n" $step $new_thresh else printf " %-5s = %3d (Error: cannot set threshold)\n" $step $new_thresh 1>&2 fi fi else echo_debug "bat" "write_thresholds($1, $2, $3, $4).$step.no_change: old=$old_thresh; new=$new_thresh" if [ "$verb" = "1" ]; then printf " %-5s = %3d (no change)\n" $step $new_thresh fi fi done # for step echo_debug "bat" "write_thresholds($1, $2, $3, $4): rc=$rc" return $rc } validate_threshold_input () { # check threshold input from cmdline or configuration # $1: start threshold, $2: stop threshold [0..100]; # rc: 0=two valid thresholds/ # 1=one threshold given/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=no thresholds given/ # 254=thresholds not supported # retval: $_start_thresh, $_stop_thresh: 0..100, "none" # # Check the following assertions: # (1) numeric # (2) within boundaries: # $_tc_start_min <= start <= $_tc_start_max # $_tc_stop_min <= stop <= $_tc_stop_max # (3) minimum difference: start + $_tc_diff <= stop # # prerequisite: check_battery_features() # replaces: normalize_thresholds # defaults _start_thresh="none" _stop_thresh="none" # no threshold method --> quit [ $_bm_thresh != "none" ] || return 254 local thresh type local rc=0 vcnt=0 for type in start stop; do case $type in start) thresh=$1 ;; stop) thresh=$2 ;; esac # check for 3 digits max if is_uint "$thresh" 3; then # numeric input --> ensure min/max, insert default value case $type in start) if is_within_bounds $thresh $_tc_start_min $_tc_start_max; then _start_thresh=$thresh vcnt=$((vcnt + 1)) elif [ $thresh -eq $_tc_default ]; then _start_thresh=$_tc_start_max vcnt=$((vcnt + 1)) else # threshold out of range rc=2 fi ;; stop) if is_within_bounds $thresh $_tc_stop_min $_tc_stop_max; then _stop_thresh=$thresh vcnt=$((vcnt + 1)) elif [ $thresh -eq $_tc_default ]; then _stop_thresh=$_tc_stop_max vcnt=$((vcnt + 1)) else # threshold out of range rc=2 fi ;; esac elif [ -n "$thresh" ]; then # threshold non-numeric rc=2 fi done # type if [ $rc -eq 0 ]; then # catch unconfigured thresholds case $vcnt in 1) rc=1 ;; # only one threshold given 0) rc=4 ;; # no thresholds given esac fi if [ $rc -eq 0 ] && [ $((_start_thresh + _tc_diff)) -gt $_stop_thresh ]; then # minimum start stop diff violated rc=3 fi echo_debug "bat" "validate_threshold_input($1, $2): start=$_start_thresh; stop=$_stop_thresh; rc=$rc" return $rc } set_charge_thresholds () { # write all charge thresholds from configuration # rc: 0 if battery_present BAT0; then if validate_threshold_input "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0"; then write_thresholds BAT0 $_start_thresh $_stop_thresh 0 fi fi if battery_present BAT1; then if validate_threshold_input "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1"; then write_thresholds BAT1 $_start_thresh $_stop_thresh 0 fi fi return 0 } print_setcharge_usage () { printf "Usage: setcharge BAT\n" 1>&2 } setcharge_battery () { # write charge thresholds (called from cmd line) # $1: start charge threshold, $2: stop charge threshold, $3: battery # rc: 0=ok/ # 1=only one threshold given/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=no thresholds given/ # 8=bat non-existent/ # 16=write error/ # 255=no thresh api local bat rc start_thresh stop_thresh vrc local use_cfg=0 # $_bat_str is global for cancel_force_discharge() trap # check params case $# in 0) # no args bat=DEF # use default(1st) battery use_cfg=1 # use configured values ;; 1) # assume $1 is battery bat=$1 use_cfg=1 # use configured values ;; 2) # assume $1,$2 are thresholds start_thresh=$1 stop_thresh=$2 bat=DEF # use default(1st) battery ;; 3) # assume $1,$2 are thresholds, $3 is battery start_thresh=$1 stop_thresh=$2 bat=$3 ;; esac # convert bat to uppercase bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") # check bat presence and/or get default(1st) battery battery_present $bat case $? in 0) # battery present if [ "$_bm_thresh" = "none" ]; then # no method available --> quit printf "Error: battery charge thresholds not available.\n" 1>&2 echo_debug "bat" "setcharge_battery.no_method" return 255 fi # get configured values if requested if [ $use_cfg -eq 1 ]; then eval start_thresh="\$START_CHARGE_THRESH_${_bat_str}" eval stop_thresh="\$STOP_CHARGE_THRESH_${_bat_str}" fi ;; *) # not present printf "Error: battery %s not present.\n" "$bat" 1>&2 echo_debug "bat" "setcharge_battery.not_present($bat)" return 8 ;; esac # check thresholds validate_threshold_input "$start_thresh" "$stop_thresh"; vrc=$? case $vrc in 0) # two valid thresholds --> write (verbose mode) write_thresholds $_bat_str $_start_thresh $_stop_thresh 1; rc=$? ;; 1) # only one threshold given (can not happen with command line args # because of arg# checks above) if [ $use_cfg -eq 1 ]; then printf "Error: incomplete threshold configuration (start: %s, stop: %s).\n" \ "$_start_thresh" "$_stop_thresh" 1>&2 echo_debug "bat" "setcharge_battery.config_incomplete" fi rc=$vrc ;; 2) # threshold(s) out of range or non-numeric if [ $use_cfg -eq 1 ]; then printf "Error: configured threshold(s) out of range or non-numeric (start: %s, stop: %s).\n" \ "$start_thresh" "$stop_thresh" 1>&2 else printf "Error: threshold argument(s) out of range or non-numeric.\n" 1>&2 fi printf "Valid ranges for thresholds are: start %d..%d, stop %d..%d.\n" \ "${_tc_start_min}" "${_tc_start_max}" "${_tc_stop_min}" "${_tc_stop_max}" 1>&2 [ $use_cfg -ne 1 ] && print_setcharge_usage echo_debug "bat" "setcharge_battery.out_of_range" rc=$vrc ;; 3) # minimum start stop diff violated if [ $use_cfg -eq 1 ]; then printf "Error: invalid threshold configuration (start: %s, stop: %s).\n" \ "$start_thresh" "$stop_thresh" 1>&2 else printf "Error: invalid threshold arguments.\n" 1>&2 fi printf "Start threshold must not exceed stop threshold - %d%%.\n" "$_tc_diff" 1>&2 [ $use_cfg -ne 1 ] && print_setcharge_usage echo_debug "bat" "setcharge_battery.min_diff_violated" rc=$vrc ;; 4) # no thresholds given e.g. not configured (can not happen with # command line args because of arg# checks above) if [ $use_cfg -eq 1 ]; then printf "Error: no threshold configuration.\n" 1>&2 echo_debug "bat" "setcharge_battery.not_configured" fi rc=$vrc ;; esac return $rc } chargeonce_battery () { # charge battery to upper threshold once # $1: battery # rc: 0=ok/1=error local bat stop_thresh start_thresh temp_start local efull=0 local enow=0 local ccharge=0 # check params if [ $# -gt 0 ]; then # some parameters given, check them # get battery arg bat=${1:-DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") else # no parameters given, use default(1st) battery bat=DEF fi # check if selected battery is present battery_present $bat case $? in 0) # battery present if [ "$_bm_thresh" = "none" ]; then # no method available --> quit echo "Error: battery charge thresholds not available." 1>&2 echo_debug "bat" "chargeonce_battery.no_method" return 1 fi ;; *) # not present echo "Error: battery $_bat_str not present." 1>&2 echo_debug "bat" "chargeonce_battery.not_present($_bat_str)" return 1 ;; esac # get thresholds from configuration eval start_thresh="\$START_CHARGE_THRESH_${_bat_str}" eval stop_thresh="\$STOP_CHARGE_THRESH_${_bat_str}" # check thresholds if ! validate_threshold_input "$start_thresh" "$stop_thresh"; then printf "Error: incomplete or invalid threshold configuration for %s (start: %s, stop: %s).\n" \ "$_bat_str" "$_start_thresh" "$_stop_thresh" 1>&2 echo_debug "bat" "chargeonce_battery($_bat_str).config_invalid" return 1 fi # get current charge level (in %) case $_bm_read in natacpi|tpacpi) # use ACPI sysfiles if [ -f $_bd_read/energy_full ]; then efull=$(read_sysval $_bd_read/energy_full) enow=$(read_sysval $_bd_read/energy_now) fi if is_uint "$enow" && is_uint "$efull" && [ $efull -ne 0 ]; then # calculate charge level rounded to integer ccharge=$(perl -e 'printf("%.0f\n", 100.0 * '$enow' / '$efull')') else ccharge=-1 fi ;; # natacpi, tpacpi tpsmapi) # use tp-smapi sysfile ccharge=$(read_sysval $_bd_read/remaining_percent) ;; # tpsmapi *) # invalid read method rc=255 ;; esac if [ $ccharge -eq -1 ] ; then printf "Error: cannot determine the charge level for %s.\n" "$_bat_str" 1>&2 echo_debug "bat" "chargeonce_battery($_bat_str).charge_level_unknown: enow=$enow; efull=$efull; ccharge=$ccharge" return 1 else echo_debug "bat" "chargeonce_battery($_bat_str).charge_level: enow=$enow; efull=$efull; ccharge=$ccharge" fi temp_start=$(( _stop_thresh - _tc_diff )) if [ $ccharge -gt $temp_start ] ; then printf "Error: the %s charge level is %s%%.\n" "$_bat_str" "$ccharge" 1>&2 printf "For this command to work, it must not exceed %s%% (configured stop threshold - %s%%).\n" \ "$temp_start" "$_tc_diff" 1>&2 echo_debug "bat" "chargeonce_battery($_bat_str).charge_level_too_high: $temp_start $stop_thresh" return 1 else echo_debug "bat" "chargeonce_battery($_bat_str).setcharge: $temp_start $_stop_thresh" fi write_thresholds $_bat_str $temp_start $_stop_thresh 1 return $? } # --- Battery Forced Discharge echo_discharge_locked () { # print "locked" message echo "Error: another discharge/recalibrate operation is pending." 1>&2 return 0 } get_force_discharge () { # $1: BAT0/BAT1, # global param: $_bm_dischg, $_bat_idx, $_bf_dischg # rc: 0=off/1=on/2=discharge not present/255=no thresh api local bsys rc=0 case $_bm_dischg in natacpi|tpsmapi) # read sysfile, 2 if non-existent rc=$(read_sysf $_bf_dischg 2) ;; # natacpi, tpsmapi tpacpi) # read via tpacpi-bat case $($TPACPIBAT -g FD $_bat_idx 2> /dev/null) in yes) rc=1 ;; no) rc=0 ;; *) rc=2 ;; esac ;; # tpacpi *) # invalid discharge method rc=255 ;; esac echo_debug "bat" "get_force_discharge($1): bm_dischg=$_bm_dischg; bat_idx=$_bat_idx; rc=$rc" return $rc } set_force_discharge () { # write force discharge state # $1: BAT0/BAT1, $2: 0=off/1=on # global param: $_bm_dischg, $_bat_idx, $_bf_dischg # rc: 0=done/1=write error/2=discharge not present/255=no thresh api local rc=0 case $_bm_dischg in natacpi|tpsmapi) if [ -f "$_bf_dischg" ]; then # write force_discharge write_sysf "$2" $_bf_dischg; rc=$? else # sysfile non-existent, possibly invalid bat argument rc=2 fi ;; # natacpi, tpsmapi tpacpi) # use tpacpi-bat $TPACPIBAT -s FD $_bat_idx $2 > /dev/null 2>&1; rc=$? ;; # tpcpaci *) # invalid discharge method rc=255 ;; esac echo_debug "bat" "set_force_discharge($1, $2): bm_dischg=$_bm_dischg; bat_idx=$_bat_idx; rc=$rc" return $rc } cancel_force_discharge () { # called from trap -- global param: $_bat_str set_force_discharge $_bat_str 0 unlock_tlp tlp_discharge echo_debug "bat" "force_discharge.cancelled($_bat_str)" echo " Cancelled." do_exit 0 } battery_discharging () { # check if battery is discharging -- $1: BAT0/BAT1, # global param: $_bm_read, $_bd_read # rc: 0=discharging/1=not discharging/255=no battery api local bsys rc=255 # determine status sysfile case $_bm_read in natacpi|tpacpi) bsys=$_bd_read/status # use ACPI sysfile ;; tpsmapi) bsys=$_bd_read/state # use tpsmapi sysfile ;; *) # invalid read method bsys="" rc=255 ;; esac # get battery state if [ -f "$bsys" ]; then case "$(read_sysf $bsys)" in [Dd]ischarging) rc=0 ;; *) rc=1 ;; esac fi echo_debug "bat" "battery_discharging($1): bm_read=$_bm_read; rc=$rc" return $rc } discharge_battery () { # discharge battery # $1: battery # global param: $_tpacpi, $_tpsmapi # rc: 0=ok/1=error local bat en ef pn rc rp wt # $_bat_str is global for cancel_force_discharge() trap # check params bat=$1 bat=${bat:=DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") # check if selected battery is present battery_present $bat case $? in 0) # battery present if [ "$_bm_dischg" = "none" ]; then # no method available --> quit echo "Error: battery discharge/recalibrate not available." 1>&2 echo_debug "bat" "discharge_battery.no_method" return 1 fi ;; *) # not present echo "Error: battery $bat not present." 1>&2 echo_debug "bat" "discharge_battery.not_present($bat)" return 1 ;; esac # start discharge set_force_discharge $_bat_str 1; rc=$? if [ $rc -ne 0 ]; then echo_debug "bat" "discharge_battery.force_discharge_malfunction($_bat_str)" echo "Error: discharge malfunction." 1>&2 return 1 fi trap cancel_force_discharge INT # enable ^C hook rc=0; rp=0 # wait for start == while status not "discharging" -- 15.0 sec timeout printf "Initiating discharge of battery %s " $_bat_str wt=15 while ! battery_discharging $_bat_str && [ $wt -gt 0 ] ; do sleep 1 printf "." wt=$((wt - 1)) done printf "\n" if battery_discharging $_bat_str; then # discharge initiated sucessfully --> wait for completion == while status "discharging" echo_debug "bat" "discharge_battery.running($_bat_str)" while battery_discharging $_bat_str; do clear echo "Currently discharging battery $_bat_str:" # show current battery state case $_bm_read in natacpi|tpacpi) # use ACPI sysfiles perl -e 'printf ("voltage = %6d [mV]\n", '"$(read_sysval $_bd_read/voltage_now)"' / 1000.0);' en=$(read_sysval $_bd_read/energy_now) perl -e 'printf ("remaining capacity = %6d [mWh]\n", '$en' / 1000.0);' ef=$(read_sysval $_bd_read/energy_full) if [ "$ef" != "0" ]; then rp=$(perl -e 'printf ("%d", 100.0 * '$en' / '$ef' );') perl -e 'printf ("remaining percent = %6d [%%]\n", '$rp' );' else printf "remaining percent = not available [%%]\n" rp=0 fi pn=$(read_sysval $_bd_read/power_now) if [ "$pn" != "0" ]; then perl -e 'printf ("remaining time = %6d [min]\n", 60.0 * '$en' / '$pn');' perl -e 'printf ("power = %6d [mW]\n", '$pn' / 1000.0);' else printf "remaining time = not discharging [min]\n" fi printf "state = %s\n" "$(read_sysf $_bd_read/status)" ;; # natacpi, tpsmapi tpsmapi) # use tp-smapi sysfiles printf "voltage = %6s [mV]\n" "$(read_sysf $_bd_read/voltage)" printf "remaining capacity = %6s [mWh]\n" "$(read_sysf $_bd_read/remaining_capacity)" rp=$(read_sysf $_bd_read/remaining_percent) printf "remaining percent = %6s [%%]\n" "$rp" printf "remaining time = %6s [min]\n" "$(read_sysf $_bd_read/remaining_running_time_now)" printf "power = %6s [mW]\n" "$(read_sysf $_bd_read/power_avg)" printf "state = %s\n" "$(read_sysf $_bd_read/state)" ;; # tpsmapi esac get_force_discharge $_bat_str; printf "force discharge = %s\n" "$?" echo "Press Ctrl+C to cancel." sleep 5 done if [ $rp -gt 0 ]; then # battery not emptied echo_debug "bat" "discharge_battery.not_emptied($_bat_str)" echo "Error: battery $_bat_str was not discharged completely. Check your hardware." 1>&2 rc=2 fi else # discharge malfunction --> cancel discharge and abort set_force_discharge $_bat_str 0; echo_debug "bat" "discharge_battery.malfunction($_bat_str)" echo "Error: discharge $_bat_str malfunction." 1>&2 rc=1 fi trap - INT # remove ^C hook # ThinkPad E-series firmware may keep force_discharge active --> cancel it ! get_force_discharge $_bat_str && set_force_discharge $_bat_str 0 if [ $rc -eq 0 ]; then echo echo "Done: battery $_bat_str was completely discharged." echo_debug "bat" "discharge_battery.complete($_bat_str)" fi return $rc } TLP-1.3.1/func.d/40-tlp-func-bay000066400000000000000000000142661361705037700160170ustar00rootroot00000000000000#!/bin/sh # tlp-func-bay - Bay Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 15-tlp-func-disk # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly DOCKGLOB="/sys/devices/platform/dock.?" readonly BAYSTATEFILE=$RUNDIR/bay_saved # ---------------------------------------------------------------------------- # Functions # --- Drive Bay get_drivebay_device () { # Find generic dock interface for drive bay # rc: 0; retval: $dock dock=$(grep -l 'ata_bay' $DOCKGLOB/type 2> /dev/null) dock=${dock%%/type} if [ ! -d "$dock" ]; then dock="" fi return 0 } check_is_docked() { # check if $dock is docked; # rc: 0 if docked, else 1 local dock_status dock_info_file # return 0 if any sysfs file indicates "docked" for dock_info_file in docked firmware_node/status; do if [ -f $dock/$dock_info_file ] && \ read -r dock_status < $dock/$dock_info_file 2>/dev/null; then # catch empty $dock_status (safety check, unlikely case) [ "${dock_status:-0}" != "0" ] && return 0 fi done # otherwise assume "not docked" return 1 } poweroff_drivebay () { # power off optical drive in drive bay # $1: 0=ac mode, 1=battery mode # $2: 0=conditional+quiet mode, 1=force+verbose mode # Some code adapted from https://www.thinkwiki.org/wiki/How_to_hotswap_UltraBay_devices local pwr optdrv syspath if [ "$1" = "1" ]; then pwr="$BAY_POWEROFF_ON_BAT" else pwr="$BAY_POWEROFF_ON_AC" fi # Run only if forced or enabled if [ "$2" != "1" ]; then case "$pwr" in 1) # enabled --> proceed ;; 0) # disabled echo_debug "pm" "poweroff_drivebay($1).disabled" return 0 ;; *) # not configured or invalid parameter echo_debug "pm" "poweroff_drivebay($1).not_configured" return 0 ;; esac fi get_drivebay_device if [ -z "$dock" ] || [ ! -d "$dock" ]; then echo_debug "pm" "poweroff_drivebay($1).no_bay_device" [ "$2" = "1" ] && echo "Error: cannot locate bay device." 1>&2 return 1 fi echo_debug "pm" "poweroff_drivebay($1): dock=$dock" # Check if bay is occupied if ! check_is_docked; then echo_debug "pm" "poweroff_drivebay($1).drive_already_off" [ "$2" = "1" ] && echo "No drive in bay (or power already off)." else # Check for optical drive optdrv="$BAY_DEVICE" if [ -z "$optdrv" ]; then echo_debug "pm" "poweroff_drivebay($1).opt_drive_not_configured" [ "$2" = "1" ] && echo "Error: no optical drive configured (BAY_DEVICE=\"\")." 1>&2 return 1 elif [ ! -b "/dev/$optdrv" ]; then echo_debug "pm" "poweroff_drivebay($1).no_opt_drive: /dev/$optdrv" [ "$2" = "1" ] && echo "No optical drive in bay (/dev/$optdrv)." return 0 else echo_debug "pm" "poweroff_drivebay($1): optdrv=$optdrv" [ "$2" = "1" ] && echo -n "Powering off drive bay..." # Unmount media umount -l $optdrv > /dev/null 2>&1 # Sync drive sync sleep 1 # Power off drive $HDPARM -Y $optdrv > /dev/null 2>&1 sleep 5 # Unregister scsi device if syspath="$($UDEVADM info --query=path --name=$optdrv)"; then syspath="/sys${syspath%/block/*}" if [ "$syspath" != "/sys" ]; then write_sysf "1" $syspath/delete echo_debug "pm" "poweroff_drivebay($1): syspath=$syspath; rc=$?" else echo_debug "pm" "poweroff_drivebay($1): got empty/invalid syspath for $optdrv" fi else echo_debug "pm" "poweroff_drivebay($1): failed to get syspath (udevadm returned $?)" fi # Turn power off write_sysf "1" $dock/undock echo_debug "pm" "poweroff_drivebay($1).bay_powered_off: rc=$?" [ "$2" = "1" ] && echo "done." fi fi return 0 } suspend_drivebay () { # Save power state of drive bay before suspend # $1: 0=ac mode, 1=battery mode if [ "$1" = "1" ] && [ "$BAY_POWEROFF_ON_BAT" = "1" ] || \ [ "$1" = "0" ] && [ "$BAY_POWEROFF_ON_AC" = "1" ]; then # setting corresponding to mode is active -> save state get_drivebay_device if [ -n "$dock" ]; then create_rundir if ! check_is_docked; then write_sysf "off" $BAYSTATEFILE echo_debug "pm" "suspend_drivebay($1): bay=off; rc=$?" else write_sysf "on" $BAYSTATEFILE echo_debug "pm" "suspend_drivebay($1): bay=on; rc=$?" fi fi else # setting not active -> remove state file rm -f $BAYSTATEFILE 2> /dev/null fi return 0 } resume_drivebay () { # # $1: 0=ac mode, 1=battery mode local cnt rc if [ "$(read_sysf $BAYSTATEFILE)" = "off" ]; then # saved state = off get_drivebay_device if [ -n "$dock" ]; then if check_is_docked; then # device active -> deactivate if [ -e $dock/undock ]; then cnt=5 rc=1 until [ $rc = 0 ] || [ $cnt = 0 ]; do cnt=$((cnt - 1)) { printf '%s\n' "1" > $dock/undock; } 2> /dev/null rc=$? [ $rc = 0 ] || sleep 0.5 done echo_debug "pm" "resume_drivebay.bay_off: rc=$rc" fi else echo_debug "pm" "resume_drivebay.already_off" fi fi else # No saved state or state != off --> apply settings poweroff_drivebay $1 0 fi rm -f $BAYSTATEFILE 2> /dev/null return 0 } TLP-1.3.1/func.d/45-tlp-func-gpu000066400000000000000000000123121361705037700160320ustar00rootroot00000000000000#!/bin/sh # tlp-func-gpu - Intel GPU Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly BASE_MODD=/sys/module readonly BASE_DRMD=/sys/class/drm readonly BASE_DEBUGD=/sys/kernel/debug/dri readonly IGPU_MIN_FREQ=gt_min_freq_mhz readonly IGPU_MAX_FREQ=gt_max_freq_mhz readonly IGPU_BOOST_FREQ=gt_boost_freq_mhz readonly IGPU_FREQ_TABLE=i915_ring_freq_table readonly RADD=/sys/module/radeon # ---------------------------------------------------------------------------- # Functions # --- Intel GPU check_intel_gpu () { # detect Intel GPU presence and determine sysdirs # rc: 0=present/1=absent # retval: $_intel_gpu_parm: card parameter sysdir; # $_intel_gpu_drm: card drm sysdir; # $_intel_gpu_dbg: card debug sysdir local cardno driver gpu _intel_gpu_parm="" _intel_gpu_drm="" _intel_gpu_dbg="" for gpu in ${BASE_DRMD}/card?; do driver=$(readlink ${gpu}/device/driver) driver=${driver##*/} case $driver in i915*) # Intel GPU found cardno=${gpu##${BASE_DRMD}/card} _intel_gpu_parm=${BASE_MODD}/${driver}/parameters _intel_gpu_drm=${gpu} _intel_gpu_dbg=${BASE_DEBUGD}/${cardno} echo_debug "pm" "check_intel_gpu.present: parm=$_intel_gpu_parm; drm=$_intel_gpu_drm; dbg=$_intel_gpu_dbg" return 0 ;; esac done # no Intel GPU found echo_debug "pm" "check_intel_gpu.no_card" return 1 } set_intel_gpu_min_max_boost_freq () { # set gpu frequency limits -- $1: 0=ac mode, 1=battery mode local minfreq maxfreq boostfreq local conf=0 check_intel_gpu || return 0 if [ "$1" = "1" ]; then minfreq=${INTEL_GPU_MIN_FREQ_ON_BAT:-} maxfreq=${INTEL_GPU_MAX_FREQ_ON_BAT:-} boostfreq=${INTEL_GPU_BOOST_FREQ_ON_BAT:-} else minfreq=${INTEL_GPU_MIN_FREQ_ON_AC:-} maxfreq=${INTEL_GPU_MAX_FREQ_ON_AC:-} boostfreq=${INTEL_GPU_BOOST_FREQ_ON_AC:-} fi if [ -n "$minfreq" ] && [ "$minfreq" != "0" ]; then write_sysf "$minfreq" $_intel_gpu_drm/$IGPU_MIN_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min: $minfreq; rc=$?" conf=1 fi if [ -n "$maxfreq" ] && [ "$maxfreq" != "0" ]; then write_sysf "$maxfreq" $_intel_gpu_drm/$IGPU_MAX_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max: $maxfreq; rc=$?" conf=1 fi if [ -n "$boostfreq" ] && [ "$boostfreq" != "0" ]; then write_sysf "$boostfreq" $_intel_gpu_drm/$IGPU_BOOST_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).boost: $boostfreq; rc=$?" conf=1 fi [ $conf -eq 1 ] || echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).not_configured" return 0 } # --- AMD Radeon GPU set_radeon_profile () { # set radeon power profile # $1: 0=ac mode, 1=battery mode local card level pwr rc1 rc2 local sdone=0 # 1=radeon present if [ ! -d $RADD ]; then # No card present --> quit echo_debug "pm" "set_radeon_profile($1).no_card" return 0 fi for card in /sys/class/drm/card[0-9]/device ; do if [ -f $card/power_dpm_state ] && [ -f $card/power_dpm_force_performance_level ]; then # Use new radeon dynamic power management method (dpm) if [ "$1" = "1" ]; then pwr=${RADEON_DPM_STATE_ON_BAT:-} level=${RADEON_DPM_PERF_LEVEL_ON_BAT:-} else pwr=${RADEON_DPM_STATE_ON_AC:-} level=${RADEON_DPM_PERF_LEVEL_ON_AC:-} fi if [ -z "$pwr" ] || [ -z "$level" ]; then # do nothing if (partially) unconfigured echo_debug "pm" "set_radeon_profile($1).not_configured: $card" return 0 else write_sysf "$pwr" $card/power_dpm_state; rc1=$? write_sysf "$level" $card/power_dpm_force_performance_level; rc2=$? echo_debug "pm" "set_radeon_profile($1): $card: state=${pwr}: rc=$rc1; perf=${level}: rc=$rc2" fi sdone=1 elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then # Use old radeon power profile method if [ "$1" = "1" ]; then pwr=${RADEON_POWER_PROFILE_ON_BAT:-} else pwr=${RADEON_POWER_PROFILE_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_radeon_profile($1).not_configured: $card" return 0 else write_sysf "profile" $card/power_method; rc1=$? write_sysf "$pwr" $card/power_profile; rc2=$? echo_debug "pm" "set_radeon_profile($1): $card; method=profile: rc=$rc1; profile=${pwr}: rc=$rc2" fi sdone=1 fi done if [ $sdone -eq 0 ]; then echo_debug "pm" "set_radeon_profile($1).not_available" fi return 0 } TLP-1.3.1/func.d/tlp-func-stat000066400000000000000000000500351361705037700157700ustar00rootroot00000000000000#!/bin/sh # tlp-func-stat - tlp-stat Helper Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 15-tlp-func-disk, 35-tlp-func-batt # shellcheck disable=SC1004,SC2059,SC2086,SC2154 # ---------------------------------------------------------------------------- # Constants readonly INITCTL=initctl readonly SESTATUS=sestatus readonly SMARTCTL=smartctl readonly SYSTEMCTL=systemctl readonly SYSTEMD=systemd readonly RE_AC_QUIRK='^UNDEFINED$' readonly RE_ATA_ERROR='ata[0-9]+: SError: {.*CommWake }' # ---------------------------------------------------------------------------- # Functions # --- Checks check_systemd () { # check if systemd is the active init system (PID 1) and systemctl is installed # rc: 0=yes, 1=no [ -d /run/systemd/system ] && cmd_exists $SYSTEMCTL } check_upstart () { # check if upstart is active init system (PID 1) # rc: 0=yes, 1=no cmd_exists $INITCTL && $INITCTL --version | grep -q upstart } check_openrc () { # check if openrc is the active init system (PID 1) # rc: 0=yes, 1=no [ -e /run/openrc/softlevel ] } check_ac_quirk () { # check for hardware known not to expose AC device # $1: model string; rc: 0=yes, 1=no printf '%s' "$1" | grep -E -q "${RE_AC_QUIRK}" } # --- Formatted Output printparm () { # formatted output of sysfile - general # $1: format, $2: sysfile, $3: n/a message, $4: cutoff local format="$1" local sysf="$2" local namsg="$3" local cutoff="$4" local val="" if val=$(read_sysf $sysf); then # sysfile read successful if [ -n "$cutoff" ]; then val=${val%$cutoff} fi fi if [ -z "$val" ]; then # replace empty value with n/a text if [ -n "$namsg" ]; then if [ "$namsg" != "_" ]; then # use specific n/a text format=$(echo $format | sed -r -e "s/##(.*)##/($namsg)/" -e "s/\[.*\]//") else # _ = skip sysf="" fi else # empty n/a text, use default text format=$(echo $format | sed -r -e "s/##(.*)##/(not available)/" -e "s/\[.*\]//") fi # output n/a text or skip [ -n "$sysf" ] && printf "$format\n" "$sysf" else # non empty value: strip delimiters from format str format=$(echo $format | sed -r "s/##(.*)##/\1/") printf "$format\n" "$sysf" "$val" fi return 0 } printparm_i915 () { # formatted output of sysfile - i915 kernel module variant # $1: sysfile; $2: alternative; $3: 1=psr/0=other local sysf val # Check if sysfile or alternative exist if [ -f "$1" ]; then sysf=$1 elif [ -f "$2" ]; then sysf=$2 else # neither file exists --> quit silently return 0 fi if val=$(read_sysf $sysf); then # sysfile exists and is actually readable, output content printf "%-44s = %2d " "$sysf" "$val" # Explain content if [ "$val" = "-1" ]; then echo "(use per-chip default)" else echo -n "(" if [ "$3" = "1" ]; then # enable_psr case $val in 0) echo -n "disabled" ;; 1) echo -n "enabled" ;; 2) echo -n "force link-standby mode" ;; 3) echo -n "force link-off mode" ;; *) echo -n "unknown" ;; esac else # other parms if [ $((val & 1)) -ne 0 ]; then echo -n "enabled" else echo -n "disabled" fi [ $((val & 2)) -ne 0 ] && echo -n " + deep" [ $((val & 4)) -ne 0 ] && echo -n " + deepest" fi echo ")" fi else # sysfile was not readable printf "%-44s = (not available)\n" "$sysf" fi return 0 } printparm_epb () { # formatted output of sysfile - Intel EPB variant # $1: sysfile local val strval if val=$(read_sysf $1); then # sysfile exists and is actually readable, output content printf "%-54s = %2d " "$1" "$val" # Convert distinct values to strings strval=$(echo $val | sed -r 's/^0/performance/; s/^4/balance_performance/; s/^6/default/; s/^8/balance_power/; s/^15/power/; s/[0-9]+//') if [ -n "$strval" ]; then printf "($strval) [EPB]\n" else printf " [EPB]\n" fi else # sysfile was not readable printf "%-54s = (not available) [EPB]\n" "$1" fi return 0 } print_sysf () { # formatted output of a sysfile # $1: format; $2: sysfile local val if val=$(read_sysf $2); then # sysfile readable printf "$1" "$val" else # sysfile not readable printf "$1" "(not available)" fi return 0 } print_sysf_trim () { # formatted output of a sysfile, trim leading and trailing # blanks -- $1: format; $2: sysfile local val if val=$(read_sysf $2); then # sysfile readable printf "$1" "$(printf "%s" "$val" | sed -r 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" else # sysfile not readable printf "$1" "(not available)" fi return 0 } print_file_modtime_and_age () { # show a file's last modification time # and age in secs -- $1: file local mtime age if [ -f "$1" ]; then mtime=$(date +%X -r $1) age=$(( $(date +%s) - $(date +%s -r $1) )) printf '%s, %6d sec(s) ago' "$mtime" "$age" else printf "unknown" fi } print_saved_powerstate () { # read and print saved state local sps sps="$(read_sysf $PWRRUNFILE)" case "$sps" in 0) printf "AC" ;; 1) printf "battery" ;; *) printf "unknown" ;; esac # check for manual mode get_manual_mode case "$_manual_mode" in 0|1) printf " (manual)\n" ;; *) # check for persistent mode if get_persist_mode && [ "$_persist_mode" = "$sps" ]; then printf " (persistent)\n" else printf "\n" fi ;; esac return 0 } print_selinux () { # print SELinux status and mode if cmd_exists $SESTATUS; then $SESTATUS | awk -F '[ \t\n]+' '/SELinux status:/ { printf "SELinux status = %s", $3 } ; \ /Current mode:/ { printf " (%s)", $3 }' printf "\n" fi } # --- Storage Devices print_disk_model () { # print disk model -- $1: dev local model vendor model=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Model Number' | \ cut -f2 -d: | sed -r 's/^ *//' ) if [ -z "$model" ]; then # hdparm -I not supported --> try udevadm approach vendor="$($UDEVADM info -q property /dev/$1 2>/dev/null | sed -n 's/^ID_VENDOR=//p')" model="$( $UDEVADM info -q property /dev/$1 2>/dev/null | sed -n 's/^ID_MODEL=//p' )" model=$(printf "%s %s" "$vendor" "$model" | sed -r 's/_/ /g; s/-//g; s/[[:space:]]+$//') fi printf '%s\n' "${model:-unknown}" return 0 } print_disk_firmware () { # print firmware version --- $1: dev local firmware firmware=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Firmware Revision' | \ cut -f2 -d: | sed -r 's/^ *//' ) printf '%s\n' "${firmware:-unknown}" return 0 } get_disk_state () { # get disk power state -- $1: dev; retval: $_disk_state _disk_state=$($HDPARM -C /dev/$1 2> /dev/null | awk -F ':' '/drive state is/ { gsub(/ /,"",$2); print $2; }') [ -z "$_disk_state" ] && _disk_state="(not available)" return 0 } get_disk_apm_level () { # get disk apm level -- $1: dev; rc: apm local apm apm=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Advanced power management level' | \ cut -f2 -d: | grep -E '^ *[0-9]+ *$') if [ -n "$apm" ]; then return $apm else return 0 fi } get_disk_trim_capability () { # check for trim capability # $1: dev; rc: 0=no, 1=yes, 254=no ssd device local trim if $HDPARM -I /dev/$1 2> /dev/null | grep -q 'Solid State Device'; then if $HDPARM -I /dev/$1 2> /dev/null | grep -q 'TRIM supported'; then trim=1 else trim=0 fi else trim=255 fi return $trim } check_ata_errors () { # check kernel log for ata errors # (possibly) caused by SATA_LINKPWR_ON_AC/BAT != max_performance # stdout: error count if wordinlist $SATA_LINKPWR_ON_BAT "min_power medium_power" || \ wordinlist $SATA_LINKPWR_ON_AC "min_power medium_power"; then # config values != max_performance exist --> check kernel log # count matching error lines dmesg | grep -E -c "${RE_ATA_ERROR}" 2> /dev/null else # no values in question configured echo "0" fi return 0 } show_disk_data () { # formatted output of NVMe / SATA disk data # $1: disk device # translate disk name and check presence if ! get_disk_dev $1; then # no block device for disk name --> we're done printf "\n%s: not present.\n" /dev/$_disk_dev return 1 fi # --- show general data case "$_disk_type" in nvme) # NVMe disk printf "\n%s:\n" /dev/$_disk_dev printf " Type = NVMe\n" [ -n "$_disk_id" ] && printf " Disk ID = %s\n" $_disk_id print_sysf " Model = %s\n" /sys/block/$_disk_dev/device/model print_sysf " Firmware = %s\n" /sys/block/$_disk_dev/device/firmware_rev # TODO: more features? ;; ata|usb|ieee1394) # ATA/USB/IEEE1394 disk printf "\n%s:\n" /dev/$_disk_dev [ -n "$_disk_id" ] && printf " Disk ID = %s\n" $_disk_id printf " Type = %s\n" "$(toupper $_disk_type)" # save spindle state get_disk_state $_disk_dev printf " Model = " print_disk_model $_disk_dev printf " Firmware = " print_disk_firmware $_disk_dev get_disk_apm_level $_disk_dev; local apm=$? printf " APM Level = " case $apm in 0|255) printf "none/disabled\n" ;; *) printf "%s" $apm if wordinlist "$_disk_type" "$DISK_TYPES_NO_APM_CHANGE"; then printf " (changes not supported)\n" else printf "\n" fi ;; esac printf " Status = %s\n" $_disk_state get_disk_trim_capability $_disk_dev; local trim=$? case $trim in 0) printf " TRIM = not supported\n" ;; 1) printf " TRIM = supported\n" ;; esac # restore standby state [ "$_disk_state" = "standby" ] && spindown_disk $_disk_dev ;; *) printf "\n%s: Device type \"%s\" ignored.\n" /dev/$_disk_dev $_disk_type return 1 ;; esac if [ -f /sys/block/$_disk_dev/queue/scheduler ]; then if [ -d /sys/block/$_disk_dev/mq ]; then print_sysf_trim " Scheduler = %s (multi queue)\n" /sys/block/$_disk_dev/queue/scheduler else print_sysf_trim " Scheduler = %s (single queue)\n" /sys/block/$_disk_dev/queue/scheduler fi fi local pdev=/sys/block/$_disk_dev/device/power if [ -f $pdev/control ]; then echo print_sysf " Runtime PM: control = %s, " $pdev/control print_sysf "autosuspend_delay_ms = %4s\n" $pdev/autosuspend_delay_ms fi # --- show SMART data # skip if smartctl not installed or disk not SMART capable cmd_exists $SMARTCTL && $SMARTCTL /dev/$_disk_dev > /dev/null 2>&1 || return 0 case "$_disk_type" in nvme) # NVMe disk printf "\n SMART info:\n" $SMARTCTL -A /dev/$_disk_dev | \ grep -E -e '^(Critical Warning|Temperature:|Available Spare)' \ -e '^(Percentage Used:|Data Units Written:|Power|Unsafe)' \ -e 'Integrity Errors' | \ sed 's/^/ /' ;; ata|usb) printf "\n SMART info:\n" $SMARTCTL -A /dev/$_disk_dev | grep -v '<==' | \ awk -F ' ' '$2 ~ /Power_Cycle_Count|Start_Stop_Count|Load_Cycle_Count|Reallocated_Sector_Ct/ \ { printf " %3d %-25s = %8d \n", $1, $2, $10 } ; \ $2 ~ /Used_Rsvd_Blk_Cnt_Chip|Used_Rsvd_Blk_Cnt_Tot|Unused_Rsvd_Blk_Cnt_Tot/ \ { printf " %3d %-25s = %8d \n", $1, $2, $10 } ; \ $2 ~ /Power_On_Hours/ \ { printf " %3d %-25s = %8d %s\n", $1, $2, $10, "[h]" } ; \ $2 ~ /Temperature_Celsius/ \ { printf " %3d %-25s = %8d %s %s %s %s\n", $1, $2, $10, $11, $12, $13, "[°C]" } ; \ $2 ~ /Airflow_Temperature_Cel/ \ { printf " %3d %-25s = %8d %s\n", $1, $2, $10, "[°C]" } ; \ $2 ~ /G-Sense_Error_Rate/ \ { printf " %3d %-25s = %8d \n", $1, $2, $10 } ; \ $2 ~ /Host_Writes/ \ { printf " %3d %-25s = %8.3f %s\n", $1, $2, $10 / 32768.0, "[TB]" } ; \ $2 ~ /Total_LBAs_Written/ \ { printf " %3d %-25s = %8.3f %s\n", $1, $2, $10 / 2147483648.0, "[TB]" } ; \ $2 ~ /NAND_Writes_1GiB/ \ { printf " %3d %-25s = %8d %s\n", $1, $2, $10, "[GB]" } ; \ $2 ~ /Available_Reservd_Space|Media_Wearout_Indicator|Wear_Leveling_Count/ \ { printf " %3d %-25s = %8d %s\n", $1, $2, $4, "[%]" }' ;; *) # unkown disk type ;; esac return 0 } # --- Graphics show_intel_gpu_data () { # show Intel GPU data check_intel_gpu || return 0 # power managment data echo "+++ Intel Graphics" printparm_i915 $_intel_gpu_parm/powersave printparm_i915 $_intel_gpu_parm/enable_rc6 $_intel_gpu_parm/i915_enable_rc6 printparm_i915 $_intel_gpu_parm/enable_dc printparm_i915 $_intel_gpu_parm/enable_fbc $_intel_gpu_parm/i915_enable_fbc printparm_i915 $_intel_gpu_parm/enable_psr "" 1 printparm_i915 $_intel_gpu_parm/lvds_downclock printparm_i915 $_intel_gpu_parm/modeset printparm_i915 $_intel_gpu_parm/semaphores echo # frequency parameters if readable_sysf $_intel_gpu_drm/$IGPU_MIN_FREQ; then printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_MIN_FREQ printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_MAX_FREQ printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_BOOST_FREQ if readable_sysf $_intel_gpu_dbg/$IGPU_FREQ_TABLE; then # available frequencies printf "%s: " $_intel_gpu_dbg/$IGPU_FREQ_TABLE awk -F ' ' '{ if (NR >= 2) { printf "%d ", $1 } }; ' $_intel_gpu_dbg/$IGPU_FREQ_TABLE printf "[MHz] \n" fi echo fi return 0 } # --- Battery Features print_methods_per_driver () { # show features provided by a battery driver # $1: driver = natacpi, tpacpi, tpsmapi local bm m mlist="" for bm in _bm_read _bm_thresh _bm_dischg; do if [ "$(eval echo \$$bm)" = "$1" ]; then # method matches driver case $bm in _bm_read) m="data" ;; _bm_thresh) m="thresholds" ;; _bm_dischg) m="recalibrate" ;; esac # concat method to output if [ -n "$mlist" ]; then mlist="$mlist, $m" else mlist="$m" fi fi done if [ -n "$mlist" ]; then printf "(%s)\n" "$mlist" else printf "(none)\n" fi return 0 } print_batstate () { # print battery charging state with # an explanation when a threshold inhibits charging # $1: sysfile # global param: $_bm_thresh, $_syspwr local sysf val # check if bat state sysfile exists if [ -f "$1" ]; then sysf=$1 else # sysfile non-existent printf "%-59s = (not available)\n" "$1" return 0 fi if val=$(read_sysf $sysf); then # sysfile was readable, output content printf "%-59s = %s" "$sysf" "$val" # Explain content if necessary case $val in "Unknown"|"Not charging") # a threshold may prevent charging ... if [ "$_syspwr" = "0" ] && [ "$_bm_thresh" != "none" ]; then # ... when on AC power and a threshold method is available printf " (threshold may prevent charging)\n" else printf "\n" fi ;; *) # Nothing to do printf "\n" ;; esac else # sysfile was not readable printf "%-59s = (not available)\n" "$sysf" fi return 0 } print_thresholds_discharge () { # formatted output of battery features # charge thresholds and force_discharge # $1: BAT0/BAT1 # global param: $_bm_thresh, $_bat_idx, $_bf_start, $_bf_stop local bsys force sp thresh local nl=0 if [ "$_bm_thresh" != "none" ]; then for sp in start stop; do thresh=$(read_threshold $sp) case $sp in start) bsys=$_bf_start ;; stop) bsys=$_bf_stop ;; esac if [ "$thresh" != "none" ]; then # valid threshold read case $_bm_thresh in natacpi|tpsmapi) printf "%-59s = %6d [%%]\n" "$bsys" "$thresh" nl=1 ;; tpacpi) printf "%-59s = %6d [%%]\n" "tpacpi-bat.${1}.${sp}Threshold" "$thresh" nl=1 ;; none) ;; # nothing to show esac else # threshold read failed case $_bm_thresh in natacpi|tpsmapi) printf "%-59s = (not available)\n" "$bsys" nl=1 ;; tpacpi) printf "%-59s = (not available)\n" "tpacpi-bat.${1}.${sp}Threshold" nl=1 ;; none) ;; # nothing to show esac fi done # for sp fi if [ "$_bm_dischg" != "none" ]; then get_force_discharge $1; force=$? if [ $force -lt 2 ]; then # valid force_discharge read case $_bm_dischg in natacpi|tpsmapi) printf "%-59s = %6d\n" "$_bf_dischg" "$force" nl=1 ;; tpacpi) printf "%-59s = %6d\n" "tpacpi-bat.${1}.forceDischarge" "$force" nl=1 ;; none) ;; # nothing to show esac else # force_discharge read failed case $_bm_dischg in natacpi|tpsmapi) printf "%-59s = (not available)\n" "$_bf_dischg" nl=1 ;; tpacpi) printf "%-59s = %6d\n" "tpacpi-bat.${1}.forceDischarge" "(not available)" nl=1 ;; none) ;; # nothing to show esac fi fi [ $nl -eq 0 ] || echo return 0 } TLP-1.3.1/man-rdw/000077500000000000000000000000001361705037700135315ustar00rootroot00000000000000TLP-1.3.1/man-rdw/tlp-rdw.8000066400000000000000000000005421361705037700152140ustar00rootroot00000000000000.TH tlp-rdw 8 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME tlp-rdw \- disable Radio Device Wizard temporarily (until reboot). . .SH SYNOPSIS .B tlp-rdw \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B disable Disable RDW actions. . .TP .B enable Enable RDW actions. . .TP Show RDW state. . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/000077500000000000000000000000001361705037700127375ustar00rootroot00000000000000TLP-1.3.1/man/bluetooth.1000066400000000000000000000006211361705037700150250ustar00rootroot00000000000000.TH bluetooth 1 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME bluetooth \- enable/disable internal bluetooth device . .SH SYNOPSIS .B bluetooth \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/run-on-ac.1000066400000000000000000000004371361705037700146240ustar00rootroot00000000000000.TH run\-on\-ac 1 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME run\-on\-ac \- run command when on ac power . .SH SYNOPSIS .B run\-on\-ac \fR\fIcommand\fR \fR[\fIarg(s)]\fR . .SH SEE ALSO .BR run\-on\-bat (1), .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/run-on-bat.1000066400000000000000000000004461361705037700150070ustar00rootroot00000000000000.TH run\-on\-bat 1 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME run\-on\-bat \- run command when on battery power . .SH SYNOPSIS .B run\-on\-bat \fR\fIcommand\fR \fR[\fIarg(s)]\fR . .SH SEE ALSO .BR run\-on\-ac (1), .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/tlp-stat.8000066400000000000000000000036511361705037700146050ustar00rootroot00000000000000.TH tlp\-stat 8 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME tlp\-stat \- show power saving settings . .SH SYNOPSIS .B tlp\-stat \fI[options]\fR [-- "..."] . .SH DESCRIPTION Show configuration, system information, active power saving settings and battery data. . .SH OPTIONS . .TP .B \-b, \-\-battery Show battery data. . .TP .B \-c, \-\-config Show configuration. . .TP .B \-d, \-\-disk Show disk data. . .TP .B \-e, \-\-pcie Show PCI(e) device data. . .TP .B \-g, \-\-graphics Show graphics card data. . .TP .B \-p, \-\-processor Show processor data. . .TP .B \-r, \-\-rfkill Show radio device data. . .TP .B \-s, \-\-system Show system information. . .TP .B \-t, \-\-temp Show temperatures and fan speed. . .TP .B \-u, \-\-usb Show USB device data. . .TP .B \-w, \-\-warn Show warnings. . .TP .B \-P, \-\-pev Monitor power supply udev events. . .TP .B \-\-psup Show power supply diagnostic (view kernel data). . .TP .B \-T, \-\-trace Show trace output. . .TP .B \-v, \-\-verbose Be more verbose. . .TP .B \-- PARAM=value "..." Add parameters to temporarily overwrite the system configuration (for this program invocation only). . .SH FILES .I /etc/tlp.conf .RS System-wide user configuration file, uncomment parameters here to override default settings and customization files below. .PP .RE .I /etc/tlp.d/*.conf .RS System-wide drop-in customization files, overriding defaults below. .PP .RE .I /usr/share/tlp/defaults.conf .RS Intrinsic default settings. DO NOT EDIT this file, instead use one of the above alternatives. .PP .RE .I /run/tlp/run.conf .RS Effective settings consolidated from all above files. DO NOT CHANGE this file, it is for reference only and regenerated on every invocation of TLP. .PP .RE .I /etc/default/tlp .RS Obsolete system-wide configuration file. DO NOT USE this file, it is evaluated only when /etc/tlp.conf is non-existent. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/tlp.8000066400000000000000000000067761361705037700136470ustar00rootroot00000000000000.TH tlp 8 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME tlp \- apply laptop power saving settings . .SH SYNOPSIS .B tlp \fIcommand\fR \fR[\fIparameters\fR] [-- "..."] . .SH DESCRIPTION Apply power saving settings manually and control ThinkPad battery features. . .SH COMMANDS . .TP .B start Initialize \fBtlp\fR and apply power saving settings according to the actual power source. Also use to apply a changed configuration. . .TP .B bat Apply power saving settings for battery power source. . .TP .B true Same as \fBbat\fR. . .TP .B ac Apply power saving settings for AC power source. . .TP .B false Same as \fBac\fR. . .TP .B usb Enable autosuspend for all USB devices, except blacklisted ones. . .TP .B bayoff Turn off optical drive in UltraBay/MediaBay. The drive may be re\-enabled by pulling the eject lever or pushing the media eject button on newer models. . .TP .B setcharge [START_CHARGE STOP_CHARGE] [BAT0|BAT1] \fR(ThinkPads only) Set charge thresholds of main (BAT0) or auxiliary (BAT1) battery temporarily. Values must be between 1 and 100, START_CHARGE must not exceed STOP_CHARGE - 4. Configured thresholds are restored upon next system boot-up. When called without arguments, configured thresholds are set. . .TP .B fullcharge [BAT0|BAT1] \fR(ThinkPads only) Set charge thresholds of main (BAT0) or auxiliary (BAT1) battery to factory presets (96/100) temporarily (causing a full charge). Configured thresholds are restored upon next system boot-up. . .TP .B chargeonce [BAT0|BAT1] \fR(ThinkPads only) Charge main (BAT0) or auxiliary (BAT1) battery to the stop charge threshold once (bypassing start threshold). Configured thresholds are restored upon next system boot-up. . .TP .B discharge [BAT0|BAT1] \fR(ThinkPads only) Force complete discharge of main (BAT0) or auxiliary (BAT1) battery. . .TP .B recalibrate [BAT0|BAT1] \fR(ThinkPads only) Battery recalibration: completely discharge main (BAT0) or auxiliary (BAT1) battery and recharge to 100%. . .TP .B diskid Show disk ids for configuration. . .TP .B \-- PARAM=value "..." Add parameters to temporarily overwrite the system configuration (for this program invocation only). . .SH NOTES For ThinkPads with more than one battery the selection works as follows: .IP BAT0 \- Main or internal battery .IP BAT1 \- Ultrabay, Slice or replaceable battery . .SH EXAMPLES Apply thresholds of 70 / 90% to the main battery: .IP tlp setcharge 70 90 BAT0 .PP Charge the auxiliary battery to full capacity: .IP tlp fullcharge BAT1 .PP Recalibrate the main battery: .IP tlp recalibrate BAT0 . .SH FILES .I /etc/tlp.conf .RS System-wide user configuration file, uncomment parameters here to override default settings and customization files below. .PP .RE .I /etc/tlp.d/*.conf .RS System-wide drop-in customization files, overriding defaults below. .PP .RE .I /usr/share/tlp/defaults.conf .RS Intrinsic default settings. DO NOT EDIT this file, instead use one of the above alternatives. .PP .RE .I /run/tlp/run.conf .RS Effective settings consolidated from all above files. DO NOT CHANGE this file, it is for reference only and regenerated on every invocation of TLP. .PP .RE .I /etc/default/tlp .RS Obsolete system-wide configuration file. DO NOT USE this file, it is evaluated only when /etc/tlp.conf is non-existent. . .SH EXIT STATUS On success, 0 is returned, a non-zero failure code otherwise. . .SH SEE ALSO .BR tlp\-stat (8), .BR bluetooth (1), .BR wifi (1), .BR wwan (1). . .PP .mso www.tmac .URL "https://linrunner.de" "Project homepage: " . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/tlp.service.8000066400000000000000000000014001361705037700152610ustar00rootroot00000000000000.TH tlp.service 8 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME . tlp.service \- Initialize power saving at boot and cleanup upon shutdown . .SH SYNOPSIS .B tlp\&.service . .SH DESCRIPTION tlp.service executes the following tasks: .IP " 1." 4 System boot-up: switch or restore radio states, apply power saving settings and charge thresholds. .IP " 2." 4 System shutdown: switch or save radio states, apply AC settings and cleanup. . .SH FILES .I /lib/systemd/system-sleep/tlp .RS Applies power saving settings upon system suspend and resume. .SH SEE ALSO .BR tlp (8). . .SH NOTES Do \fInot\fR employ tlp.service to refresh power saving settings after a configuration change. Use \fBtlp start\fR instead. . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/wifi.1000066400000000000000000000005751361705037700137660ustar00rootroot00000000000000.TH wifi 1 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME wifi \- enable/disable internal wifi device . .SH SYNOPSIS .B wifi \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/man/wwan.1000066400000000000000000000006051361705037700137760ustar00rootroot00000000000000.TH wwan 1 2020-01-31 "TLP 1.3.0" "Power Management" . .SH NAME wwan \- enable/disable internal wwan (3G/4G) device . .SH SYNOPSIS .B wwan \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2020 Thomas Koch TLP-1.3.1/tlp-func-base.in000066400000000000000000000642431361705037700151650ustar00rootroot00000000000000#!/bin/sh # tlp - Base Functions # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly TLPVER="@TLPVER@" readonly RUNDIR=@TLP_RUN@ readonly VARDIR=@TLP_VAR@ readonly CONF_DEF=@TLP_CONFDEF@ readonly CONF_DIR=@TLP_CONFDIR@ readonly CONF_USR=@TLP_CONFUSR@ readonly CONF_OLD=@TLP_CONF@ readonly CONF_RUN="$RUNDIR/run.conf" readonly FLOCK=flock readonly HDPARM=hdparm readonly LAPMODE=laptop_mode readonly LOGGER=logger readonly MKTEMP=mktemp readonly MODPRO=modprobe readonly READCONFS=@TLP_TLIB@/tlp-readconfs readonly TPACPIBAT=@TPACPIBAT@ readonly UDEVADM=udevadm readonly LOCKFILE=$RUNDIR/lock readonly LOCKTIMEOUT=2 readonly PWRRUNFILE=$RUNDIR/last_pwr readonly MANUALMODEFILE=$RUNDIR/manual_mode readonly MOD_MSR="msr" readonly MOD_TEMP="coretemp" readonly MOD_TPSMAPI="tp_smapi" readonly MOD_TPACPI="acpi_call" readonly DMID=/sys/class/dmi/id/ readonly NETD=/sys/class/net readonly RE_TPSMAPI_ONLY='^(Edge( 13.*)?|G41|R[56][012][eip]?|R[45]00|SL[45]10|T23|T[346][0123][p]?|T[45][01]0[s]?|W[57]0[01]|X[346][012][s]?( Tablet)?|X1[02]0e|X[23]0[01][s]?( Tablet)?|Z6[01][mpt])$' readonly RE_TPSMAPI_AND_TPACPI='^(X1|X220[s]?( Tablet)?|T[45]20[s]?|W520)$' readonly RE_TP_NONE='^(L[45]20|SL[345]00|X121e)$' readonly RE_PARAM='^[A-Z_]+[0-9]*=[0-9a-zA-Z _\-\:\.]*$' # power supplies: ignore MacBook Pro 2017 sbs-charger and hid devices readonly RE_PS_IGNORE='sbs-charger|hidpp_battery|hid-' readonly DEBUG_TAGS_ALL="arg bat cfg disk lock nm path pm ps rf run sysfs udev usb" # ---------------------------------------------------------------------------- # Control _nodebug=0 # ---------------------------------------------------------------------------- # Functions # -- Exit do_exit () { # cleanup and exit -- $1: rc # remove temporary runconf [ -f "$_conf_tmp" ] && rm -rf "$_conf_tmp" exit $1 } # --- Debug echo_debug () { # write debug msg if tag matches -- $1: tag; $2: msg; [ "$_nodebug" = "1" ] && return 0 if wordinlist "$1" "$TLP_DEBUG"; then $LOGGER -p debug -t "tlp" --id=$$ -- "$2" > /dev/null 2>&1 fi } # --- Strings tolower () { # echo string in lowercase -- $1: string printf "%s" "$1" | tr "[:upper:]" "[:lower:]" } toupper () { # echo string in lowercase -- $1: string printf "%s" "$1" | tr "[:lower:]" "[:upper:]" } wordinlist () { # test if word in list # $1: word, $2: whitespace-separated list of words local word if [ -n "${1-}" ]; then for word in ${2-}; do [ "${word}" != "${1}" ] || return 0 # exact match done fi return 1 # no match } # --- Sysfiles check_sysfs () { # debug: check if sysfile exists -- $1: routine; $2: sysfs path if wordinlist "sysfs" "$TLP_DEBUG"; then if [ ! -e $2 ]; then $LOGGER -p debug -t "tlp" --id=$$ -- "$1: $2 nonexistent" > /dev/null 2>&1 fi fi } read_sysf () { # read and echo contents of sysfile # return 1 and echo default if read fails # $1: sysfile, $2: default; rc: 0=ok/1=error if ! cat "$1" 2> /dev/null; then printf "%s" "$2" return 1 else return 0 fi } readable_sysf () { # check if file is actually readable -- $1: file # rc: 0=readable/1=read error cat "$1" > /dev/null 2>&1 } read_sysval () { # read and echo contents of sysfile # echo '0' if file is non-existent, read fails or is non-numeric # $1: sysfile; rc: 0=ok/1=error printf "%d" "$(read_sysf $1)" 2> /dev/null } write_sysf () { # write string to a sysfile # $1: string, $2: sysfile # rc: 0=ok/1=error { printf '%s\n' "$1" > "$2"; } 2> /dev/null } # --- Tests cmd_exists () { # test if command exists -- $1: command command -v $1 > /dev/null 2>&1 } test_root () { # test root privilege -- rc: 0=root, 1=not root [ "$(id -u)" = "0" ] } check_root () { # show error message and quit when root privilege missing if ! test_root; then echo "Error: missing root privilege." 1>&2 do_exit 1 fi } check_tlp_enabled () { # check if TLP is enabled in config file # $1: 1=verbose (default: 0) # rc: 0=disabled/1=enabled if [ "$TLP_ENABLE" = "1" ]; then return 0 else [ "${1:-0}" = "1" ] && echo "Error: TLP power save is disabled. Set TLP_ENABLE=1 in ${CONF_USR}." 1>&2 return 1 fi } # --- Type and value checking is_uint () { # check for unsigned integer -- $1: string; $2: max digits printf "%s" "$1" | grep -E -q "^[0-9]{1,$2}$" 2> /dev/null } is_within_bounds () { # check condition min <= value <= max # $1: value; $2: min; $3: max (all unsigned int) # rc: 0=within/1=below/2=above/255=invalid # # value, min or max undefined/non-numeric means that this branch of the # condition is fulfilled is_uint $1 || return 255 if is_uint $2; then [ $1 -ge $2 ] || return 1 fi if is_uint $3; then [ $1 -le $3 ] || return 2 fi return 0 } # --- Locking and Semaphores set_run_flag () { # set flag -- $1: flag name # rc: 0=success/1,2=failed local rc create_rundir touch $RUNDIR/$1; rc=$? echo_debug "lock" "set_run_flag.touch: $1; rc=$rc" return $rc } reset_run_flag () { # reset flag -- $1: flag name if rm $RUNDIR/$1 2> /dev/null 1>&2 ; then echo_debug "lock" "reset_run_flag($1).remove" else echo_debug "lock" "reset_run_flag($1).not_found" fi return 0 } check_run_flag () { # check flag -- $1: flag name # rc: 0=flag set/1=flag not set local rc [ -f $RUNDIR/$1 ]; rc=$? echo_debug "lock" "check_run_flag($1): rc=$rc" return $rc } lock_tlp () { # get exclusive lock: blocking with timeout # $1: lock id (default: tlp) # rc: 0=success/1=failed create_rundir # open file for writing and attach fd 9 # when successful lock fd 9 exclusive and blocking # wait $LOCKTIMEOUT secs to obtain the lock if { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null && $FLOCK -x -w $LOCKTIMEOUT 9 ; then echo_debug "lock" "lock_tlp($1).success" return 0 else echo_debug "lock" "lock_tlp($1).failed" return 1 fi } lock_tlp_nb () { # get exclusive lock: non-blocking # $1: lock id (default: tlp) # rc: 0=success/1=failed create_rundir # open file for writing and attach fd 9 # when successful lock fd 9 exclusive and non-blocking if { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null && $FLOCK -x -n 9 ; then echo_debug "lock" "lock_tlp_nb($1).success" return 0 else echo_debug "lock" "lock_tlp_nb($1).failed" return 1 fi } unlock_tlp () { # free exclusive lock # $1: lock id (default: tlp) # defer unlock for $X_DEFER_UNLOCK seconds -- debugging only [ -n "$X_DEFER_UNLOCK" ] && sleep $X_DEFER_UNLOCK # free fd 9 and scrap lockfile { exec 9>&- ; } 2> /dev/null rm -f ${LOCKFILE}_${1:-tlp} echo_debug "lock" "unlock_tlp($1)" return 0 } lockpeek_tlp () { # check for pending lock (by looking for the lockfile) # $1: lock id (default: tlp) if [ -f ${LOCKFILE}_${1:-tlp} ]; then echo_debug "lock" "lockpeek_tlp($1).locked" return 0 else echo_debug "lock" "lockpeek_tlp($1).not_locked" return 1 fi } echo_tlp_locked () { # print "locked" message echo "Error: TLP is locked by another operation." 1>&2 return 0 } set_timed_lock () { # create timestamp n seconds in the future # $1: lock id, $2: lock duration [s] local lock rc time lock=${1}_timed_lock_$(date +%s -d "+${2} seconds") set_run_flag $lock; rc=$? echo_debug "lock" "set_timed_lock($1, $2): $lock; rc=$rc" # cleanup obsolete locks time=$(date +%s) for lockfile in $RUNDIR/${1}_timed_lock_*; do if [ -f "$lockfile" ]; then locktime=${lockfile#${RUNDIR}/${1}_timed_lock_} if [ $time -ge $locktime ]; then rm -f $lockfile echo_debug "lock" "set_timed_lock($1, $2).remove_obsolete: ${lockfile#${RUNDIR}/}" fi fi done return $rc } check_timed_lock () { # check if active timestamp exists # $1: lock id; rc: 0=locked/1=not locked local lockfile locktime time time=$(date +%s) for lockfile in $RUNDIR/${1}_timed_lock_*; do if [ -f "$lockfile" ]; then locktime=${lockfile#${RUNDIR}/${1}_timed_lock_} if [ $time -lt $(( locktime - 120 )) ]; then # timestamp is more than 120 secs in the future, # something weird has happened -> remove it rm -f $lockfile echo_debug "lock" "check_timed_lock($1).remove_invalid: ${lockfile#${RUNDIR}/}" elif [ $time -lt $locktime ]; then # timestamp in the future -> we're locked echo_debug "lock" "check_timed_lock($1).locked: $time, $locktime" return 0 else # obsolete timestamp -> remove it rm -f $lockfile echo_debug "lock" "check_timed_lock($1).remove_obsolete: ${lockfile#${RUNDIR}/}" fi fi done echo_debug "lock" "check_timed_lock($1).not_locked: $time" return 1 } # --- PATH add_sbin2path () { # check if /sbin /usr/sbin in $PATH, otherwise add them # retval: $PATH, $_oldpath, $_addpath local sp # shellcheck disable=SC2034 _oldpath="$PATH" _addpath="" for sp in /usr/sbin /sbin; do if [ -d $sp ] && [ ! -h $sp ]; then # dir exists and is not a symlink case ":$PATH:" in *":$sp:"*) # $sp already in $PATH ;; *) # $sp not in $PATH, add it _addpath="$_addpath:$sp" ;; esac fi done if [ -n "$_addpath" ]; then export PATH="${PATH}${_addpath}" fi return 0 } create_rundir () { # make sure $RUNDIR exists [ -d $RUNDIR ] || mkdir -p $RUNDIR 2> /dev/null 1>&2 } # --- Configuration read_config () { # read all config files and write temporary runconf file # $1: 0=continue/1=quit on error # $2: 1=no trace # rc: 0=ok/5=tlp.conf missing/6=defaults.conf missing/7=file creation error # retval: config parameters; # _conf_tmp: runconf local rc=0 local tmpdir if test_root; then tmpdir=$RUNDIR create_rundir else tmpdir=${TMPDIR:-/tmp} fi if _conf_tmp=$($MKTEMP -p "$tmpdir" "tlp-run.conf_tmpXXXXXX"); then # external perl script: merge all config files to $cf if [ "$2" = "1" ]; then $READCONFS --outfile "$_conf_tmp" --notrace; rc=$? else $READCONFS --outfile "$_conf_tmp"; rc=$? fi # shellcheck disable=SC1090 [ $rc -eq 0 ] && . "$_conf_tmp" else rc=7 fi if [ $rc -ne 0 ]; then case $rc in 5) echo "Error: cannot read user configuration from $CONF_USR or $CONF_OLD." 1>&2 ;; 6) echo "Error: cannot read default configuration from $CONF_DEF." 1>&2 ;; 7) echo "Error: cannot write runtime configuration to $_conf_tmp." 1>&2 ;; esac if [ "$1" = "1" ]; then do_exit $rc fi fi return 0 } parse_args4config () { # parse command-line arguments: everything after the # delimiter '--' is interpreted as a config parameter # retval: config parameters local dflag=0 param value echo_debug "arg" "parse_args4config: ${0##/*/} $*" # iterate arguments while [ $# -gt 0 ]; do if [ $dflag -eq 1 ]; then # delimiter was passed --> sanitize and parse argument: # quotes stripped by the shell calling tlp # format is PARAMETER=value # PARAMETER allows 'A'..'Z' and '_' only, may end in a number (_BAT0) # value allows 'A'..'Z', 'a'..'z', '0'..'9', ' ', '_', '-', ':' # value may be an empty string if printf "%s" "$1" | grep -E -q "$RE_PARAM"; then param="${1%%=*}" value="${1#*=}" if [ -n "$param" ]; then echo_debug "cfg" "parse_args4config.param: $param=$value" eval "$param='$value'" 2> /dev/null fi fi elif [ "$1" = "--" ]; then # delimiter reached --> begin interpretation dflag=1 fi shift # next argument done # while arguments return 0 } print_config () { # print runtime conf based on all config files # external perl script: merge all config files to $cf $READCONFS --notrace } save_runconf () { # copy temporary to final runconf create_rundir if cp --preserve=timestamps "$_conf_tmp" $CONF_RUN > /dev/null 2>&1; then chmod 664 "$_conf_tmp" $CONF_RUN > /dev/null 2>&1 echo_debug "run" "save_runconf.ok: $_conf_tmp -> $CONF_RUN" else echo_debug "run" "save_runconf.failed: $_conf_tmp -> $CONF_RUN" fi } # --- Kernel kernel_version_ge () { # check if running kernel version >= $1: minimum version [ "$1" = "$(printf "%s\n%s\n" "$1" "$(uname -r)" | sort -V | head -n 1)" ] } load_modules () { # load kernel module(s) -- $*: modules local mod # verify module loading is allowed (else explicitly disabled) # and possible (else implicitly disabled) [ "${TLP_LOAD_MODULES:-y}" = "y" ] && [ -e /proc/modules ] || return 0 # load modules, ignore any errors # shellcheck disable=SC2048 for mod in $*; do $MODPRO $mod > /dev/null 2>&1 done return 0 } # --- DMI read_dmi () { # read DMI data -- $*: keywords; stdout: dmi strings local ds key outr outr="" # shellcheck disable=SC2048 for key in $*; do ds="$(read_sysf ${DMID}/$key | \ grep -E -v -i 'not available|to be filled|DMI table is broken')" if [ -n "$outr" ]; then [ -n "$ds" ] && outr="$outr $ds" else outr="$ds" fi done printf '%s' "$outr" return 0 } # --- ThinkPad Checks supports_tpsmapi_only () { # rc: 0=ThinkPad supports tpsmapi only/1=false # prerequisite: check_thinkpad() printf '%s' "$_tpmodel" | grep -E -q "${RE_TPSMAPI_ONLY}" } supports_tpsmapi_and_tpacpi () { # rc: 0=ThinkPad supports tpsmapi, tpacpi-bat, natacpi/1=false # prerequisite: check_thinkpad() printf '%s' "$_tpmodel" | grep -E -q "${RE_TPSMAPI_AND_TPACPI}" } supports_no_tp_bat_funcs () { # rc: 0=ThinkPad doesn't support battery features/1=false # prerequisite: check_thinkpad() printf '%s' "$_tpmodel" | grep -E -q "${RE_TP_NONE}" } supports_tpacpi () { # rc: 0=ThinkPad does support tpacpi-bat, natacpi/1=false # prerequisite: check_thinkpad() # assumption: all newer models support tpapaci-bat/natacapi except # explicitly unsupported or older tpsmapi only models ! supports_no_tp_bat_funcs && ! supports_tpsmapi_only } check_thinkpad () { # check for ThinkPad hardware and save model string, # load ThinkPad specific kernel modules # rc: 0=ThinkPad, 1=other hardware # retval: $_tpmodel local pv _tpmodel="" if [ -d $TPACPIDIR ]; then # kernel module thinkpad_acpi is loaded if [ -z "$X_SIMULATE_MODEL" ]; then # get DMI product string and sanitize it pv="$(read_dmi product_version | tr -C -d 'a-zA-Z0-9 ')" else # simulate arbitrary model pv="$X_SIMULATE_MODEL" fi # check DMI product string for occurrence of "ThinkPad" if printf '%s' "$pv" | grep -E -q 'Think[Pp]ad'; then # it's a real ThinkPad --> save model substring _tpmodel=$(printf '%s\n' "$pv" | sed -r 's/^Think[Pp]ad //') fi else # not a ThinkPad: get DMI product string pv="$(read_dmi product_version)" fi if [ -n "$_tpmodel" ]; then # load tp-smapi for supported models only; prevents kernel messages if supports_tpsmapi_only || supports_tpsmapi_and_tpacpi; then load_modules $MOD_TPSMAPI fi # load acpi-call for supported models only; prevents kernel messages if supports_tpacpi; then load_modules $MOD_TPACPI fi echo_debug "bat" "check_thinkpad: tpmodel=$_tpmodel" return 0 fi # not a ThinkPad echo_debug "bat" "check_thinkpad.not_a_thinkpad: model=$pv" return 1 } is_thinkpad () { # check for ThinkPad by saved model string # rc: 0=ThinkPad, 1=other hardware [ -n "$_tpmodel" ] } # --- Power Source get_sys_power_supply () { # get current power supply # retval/rc: $_syspwr (0=ac, 1=battery, 2=unknown) # _syspwr is determined by iterating all power sources: # # AC status: | none | 1 | 0 | # | found | online | offline | # +------------ +-------+---------+---------+ # battery | none found | 2 | 0 *s | *o | # status | Charging | *r | 0 *s | *r | # | Discharging | *d | 0 *s | *d | # | Full | *r | 0 *s | *r | # | Unknown | *r | 0 *s | *r | # # special cases: # *o) if battery status "Unknown" was seen previously then _syspwr=1; # else remember AC offline state # *r) re-check battery status after 0.5 sec -> if battery status # "Discharging" (not forced) then _syspwr=1 and stop looking; # else _syspwr=0 # *d) if forced discharge in progress then _syspwr=0; else _syspwr=1; # stop looking # *s) stop looking (for other power sources) local bs psrc psrc_name local ac0seen= _syspwr= for psrc in /sys/class/power_supply/*; do # -f $psrc/type not necessary - read_sysf() handles this psrc_name="${psrc##*/}" # ignore atypical power supplies and batteries printf '%s\n' "$psrc_name" | grep -E -q "$RE_PS_IGNORE" && continue case "$(read_sysf $psrc/type)" in Mains|USB) # AC detected # skip device to ignore incorrect AC status if [ "$TLP_PS_IGNORE" = "AC" ]; then echo_debug "ps" "get_sys_power_supply(${psrc_name}).ac_ignored: syspwr=$_syspwr" continue fi # check AC status if [ "$(read_sysf $psrc/online)" = "1" ]; then # AC online --> end iteration _syspwr=0 echo_debug "ps" "get_sys_power_supply(${psrc_name}).ac_online: syspwr=$_syspwr" break else # AC offline could mean battery, but quirky hardware exists # --> remember and continue looking (for batteries) ac0seen=1 echo_debug "ps" "get_sys_power_supply(${psrc_name}).ac_offline_remembered" fi ;; Battery) # battery detected # skip device to ignore incorrect battery status if [ "$TLP_PS_IGNORE" = "BAT" ]; then echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_ignored: syspwr=$_syspwr" continue fi # check battery status bs="$(read_sysf $psrc/status)" case "$bs" in Discharging) if ! lockpeek_tlp tlp_discharge; then # battery status "Discharging" means battery mode ... _syspwr=1 echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_discharging: syspwr=$_syspwr" else # ... unless forced discharge is in progress, which means AC _syspwr=0 echo_debug "ps" "get_sys_power_supply(${psrc_name}).forced_discharge: syspwr=$_syspwr" fi break # --> end iteration ;; *) # everything else - e.g. "Charging", "Full", "Unknown" - might be caused by lagging # battery status updates --> re-check after a short delay echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_not_discharging_check1: bs=$bs; syspwr=$_syspwr" sleep 0.8 bs="$(read_sysf $psrc/status)" if [ "$bs" = "Discharging" ] && ! lockpeek_tlp tlp_discharge; then # battery status changed to "Discharging" (not forced) means battery mode # --> end iteration _syspwr=1 echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_discharging_check2: bs=$bs; syspwr=$_syspwr" break else # otherwise assume AC mode # --> continue looking _syspwr=0 echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_not_discharging_check2: bs=$bs; syspwr=$_syspwr" fi ;; esac ;; *) # unknown power source type --> ignore ;; esac done if [ -z "$_syspwr" ]; then # _syspwr result yet undecided if [ "$ac0seen" = "1" ]; then # AC offline remembered --> battery mode _syspwr=1 echo_debug "ps" "get_sys_power_supply(${ac0seen##/*/}).ac_offline: syspwr=$_syspwr" else # we have seen neither a AC nor a battery power source --> unknown mode _syspwr=2 echo_debug "ps" "get_sys_power_supply.none_found: syspwr=$_syspwr" fi fi return $_syspwr } get_persist_mode () { # get persistent operation mode # rc: 0=persistent/1=not persistent # retval: $_persist_mode (0=ac, 1=battery, none) local rc=1 _persist_mode="none" if [ "$TLP_PERSISTENT_DEFAULT" = "1" ]; then # persistent mode = configured default mode case "$TLP_DEFAULT_MODE" in ac|AC) _persist_mode=0; rc=0 ;; bat|BAT) _persist_mode=1; rc=0 ;; esac fi return $rc } get_power_mode () { # get current operation mode -- rc: 0=AC, 1=battery # similar to get_sys_power_supply(), # but maps unknown power source to TLP_DEFAULT_MODE or returns # persistent mode when enabled get_sys_power_supply local rc=$? if get_persist_mode; then # persistent mode rc=$_persist_mode else # non-persistent mode, use current power source if [ $rc -eq 2 ]; then # unknown power supply, use configured default mode case "$TLP_DEFAULT_MODE" in ac|AC) rc=0 ;; bat|BAT) rc=1 ;; *) rc=0 ;; # use AC if none or invalid mode esac fi fi return $rc } compare_and_save_power_state() { # compare $1 to last saved power state, # save $1 afterwards when different # $1: new state 0=ac, 1=battery # rc: 0=different, 1=equal local lp # intercept invalid states case $1 in 0|1) ;; # valid state *) # invalid new state --> return "different" echo_debug "ps" "compare_and_save_power_state($1).invalid" return 0 ;; esac # read saved state lp=$(read_sysf $PWRRUNFILE) # compare if [ -z "$lp" ] || [ "$lp" != "$1" ]; then # saved state is nonexistent/empty or is different --> save new state create_rundir write_sysf "$1" $PWRRUNFILE echo_debug "ps" "compare_and_save_power_state($1).different: old=$lp" return 0 else # touch file for last run touch $PWRRUNFILE echo_debug "ps" "compare_and_save_power_state($1).equal" return 1 fi } clear_saved_power_state() { # remove last saved power state rm -f $PWRRUNFILE 2> /dev/null return 0 } check_ac_power () { # check if ac power connected -- $1: function if ! get_sys_power_supply ; then echo_debug "bat" "check_ac_power($1).no_ac_power" echo "Error: $1 is possible on AC power only." 1>&2 return 1 fi return 0 } echo_started_mode () { # print operation mode -- $1: 0=ac mode, 1=battery mode if [ "$1" = "0" ]; then printf "TLP started in AC mode" else printf "TLP started in battery mode" fi if [ "$_manual_mode" != "none" ]; then printf " (manual).\n" else printf " (auto).\n" fi return 0 } set_manual_mode () { # set manual operation mode # $1: 0=ac mode, 1=battery mode # retval: $_manual_mode (0=ac, 1=battery, none=not found) create_rundir write_sysf "$1" $MANUALMODEFILE _manual_mode="$1" echo_debug "pm" "set_manual_mode($1)" return 0 } clear_manual_mode () { # remove manual operation mode # retval: $_manual_mode (none) rm -f $MANUALMODEFILE 2> /dev/null _manual_mode="none" echo_debug "pm" "clear_manual_mode" return 0 } get_manual_mode () { # get manual operation mode # rc: 0=ok/1=not found # retval: $_manual_mode (0=ac, 1=battery, none=not found) local rc=1 _manual_mode="none" if [ -f $MANUALMODEFILE ]; then # read mode file _manual_mode=$(read_sysf $MANUALMODEFILE) case $_manual_mode in 0|1) rc=0 ;; *) _manual_mode="none" ;; esac fi return $rc } # --- Misc Checks check_laptop_mode_tools () { # check if lmt installed -- rc: 0=not installed, 1=installed if cmd_exists $LAPMODE; then echo 1>&2 echo "***Warning: laptop-mode-tools detected, this may cause conflicts with TLP." 1>&2 echo " Please uninstall laptop-mode-tools." 1>&2 echo 1>&2 echo_debug "pm" "check_laptop_mode_tools: yes" return 1 else return 0 fi } TLP-1.3.1/tlp-pcilist000066400000000000000000000040021361705037700143470ustar00rootroot00000000000000#!/usr/bin/perl # tlp-pcilist - list pci devices with runtime pm mode and device class # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. package tlp_pcilist; use strict; use warnings; # Read content from a sysfile # $_[0]: input file # return: content / empty string if nonexistent or not readable sub catsysf { my $fname = "$_[0]"; my $sysval = ""; if (open my $sysf, "<", $fname) { chomp ($sysval = <$sysf>); close $sysf; } return $sysval; } # Read device driver from DEVICE/uevent # $_[0]: (sub)device base path # return: driver / empty string if uevent nonexistent or not readable sub getdriver { my $dpath = "$_[0]"; my $driver = ""; if ( open (my $sysf, "<", $dpath . "/uevent") ) { # read file line by line while (<$sysf>) { # match line content and return DRIVER= value if ( s/^DRIVER=(.*)/$1/ ) { chomp ($driver = $_); last; # break loop } } close ($sysf); } return $driver } # Output device list with Runtime PM mode and device class foreach (`lspci -m`) { # parse lspci output: get short PCI(e) id and long description of device my ($dev, $classdesc) = /(\S+) \"(.+?)\"/; # join device path my $devp = "/sys/bus/pci/devices/0000:$dev"; # control file for Runtime PM my $devc = "$devp/power/control"; # get device class my $class = catsysf ("$devp/class"); # get device driver my $driver = getdriver ("$devp") || "no driver"; if (-f $devc) { # control file exists # get Runtime PM mode my $pmode = catsysf ("$devc"); # output pm mode and device data printf "%s/power/control = %-4s (%s, %s, %s)\n", $devp, $pmode, $class, $classdesc, $driver; } else { # control file missing --> output device data only printf "%s/power/control = (not available) (%s, %s, %s)\n", $devp, $class, $classdesc, $driver; } } exit 0; TLP-1.3.1/tlp-rdw-nm.in000066400000000000000000000127141361705037700145220ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - network manager dispatcher hook: # enable/disable radios on ifup/ifdown # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- Functions check_switch_lock() { # switch listed radio devices # and time-lock them afterwards if actually switched # $1: device type where the event originated -- do nothing if its time-locked # $2: list of device types to switch # $3: on/off local type # quit if the originating *radio* device is time-locked (not LAN) [ "$1" != "LAN" ] && check_timed_lock "${RDW_NM_LOCK}_$1" && return 1 for type in $2; do if [ -n "$type" ] && [ "$type" != "$1" ]; then # device type is valid and not the originating one # --> do switch with state change lock device_switch $type $3 "${RDW_NM_LOCK}_${type}" $RDW_LOCKTIME fi done return 0 } save_iface_type () { # save interface type -- $1: interface; $2: type # rc: 0=saved/1=error [ -d $NETD/$1 ] && { printf '%s\n' "$2" > $RUNDIR/${1}.itype; } 2> /dev/null return $? } get_iface_type () { # get saved interface type -- $1: interface # rc: 0=saved state found/1=not found # retval: $itype local rc itype=$(read_sysf $RUNDIR/${1}.itype); rc=$? rm -f $RUNDIR/${1}.itype return $rc } # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 check_tlp_enabled || do_exit 0 check_run_flag $RDW_KILL && do_exit 0 add_sbin2path # Get args iface="$1" action="$2" itype="" # Quit for invalid interfaces # shellcheck disable=SC2015 [ -n "$iface" ] && [ "$iface" != "none" ] || do_exit 0 # Quit for actions other than "up" and "down" [ "$action" = "up" ] || [ "$action" = "down" ] || do_exit 0 # Quit for virtual interfaces (up action) if [ "$action" = "up" ] && readlink "$NETD/$iface" | grep -q '/virtual/'; then # save type for down action where $NETD/$iface won't be there anymore save_iface_type $iface virtual [ "$X_NET_VIRTUAL_TRACE" = 1 ] && \ echo_debug "nm" "+++ rdw_nm($iface).up.ignore_virtual" do_exit 0 fi # Get saved interface type (down action) if [ "$action" = "down" ]; then get_iface_type $iface # quit for virtual interfaces if [ "$itype" = "virtual" ]; then [ "$X_NET_VIRTUAL_TRACE" = 1 ] && \ echo_debug "nm" "+++ rdw_nm($iface).down.ignore_virtual" do_exit 0 fi fi echo_debug "nm" "+++ rdw_nm($iface).$action" # shellcheck disable=SC2154 if [ -n "$_addpath" ]; then # shellcheck disable=SC2154 echo_debug "path" "PATH=$_oldpath[$_addpath]" else # shellcheck disable=SC2154 echo_debug "path" "PATH=$_oldpath" fi # Determine interface type if [ -n "$itype" ]; then # saved type available (down action) echo_debug "nm" "rdw_nm($iface).$action: type=$itype [saved]" elif cmd_exists $NMCLI ; then # no saved type but nmcli is available # --> check if nmcli dev output matches interface itype="$($NMCLI dev | awk '$1 ~ /^'$iface'$/ { print $2; }')" if [ -z "$itype" ]; then # iface is not found in nmcli dev output: many WWAN devices have # different devices for control and the actual network connection # --> check if interface matches a WWAN device get_wwan_ifaces # shellcheck disable=SC2154 if wordinlist "$iface" "$_wanifaces"; then itype="wwan" else # fallback: # if interface type detection with nmcli failed, then try to # deduct it using interface name: it can happen if e.g. # usb network card is unplugged case "$iface" in en* | eth*) itype="ethernet" ;; wl*) itype="wifi" ;; ww*) itype="wwan" ;; *) itype="unknown" ;; esac fi fi # save interface type (up action) [ "$action" = "up" ] && save_iface_type "$iface" "$itype" echo_debug "nm" "rdw_nm($iface).$action: type=$itype [nmcli]" else # nmcli is not available itype="unknown" echo_debug "nm" "rdw_nm($iface)$action: type=$itype [none]" fi case $action in up) # interface up, disable configured interfaces case $itype in *ethernet) check_switch_lock LAN "$DEVICES_TO_DISABLE_ON_LAN_CONNECT" off ;; *wireless|wifi) check_switch_lock wifi "$DEVICES_TO_DISABLE_ON_WIFI_CONNECT" off ;; gsm|wwan) check_switch_lock wwan "$DEVICES_TO_DISABLE_ON_WWAN_CONNECT" off ;; esac ;; # up down) # interface down, enable configured interfaces case $itype in *ethernet) check_switch_lock LAN "$DEVICES_TO_ENABLE_ON_LAN_DISCONNECT" on ;; *wireless|wifi) check_switch_lock wifi "$DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT" on ;; gsm|wwan) check_switch_lock wwan "$DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT" on ;; esac ;; # down esac do_exit 0 TLP-1.3.1/tlp-rdw-udev.in000066400000000000000000000056411361705037700150540ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - handle dock/undock events # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 check_tlp_enabled || do_exit 0 check_run_flag $RDW_KILL && do_exit 0 add_sbin2path # get power source get_sys_power_supply # get device/type ddev=/sys$1 devtype=$2 case $devtype in dock) # check if type is "dock_station", quit if not type=$(read_sysf $ddev/type) [ "$type" = "dock_station" ] || do_exit 0 docked=$(read_sysf $ddev/docked) action=$EVENT # shellcheck disable=SC2154 echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev type=$type docked=$docked syspwr=$_syspwr" ;; usb_dock) # shellcheck disable=SC2153 case $ACTION in add) action="dock" ;; remove) action="undock" ;; esac echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev syspwr=$_syspwr" ;; *) do_exit 0 ;; # unknown device type esac # quit if timed lock in progress if check_timed_lock $RDW_DOCK_LOCK ; then echo_debug "udev" "rdw_udev.locked" do_exit 0 fi case $action in dock) # laptop was docked # lock for 2 seconds in case dock has multiple devices set_timed_lock $RDW_DOCK_LOCK $RDW_LOCKTIME # enable configured radios (obey rdw nm locks too) for dev in $DEVICES_TO_ENABLE_ON_DOCK; do [ -n "$dev" ] && ! check_timed_lock "${RDW_NM_LOCK}_${dev}" \ && device_switch $dev on "${RDW_NM_LOCK}_${dev}" $RDW_LOCKTIME done # disable configured radios (obey rdw nm locks too) for dev in $DEVICES_TO_DISABLE_ON_DOCK; do [ -n "$dev" ] && ! check_timed_lock "${RDW_NM_LOCK}_${dev}" \ && device_switch $dev off "${RDW_NM_LOCK}_${dev}" $RDW_LOCKTIME done ;; undock) # laptop was undocked # lock for 2 seconds in case dock has multiple devices set_timed_lock $RDW_DOCK_LOCK $RDW_LOCKTIME # enable configured radios (obey rdw nm locks too) for dev in $DEVICES_TO_ENABLE_ON_UNDOCK; do [ -n "$dev" ] && ! check_timed_lock "${RDW_NM_LOCK}_${dev}" \ && device_switch $dev on "${RDW_NM_LOCK}_${dev}" $RDW_LOCKTIME done # disable configured radios (obey rdw nm locks too) for dev in $DEVICES_TO_DISABLE_ON_UNDOCK; do [ -n "$dev" ] && ! check_timed_lock "${RDW_NM_LOCK}_${dev}" \ && device_switch $dev off "${RDW_NM_LOCK}_${dev}" $RDW_LOCKTIME done ;; *) ;; # unknown action -> do nothing esac do_exit 0 TLP-1.3.1/tlp-rdw.bash_completion000066400000000000000000000004331361705037700166450ustar00rootroot00000000000000# bash completion for TLP-RDW _tlp_rdw() { local cur prev words cword opts _init_completion || return opts="enable disable" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_rdw tlp-rdw TLP-1.3.1/tlp-rdw.in000066400000000000000000000016001361705037700141020ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - enable/disable RDW # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 carg1=$1 parse_args4config "$@" case $carg1 in enable) check_root reset_run_flag $RDW_KILL echo "tlp-rdw: enabled." ;; disable) check_root set_run_flag $RDW_KILL echo "tlp-rdw: disabled." ;; *) if check_run_flag $RDW_KILL; then echo "tlp-rdw: disabled." else echo "tlp-rdw: enabled." fi ;; esac do_exit 0 TLP-1.3.1/tlp-rdw.rules.in000066400000000000000000000032311361705037700152350ustar00rootroot00000000000000# tlp-rdw - udev rules # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # --- Dock/undock events # ThinkPad Advanced Mini Dock (and all older models), ThinkPad UltraBase ACTION=="change", SUBSYSTEM=="platform", KERNEL=="dock.*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p dock" # ThinkPad Mini Dock (Plus) Series 3 ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/100a/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Ultra Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1010/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Pro Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1012/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Pro Dock (USB3 Gigabit LAN interface) ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/304b/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/304f/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/3049/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Dock Plus ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/3054/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Pro Dock (CS18) ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/306f/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" TLP-1.3.1/tlp-readconfs.in000066400000000000000000000100671361705037700152610ustar00rootroot00000000000000#!/usr/bin/perl # tlp-readconfs - read all of TLP's config files # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Cmdline options # --outfile : filepath to contain merged configuration # --notrace: disable trace # # Return codes # 0: ok # 5: tlp.conf missing # 6: defaults.conf missing package tlp_readconfs; use strict; use warnings; # --- Modules use File::Basename; use Getopt::Long; # --- Constants use constant CONF_USR => "@TLP_CONFUSR@"; use constant CONF_DIR => "@TLP_CONFDIR@"; use constant CONF_DEF => "@TLP_CONFDEF@"; use constant CONF_OLD => "@TLP_CONF@"; # --- Global vars my @config_val = (); # 2-dim array: parameter name, value, source my %config_idx = (); # hash: parameter name ==> index into the name-value array my $notrace = 0; my $debug = 0; # --- Subroutines # Format and write debug message # @_: printf arguments including format string sub printf_debug { if ( not $notrace and $debug ) { open (my $logpipe, "|-", "logger -p debug -t \"tlp\" --id=\$\$ --") or return 1; printf {$logpipe} @_; close ($logpipe); } return 0; } # Store parameter name, value, source in array/hash # $_[0]: parameter name (non-null string) # $_[1]: parameter value (maybe null string) # $_[2]: parameter source e.g. filepath + line no. # return: 0=new name/1=known name sub store_name_value_source { my $name = $_[0]; my $value = $_[1]; my $source = $_[2]; $debug = 1 if ( $name eq "TLP_DEBUG" ) && ( $value =~ /\bcfg\b/ ); if ( defined $config_idx{$name} ) { # existing name --> overwrite value, source only $config_val[$config_idx{$name}][1] = $value; $config_val[$config_idx{$name}][2] = $source; printf_debug ("tlp-readconfs.replace [%s]: %s=\"%s\" %s\n", $config_idx{$name}, $name, $value, $source); } else { # new name --> store name, value, source and hash name push(@config_val, [$name, $value, $source]); $config_idx{$name} = $#config_val; printf_debug ("tlp-readconfs.insert [%s]: %s=\"%s\" %s\n", $#config_val, $name, $value, $source); } return 0; } # Parse whole config file and store parameters # $_[0]: filepath # return: 0=ok/1=file non-existent sub parse_configfile { my $fname = $_[0]; my $source; if ( $fname eq CONF_DEF ) { $source = basename ($fname); } else { $source = $fname; } open (my $cf, "<", $fname) or return 1; my $ln = 0; while ( my $line = <$cf> ) { chomp $line; $ln += 1; if ( $line =~ /^(?[A-Z_]+[0-9]*)=(?:(?[0-9a-zA-Z_\-:\.]*)|"(?[0-9a-zA-Z _\-:\.]*)")\s*$/ ) { my $name = $+{name}; my $value = $+{val_dquoted} // $+{val_bare}; store_name_value_source ($name, $value, $source . " L" . sprintf ("%04d", $ln) ); } } close ($cf); return 0; } # Output all stored parameter name, value to a file # or parameter name, value, source to stdout # $_[0]: filepath (without argument the output will be written to stdout) # return: 0=ok/1=file open error sub write_runconf { my $fname = $_[0]; my $runconf; if ( not $fname ) { $runconf = *STDOUT; } else { open ($runconf, ">", $fname) or return 1; } foreach ( @config_val ) { my ($name, $value, $source) = @$_; if ( $runconf eq *STDOUT ) { printf {$runconf} "%s: %s=\"%s\"\n", $source, $name, $value; } else { printf {$runconf} "%s=\"%s\"\n", $name, $value; } } close ($runconf); return 0 } # --- MAIN my $outfile = ''; # parse arguments GetOptions ('outfile=s' => \$outfile, 'notrace' => \$notrace); # 1. read intrinsic defaults parse_configfile (CONF_DEF) == 0 or exit 6; # 2. read customization foreach my $conffile ( grep { -f $_ } glob CONF_DIR . "/*.conf" ) { parse_configfile ($conffile); } # 3. read user settings parse_configfile (CONF_USR) == 0 or parse_configfile (CONF_OLD) == 0 or exit 5; # save result write_runconf ($outfile); exit 0; TLP-1.3.1/tlp-rf.in000066400000000000000000000022741361705037700137250ustar00rootroot00000000000000#!/bin/sh # tlp - switch bluetooth/wifi/wwan on/off # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086,SC2154 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do # shellcheck disable=SC1090 . $lib done # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 carg1=$1 parse_args4config "$@" add_sbin2path self=${0##*/} case $self in bluetooth|wifi|wwan) case $carg1 in on) device_switch $self on echo_device_state $self $_devs ;; off) device_switch $self off echo_device_state $self $_devs ;; toggle) device_switch $self toggle echo_device_state $self $_devs ;; *) device_state $self echo_device_state $self $_devs ;; esac ;; *) echo "Error: unknown device type \"$self\"." 1>&2 do_exit 1 ;; esac do_exit 0 TLP-1.3.1/tlp-run-on.in000066400000000000000000000014271361705037700145330ustar00rootroot00000000000000#!/bin/sh # tlp - run commands depending on power source # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base; do # shellcheck disable=SC1090 . $lib done # --- MAIN self=${0##*/} cmd=$1 if [ -z "$cmd" ]; then echo "Usage: $self command [arg(s)]" 1>&2 exit 1 fi if ! cmd_exists "$cmd"; then echo "Error: \"$cmd\" not found." 1>&2 exit 2 fi shift case $self in run-on-ac) if get_power_mode; then $cmd "$@" fi ;; run-on-bat) if ! get_power_mode; then $cmd "$@" fi ;; *) echo "Error: unknown mode $self." 1>&2 exit 1 ;; esac TLP-1.3.1/tlp-sleep000066400000000000000000000003571361705037700140210ustar00rootroot00000000000000#!/bin/sh # tlp - systemd suspend/resume hook # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. case $1 in pre) tlp suspend ;; post) tlp resume ;; esac TLP-1.3.1/tlp-sleep.elogind000066400000000000000000000002141361705037700154310ustar00rootroot00000000000000#!/bin/sh case "${1-}" in 'pre') exec tlp suspend ;; 'post') exec tlp resume ;; *) exit 64 ;; esac TLP-1.3.1/tlp-stat.in000066400000000000000000001134001361705037700142630ustar00rootroot00000000000000#!/bin/sh # tlp-stat - display power saving details # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086,SC2154 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/[0-9][0-9]* @TLP_FLIB@/tlp-func-stat; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- Constants readonly TLPUSB=@TLP_TLIB@/tlp-usblist readonly TLPPCI=@TLP_TLIB@/tlp-pcilist readonly TLPRDW=tlp-rdw readonly LSBREL=lsb_release readonly JOURNALCTL=journalctl readonly ASPM=/sys/module/pcie_aspm/parameters/policy readonly EFID=/sys/firmware/efi readonly NMIWD=/proc/sys/kernel/nmi_watchdog readonly WQPE=/sys/module/workqueue/parameters/power_efficient readonly CORETEMP_DIRS=" /sys/devices/platform/coretemp.0 /sys/devices/platform/coretemp.0/hwmon/hwmon*" readonly HWMONFAN_DIRS=" /sys/class/hwmon/hwmon*/device /sys/class/hwmon/hwmon*" readonly IBMFAN=/proc/acpi/ibm/fan readonly IBMTHERMAL=/proc/acpi/ibm/thermal readonly DEBUGLOG=/var/log/debug readonly SYSTEMD_SERVICES="tlp.service" readonly RFKILL_SERVICES="systemd-rfkill.service systemd-rfkill.socket" # --- Variables needs_root_priv= show_all=1 show_bat=0 show_conf=0 show_disk=0 show_graf=0 show_pcie=0 show_pev=0 show_proc=0 show_psup=0 show_rfkill=0 show_system=0 show_temp=0 show_trace=0 show_usb=0 show_verbose=0 show_warn=0 no_runtimepm=0 # --- Functions # @stdout glob_files ( glob_pattern, dir[, dir...] ) # # Nested for-loop that applies a glob expression to several directories # (or file path prefixes) and prints matching file paths to stdout. # glob_files () { [ -n "${1-}" ] || return 64 local glob_pattern file_iter glob_pattern="${1}" while shift && [ $# -gt 0 ]; do for file_iter in ${1}${glob_pattern}; do [ ! -f "${file_iter}" ] || printf '%s\n' "${file_iter}" done done } parse_args () { # parse command-line -- $@: arguments to parse # iterate arguments until delimiter '--' reached while [ $# -gt 0 ]; do case "$1" in "-b"|"--battery") show_all=0 show_bat=1 needs_root_priv=1 ;; "-c"|"--config") show_all=0 show_conf=1 : ${needs_root_priv:=0} ;; "-d"|"--disk") show_all=0 show_disk=1 needs_root_priv=1 ;; "-e"|"--pcie") show_all=0 show_pcie=1 : ${needs_root_priv:=0} ;; "-g"|"--graphics") show_all=0 show_graf=1 needs_root_priv=1 ;; "-p"|"--processor") show_all=0 show_proc=1 needs_root_priv=1 ;; "-r"|"--rfkill") show_all=0 show_rfkill=1 : ${needs_root_priv:=0} ;; "-s"|"--system") show_all=0 show_system=1 : ${needs_root_priv:=0} ;; "-t"|"--temp") show_all=0 show_temp=1 : ${needs_root_priv:=0} ;; "-u"|"--usb") show_all=0 show_usb=1 : ${needs_root_priv:=0} ;; "-v"|"--verbose") show_verbose=1 ;; "-w"|"--warn") show_all=0 show_warn=1 : ${needs_root_priv:=0} ;; "-P"|"--pev") show_all=0 show_pev=1 needs_root_priv=1 ;; "--psup") show_all=0 show_psup=1 : ${needs_root_priv:=0} ;; "-T"|"--trace") show_all=0 show_trace=1 needs_root_priv=1 ;; "--") # config values follow --> quit loop break ;; *) echo "Usage: tlp-stat [ -b | --battery | -c | --config |" echo " -d | --disk | -e | --pcie |" echo " -g | --graphics | -p | --processor |" echo " -r | --rfkill | -s | --system |" echo " -t | --temp | -u | --usb |" echo " -w | --warn | -v | --verbose |" echo " -P | --pev | | --psup |" echo " -T | --trace ]" do_exit 3 ;; esac shift # next argument done # while arguments return 0 } # --- MAIN # read configuration; continue on error, no trace read_config 0 1 parse_args "$@" parse_args4config "$@" add_sbin2path : ${needs_root_priv:=1} # inhibit trace output (unless forced) # shellcheck disable=SC2034 [ "$X_TRACE_TLP_STAT" = "1" ] || _nodebug=1 # check prerequisites if [ "$needs_root_priv" = "1" ]; then check_root load_modules $MOD_MSR $MOD_TEMP check_thinkpad check_battery_features fi echo "--- TLP $TLPVER --------------------------------------------" echo # --- show configuration if [ "$show_conf" = "1" ] || [ "$show_all" = "1" ]; then echo "+++ Configured Settings:" print_config echo fi # show_conf if [ "$show_system" = "1" ] || [ "$show_all" = "1" ] ; then # --- show system info # simulate arbitrary model if [ -z "$X_SIMULATE_MODEL" ]; then model="$(read_dmi product_version)" else model="$X_SIMULATE_MODEL" fi echo "+++ System Info" echo "System = $(read_dmi sys_vendor) $model $(read_dmi product_name)" echo "BIOS = $(read_dmi bios_version)" # --- show release & kernel info cmd_exists $LSBREL && echo "Release = $($LSBREL -d -s)" echo "Kernel = $(uname -r -m -v)" printparm "%-14s = %s" /proc/cmdline # --- show init system info if check_systemd; then echo "Init system = systemd $(systemd --version 2> /dev/null | sed -rn 's/systemd ([0-9]+)/v\1/p')" elif check_upstart; then echo "Init system = upstart" elif check_openrc; then echo "Init system = openrc" else echo "Init system = sysvinit" fi if [ -d $EFID ]; then echo "Boot mode = UEFI" else echo "Boot mode = BIOS (CSM, Legacy)" fi print_selinux echo # --- show TLP status echo "+++ TLP Status" if check_tlp_enabled; then printf "State = enabled\n" else printf "State = disabled\n" fi # --- show RDW status if cmd_exists $TLPRDW; then if ! check_tlp_enabled; then printf "RDW state = disabled (TLP disabled)\n" elif check_run_flag $RDW_KILL; then printf "RDW state = disabled\n" else printf "RDW state = enabled\n" fi else printf "RDW state = not installed\n" fi # --- show last invocation time printf "Last run = %s\n" "$(print_file_modtime_and_age $PWRRUNFILE)" # --- show actual power mode printf "Mode = %s\n" "$(print_saved_powerstate)" # ---- show actual power source get_sys_power_supply case $? in 0) printf "Power source = AC\n" ;; 1) printf "Power source = battery\n" ;; *) printf "Power source = unknown\n" ;; esac if check_ac_quirk "$model"; then echo "Notice: system may not detect AC/charger -- see: https://linrunner.de/en/tlp/docs/tlp-faq.html#acquirk" fi echo # -- check systemd service units status if check_systemd; then cnt=0 for su in $SYSTEMD_SERVICES; do if ! $SYSTEMCTL is-enabled $su > /dev/null 2>&1 ; then echo "Notice: $su is not enabled -- invoke \"systemctl enable $su\" to correct this!" cnt=$((cnt+1)) fi done for su in $RFKILL_SERVICES; do if $SYSTEMCTL is-enabled $su 2> /dev/null | grep -q -v 'masked'; then echo "Notice: $su is not masked -- invoke \"systemctl mask $su\" to correct this!" cnt=$((cnt+1)) fi done [ $cnt -gt 0 ] && echo fi # -- show warning if l-m-t detected check_laptop_mode_tools fi # show_system if [ "$show_proc" = "1" ] || [ "$show_all" = "1" ]; then # --- show cpu info echo "+++ Processor" sed -rn 's/model name[ \t]+: (.+)/CPU model = \1/p' /proc/cpuinfo | head -1 echo # -- show scaling gov and freq info epp=0 for cpuf in /sys/devices/system/cpu/cpu*/cpufreq; do if [ -f $cpuf/scaling_driver ]; then printparm "%-54s = ##%s##" $cpuf/scaling_driver printparm "%-54s = ##%s##" $cpuf/scaling_governor printparm "%s = ##%s##" $cpuf/scaling_available_governors _ if [ -f $cpuf/scaling_min_freq ]; then printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_min_freq" "$(read_sysf $cpuf/scaling_min_freq)" fi if [ -f $cpuf/scaling_max_freq ]; then printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_max_freq" "$(read_sysf $cpuf/scaling_max_freq)" fi if [ -f $cpuf/scaling_available_frequencies ]; then printf "%s = " "$cpuf/scaling_available_frequencies" for freq in $(read_sysf $cpuf/scaling_available_frequencies); do printf "%s " "$freq" done printf "[kHz]\n" fi if [ -f $cpuf/energy_performance_preference ]; then printparm "%s = ##%s## [HWP.EPP]" $cpuf/energy_performance_preference epp=1 fi if [ -f $cpuf/energy_performance_available_preferences ]; then printparm "%s = ##%s##" $cpuf/energy_performance_available_preferences fi printf "\n" fi done if check_intel_pstate; then # show Intel P-state info printparm "%-54s = ##%3d## [%%]" $CPU_MIN_PERF_PCT printparm "%-54s = ##%3d## [%%]" $CPU_MAX_PERF_PCT printparm "%-54s = ##%3d##" $CPU_TURBO_PSTATE printparm "%-54s = ##%3d## [%%]" $INTEL_PSTATED/turbo_pct printparm "%-54s = ##%3d##" $INTEL_PSTATED/num_pstates elif [ -f $CPU_BOOST_ALL_CTRL ]; then # show turbo boost info boost=$(read_sysval $CPU_BOOST_ALL_CTRL) # simple test for attribute "w" doesn't work, so actually write if write_sysf "$boost" $CPU_BOOST_ALL_CTRL; then printparm "%-54s = ##%d##" $CPU_BOOST_ALL_CTRL else printparm "%-54s = ##%d## (CPU not supported)" $CPU_BOOST_ALL_CTRL fi else printparm "%-54s = (not available)" $CPU_BOOST_ALL_CTRL fi # --- show sched power save info for pool in mc smp smt; do sdev="/sys/devices/system/cpu/sched_${pool}_power_savings" printparm "%-54s = ##%d##" $sdev _ done echo # --- show Intel energy and performance bias hint (EPB) if [ $epp -eq 0 ] || [ "$X_FORCE_EPB" = "1" ]; then # show only when HWP.EPP not present (or forced) if supports_intel_cpu_epb; then # CPU supports EPB: try native kernel API first (5.2 and later) cnt=0 for cpuf in /sys/devices/system/cpu/cpu*/power/energy_perf_bias; do if [ -f $cpuf ]; then printparm_epb $cpuf cnt=$((cnt + 1)) fi done if [ $cnt -gt 0 ]; then # native kernel API actually detected printf "\n" else # no native kernel API --> parse x86_energy_perf_policy output if cmd_exists $ENERGYPERF; then # check CPU support eperf=$($ENERGYPERF -r 2> /dev/null) case $? in 0) if [ -n "$eperf" ]; then # parse x86_energy_perf_policy output: # - replace numbers with descriptive strings # - remove ":" # - indent and align $ENERGYPERF -r 2>/dev/null | grep -v 'HWP_' | \ sed -r 's/://; s/(0x0000000000000000|EPB 0)/0 \(performance\)/; s/(0x0000000000000004|EPB 4)/4 \(balance_performance\)/; s/(0x0000000000000006|EPB 6)/6 \(default\)/; s/(0x0000000000000008|EPB 8)/8 \(balance_power\)/; s/(0x000000000000000f|EPB 15)/15 \(power\)/' | \ awk '{ printf "x86_energy_perf_policy.%-31s = %s %s [EPB]\n", $1, $2, $3; }' printf "\n" fi ;; 2) printf "x86_energy_perf_policy: program for your kernel not installed.\n\n" ;; *) printf "x86_energy_perf_policy: not available.\n\n" ;; esac else echo "x86_energy_perf_policy: program not installed." echo fi fi else # CPU does not support EPB printf "Intel EPB: unsupported CPU.\n\n" fi fi # --- show workqueue power efficient status printparm "%-54s = ##%s##" $WQPE # --- show nmi watchdog printparm "%-54s = ##%d##" $NMIWD echo # --- show PHC voltages phc_avail=0 for cpuf in /sys/devices/system/cpu/cpu*/cpufreq; do if [ -f $cpuf/phc_controls ]; then if [ $phc_avail = 0 ]; then echo "+++ Undervolting" phc_avail=1 fi printparm "%-58s = ##%s##" $cpuf/phc_controls printparm "%-58s = ##%s##" $cpuf/phc_default_controls echo fi done fi # show_proc if [ "$show_temp" = "1" ] || [ "$show_all" = "1" ]; then # --- show temperatures echo "+++ Temperatures" if [ -f $IBMTHERMAL ]; then # use thinkpad-specific sysfile echo "$IBMTHERMAL = $(read_sysf $IBMTHERMAL | cut -f2 ) [°C]" else # use sensors cmax=0 for sens in $(glob_files '/temp?*_input' $CORETEMP_DIRS); do if grep -q -- 'Physical' ${sens%input}label 2>/dev/null; then # package info is available -> ignore remaining sensors cmax=$(read_sysval $sens) break else # core info -> find max value ctemp=$(read_sysval $sens) [ $ctemp -gt $cmax ] && cmax=$ctemp fi done if [ $cmax -gt 0 ]; then perl -e 'printf ("CPU temp = %5d [°C]\n", '$cmax' / 1000.0);' fi fi # --- show fan speed if is_thinkpad && [ -f $IBMFAN ]; then # use thinkpad-specific sysfile awk '$1 ~ /speed:/ { printf "'$IBMFAN' = %5d [/min]\n", $2 }' $IBMFAN else # use hwmon have_any_fan= for fan in $(glob_files '/fan?*_input' $HWMONFAN_DIRS); do if fan_speed=$(read_sysval $fan); then fan_name="${fan##*/}"; fan_name="${fan_name%_input}" have_any_fan=y printf "Fan speed (%s) = %5d [/min]\n" \ "${fan_name}" "${fan_speed}" fi done if [ -z "${have_any_fan}" ]; then printf "Fan speed = (not available)\n" fi fi echo fi # show_temp if [ "$show_all" = "1" ]; then # --- show laptop-mode, dirty buffers params echo "+++ File System" printparm "%-38s = ##%5d##" /proc/sys/vm/laptop_mode printparm "%-38s = ##%5d##" /proc/sys/vm/dirty_writeback_centisecs printparm "%-38s = ##%5d##" /proc/sys/vm/dirty_expire_centisecs printparm "%-38s = ##%5d##" /proc/sys/vm/dirty_ratio printparm "%-38s = ##%5d##" /proc/sys/vm/dirty_background_ratio printparm "%-38s = ##%5d##" /proc/sys/fs/xfs/age_buffer_centisecs _ printparm "%-38s = ##%5d##" /proc/sys/fs/xfs/xfssyncd_centisecs _ printparm "%-38s = ##%5d##" /proc/sys/fs/xfs/xfsbufd_centisecs _ echo fi # show_all if [ "$show_disk" = "1" ] || [ "$show_all" = "1" ]; then # --- show storage device info echo "+++ Storage Devices" # list for storage device iteration disklist="$DISK_DEVICES" # list for output: print "(disabled)" when empty diskstat="${DISK_DEVICES:-(disabled)}" printf "Devices = %s\n" "$diskstat" # iterate over list if [ -n "$disklist" ]; then cnt=0 for dev in $disklist; do # iterate all devices show_disk_data $dev && cnt=$((cnt+1)) done fi echo # --- show sata alpm mode echo "+++ AHCI Link Power Management (ALPM)" if stat -t /sys/class/scsi_host/host*/link_power_management_policy > /dev/null 2>&1; then for i in /sys/class/scsi_host/host* ; do printparm "%-56s = ##%s##" $i/link_power_management_policy _ done else echo "No AHCI-enabled host controller detected." fi echo # --- show ahci runtime pm if stat -t ${AHCID}/power > /dev/null 2>&1; then echo "+++ AHCI Host Controller Runtime Power Management" for dev in ${AHCID}/power ; do printparm "%-40s = ##%s##" $dev/control done echo fi # -- show docks cnt=0 for dock in $DOCKGLOB; do [ ! -d $dock ] && break # no dock/bay detected # dock/bay detected, print header [ $cnt -eq 0 ] && echo "+++ Docks and Device Bays" cnt=$((cnt+1)) # get dock type { read -r dock_type < $dock/type; } 2>/dev/null # get dock state if check_is_docked; then # docked case $dock_type in ata_bay) dock_state="drive present" ;; battery_bay) dock_state="battery present" ;; dock_station) dock_state="docked" ;; *) dock_state="docked" dock_type="unknown" ;; esac else # not docked case $dock_type in ata_bay) dock_state="no drive (or powered off)" ;; battery_bay) dock_state="no battery " ;; dock_station) dock_state="undocked" ;; *) dock_state="undocked" dock_type="unknown" ;; esac fi # print dock data printf "%s: %-13s = %s\n" "$dock" "$dock_type" "$dock_state" done [ $cnt -gt 0 ] && echo fi # show_disk if [ "$show_graf" = "1" ] || [ "$show_all" = "1" ]; then # --- show Intel GPU power mgmt and frequencies show_intel_gpu_data # --- show radeon power profile or dpm state if [ -d $RADD ]; then for card in /sys/class/drm/card[0-9]/device ; do if [ -f $card/power_dpm_state ] && [ -f $card/power_dpm_force_performance_level ]; then # Use new radeon dpm state echo "+++ Radeon Graphics" printparm "%-25s = ##%s##" $card/power_dpm_state printparm "%-25s = ##%s##" $card/power_dpm_force_performance_level echo break elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then # Use old radeon power profile echo "+++ Radeon Graphics" printparm "%-25s = ##%s##" $card/power_method printparm "%-25s = ##%s##" $card/power_profile echo break fi done fi fi # show_graf if [ "$show_rfkill" = "1" ] || [ "$show_all" = "1" ]; then echo "+++ Wireless" # --- show rfkill state for i in bluetooth wifi wwan; do get_devc $i get_devs $i echo_device_state $i $_devs done echo ifshown=0 # --- show bluetooth get_bluetooth_ifaces for iface in $_bifaces; do if [ -n "$iface" ]; then ifshown=1 # get bluetooth driver get_bluetooth_driver $iface printf "%-30s: bluetooth, " "$iface($_btdrv)" if bluetooth_in_use $iface; then echo "connected" else echo "not connected" fi fi done # --- show wifi data get_wifi_ifaces for iface in $_wifaces; do if [ -n "$iface" ]; then ifshown=1 # get wifi power mgmt state wifipm="" if [ "$X_DONT_USE_IW" != "1" ] && cmd_exists $IW; then # try with iw first wifipm=$($IW dev $iface get power_save 2> /dev/null | \ grep 'Power save' | \ sed -r 's/.*Power save: (on|off).*/\1/') fi if cmd_exists $IWC; then if [ -z "$wifipm" ]; then # iw did not succeed or iw not installed -> try with iwconfig wifipm=$($IWC $iface 2> /dev/null | \ grep 'Power Management' | \ sed -r 's/.*Power Management:(on|off).*/\1/') fi fi # get wifi driver get_wifi_driver $iface printf "%-30s: wifi, " "$iface($_wifidrv)" if wireless_in_use $iface; then printf "connected, " else printf "not connected, " fi printf "power management = " case $wifipm in on|off) printf "%s" "$wifipm" ;; *) printf "unknown" ;; esac printf "\n" fi done # --- show wwan data get_wwan_ifaces for iface in $_wanifaces; do if [ -n "$iface" ]; then ifshown=1 # get wwan driver get_wwan_driver $iface printf "%-30s: wwan, " "$iface($_wwandrv)" if wireless_in_use $iface; then printf "connected" else printf "not connected" fi printf "\n" fi done [ "$ifshown" = "1" ] && echo fi # show_rfkill if [ "$show_all" = "1" ]; then # --- show sound power mode echo "+++ Audio" if [ -d /sys/module/snd_hda_intel ]; then printparm "%-58s = ##%s##" /sys/module/snd_hda_intel/parameters/power_save printparm "%-58s = ##%s##" /sys/module/snd_hda_intel/parameters/power_save_controller fi if [ -d /sys/module/snd_ac97_codec ]; then printparm "%s = ##%s##" /sys/module/snd_ac97_codec/parameters/power_save fi echo fi # show_all if [ "$show_pcie" = "1" ] || [ "$show_all" = "1" ]; then # --- show pcie aspm state echo "+++ PCIe Active State Power Management" if [ -f $ASPM ]; then pol=$(read_sysf $ASPM | sed -r 's/[[:space:]]+$//') apol=$(printf "%s" "$pol" | sed -r 's/.*\[(.*)\].*/\1/') if write_sysf "$apol" $ASPM; then echo "$ASPM = $pol" else echo "$ASPM = $pol (using BIOS preferences)" fi else echo "$ASPM = (not available)" fi echo # -- show runtime pm echo "+++ Runtime Power Management" echo "Device blacklist = ${RUNTIME_PM_BLACKLIST:=(not configured)}" # add "(disabled)" when empty echo "Driver blacklist = ${RUNTIME_PM_DRIVER_BLACKLIST:-(disabled)}" echo if cmd_exists $TLPPCI; then $TLPPCI [ $? -eq 4 ] && no_runtimepm=1 else echo "Error: missing subcommand $TLPPCI." 1>&2 fi echo fi # show_pcie if [ "$show_usb" = "1" ] || [ "$show_all" = "1" ]; then # -- show usb autosuspend echo "+++ USB" if [ "$USB_AUTOSUSPEND" = "1" ]; then echo "Autosuspend = enabled" else echo "Autosuspend = disabled" fi echo "Device whitelist = ${USB_WHITELIST:=(not configured)}" echo "Device blacklist = ${USB_BLACKLIST:=(not configured)}" if [ "${USB_BLACKLIST_BTUSB:-0}" = "1" ]; then echo "Bluetooth blacklist = enabled" else echo "Bluetooth blacklist = disabled" fi if [ "${USB_BLACKLIST_PHONE:-0}" = "1" ]; then echo "Phone blacklist = enabled" else echo "Phone blacklist = disabled" fi if [ "${USB_BLACKLIST_WWAN:-1}" = "1" ]; then echo "WWAN blacklist = enabled" else echo "WWAN blacklist = disabled" fi if [ -n "$USB_DRIVER_BLACKLIST" ] && [ "$USB_DRIVER_BLACKLIST" != "usbhid" ]; then echo "Notice: USB_DRIVER_BLACKLIST is no longer supported, use USB_BLACKLIST." fi echo if cmd_exists $TLPUSB; then $TLPUSB [ $? -eq 4 ] && no_runtimepm=1 else echo "Error: missing subcommand $TLPUSB." 1>&2 fi echo fi # show_usb if [ "$show_bat" = "1" ] || [ "$show_all" = "1" ]; then # --- show battery info & charge thresholds bcnt=0 efsum=0 ensum=0 # --- show availability of (ThinkPad) battery features echo "+++ Battery Features: Charge Thresholds and Recalibrate" # native ACPI kernel battery API case $_natacpi in 0|1) printf "natacpi = active "; print_methods_per_driver "natacpi" ;; 32) echo "natacpi = inactive (disabled by configuration)" ;; 128) echo "natacpi = inactive (no kernel support)" ;; 253) echo "natacpi = inactive (laptop not supported)" ;; 254) echo "natacpi = inactive (ThinkPad not supported)" ;; *) echo "natacpi = unknown status" ;; esac # ThinkPad-specific case $_tpacpi in 0) printf "tpacpi-bat = active "; print_methods_per_driver "tpacpi" ;; 32) echo "tpacpi-bat = inactive (disabled by configuration)" ;; 64) echo "tpacpi-bat = inactive (kernel module 'acpi_call' load error)" ;; 127) echo "tpacpi-bat = inactive (program 'tpacpi-bat' not installed)" ;; 128) echo "tpacpi-bat = inactive (kernel module 'acpi_call' not installed)" ;; 253) echo "tpacpi-bat = inactive (laptop not supported)" ;; 254) echo "tpacpi-bat = inactive (ThinkPad not supported)" ;; 255) echo "tpacpi-bat = inactive (superseded by natacpi)" ;; *) echo "tpacpi-bat = unknown status" ;; esac case $_tpsmapi in 0) printf "tp-smapi = active "; print_methods_per_driver "tpsmapi" ;; 1) printf "tp-smapi = readonly "; print_methods_per_driver "tpsmapi" ;; 32) echo "tp-smapi = inactive (disabled by configuration)" ;; 64) echo "tp-smapi = inactive (kernel module 'tp_smapi' load error)" ;; 128) echo "tp-smapi = inactive (kernel module 'tp_smapi' not installed)" ;; 253) echo "tp-smapi = inactive (laptop not supported)" ;; 254) echo "tp-smapi = inactive (ThinkPad not supported)" ;; 255) echo "tp-smapi = inactive (superseded by natacpi)" ;; *) echo "tp-smapi = unknown status" ;; esac echo case $_bm_read in natacpi) batglob="$ACPIBATDIR/*" ;; tpsmapi) batglob="$SMAPIBATDIR/BAT[01]" ;; esac # -- show battery data for batd in $batglob; do # iterate all batteries [ -d "$batd" ] || continue # skip if battery directory does not exist batt=${batd##/*/} battery_present $batt || continue # skip if battery is not present or atypical if is_thinkpad && [ "$_bm_thresh" != 'none' ]; then # ThinkPad with battery feature support case $_bat_idx in 1) echo "+++ ThinkPad Battery Status: $batt (Main / Internal)" ;; 2) echo "+++ ThinkPad Battery Status: $batt (Ultrabay / Slice / Replaceable)" ;; 0) echo "+++ ThinkPad Battery Status: $batt" ;; esac else # non-featured ThinkPad or other laptop make echo "+++ Battery Status: $batt" fi case $_bm_read in natacpi) # use ACPI data printparm "%-59s = ##%s##" $batd/manufacturer printparm "%-59s = ##%s##" $batd/model_name if cc=$(read_sysval $batd/cycle_count) && [ $cc -gt 0 ]; then printf "%-59s = %6d\n" "$batd/cycle_count" "$cc" else printf "%-59s = (not supported)\n" "$batd/cycle_count" fi if [ -f $batd/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $batd/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $batd/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $batd/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $batd/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $batd/energy_full_design) ef=$(read_sysval $batd/energy_full) en=$(read_sysval $batd/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $batd/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $batd/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $batd/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $batd/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $batd/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $batd/charge_full_design) ef=$(read_sysval $batd/charge_full) en=$(read_sysval $batd/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $batd/status echo if [ $show_verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $batd/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $batd/voltage_now "" 000 echo fi ;; # natacpi tpsmapi) # ThinkPad with active tp-smapi printparm "%-59s = ##%s##" $batd/manufacturer printparm "%-59s = ##%s##" $batd/model printparm "%-59s = ##%s##" $batd/manufacture_date printparm "%-59s = ##%s##" $batd/first_use_date printparm "%-59s = ##%6d##" $batd/cycle_count if [ -f $batd/temperature ]; then # shellcheck disable=SC2046 perl -e 'printf ("%-59s = %6d [°C]\n", "'$batd/temperature'", '$(read_sysval $batd/temperature)' / 1000.0);' fi printparm "%-59s = ##%6d## [mWh]" $batd/design_capacity printparm "%-59s = ##%6d## [mWh]" $batd/last_full_capacity printparm "%-59s = ##%6d## [mWh]" $batd/remaining_capacity printparm "%-59s = ##%6d## [%%]" $batd/remaining_percent printparm "%-59s = ##%6s## [min]" $batd/remaining_running_time_now printparm "%-59s = ##%6s## [min]" $batd/remaining_charging_time printparm "%-59s = ##%6d## [mW]" $batd/power_now printparm "%-59s = ##%6d## [mW]" $batd/power_avg print_batstate $batd/state echo if [ $show_verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $batd/design_voltage printparm "%-59s = ##%6s## [mV]" $batd/voltage printparm "%-59s = ##%6s## [mV]" $batd/group0_voltage printparm "%-59s = ##%6s## [mV]" $batd/group1_voltage printparm "%-59s = ##%6s## [mV]" $batd/group2_voltage printparm "%-59s = ##%6s## [mV]" $batd/group3_voltage echo fi # store values for charge / capacity calculation below ed=$(read_sysval $batd/design_capacity) ef=$(read_sysval $batd/last_full_capacity) en=$(read_sysval $batd/remaining_capacity) efsum=$((efsum + ef)) ensum=$((ensum + en)) ;; # tp-smapi esac # $_bm_read # --- show battery features: thresholds, force_discharge print_thresholds_discharge $batt # show charge + capacity lcnt=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lcnt=$((lcnt+1)) fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lcnt=$((lcnt+1)) fi [ $lcnt -gt 0 ] && echo bcnt=$((bcnt+1)) done # for batd if [ $bcnt -eq 0 ]; then # no battery detected printf "+++ Battery Status\n" printf "No battery data available.\n\n" elif [ $bcnt -gt 1 ] && [ $efsum -ne 0 ]; then # more than one battery detected --> show charge total perl -e 'printf ("%-59s = %6.1f [%%]\n", "+++ Charge total", 100.0 * '$ensum' / '$efsum');' echo fi fi # show_bat if [ "$show_warn" = "1" ] || [ "$show_disk" = "1" ] || [ "$show_all" = "1" ]; then # --- show warnings # ata errors (possibly) caused by SATA_LINKPWR_ON_AC/BAT != max_performance ecnt=$(check_ata_errors) if [ $ecnt -ne 0 ]; then echo "+++ Warnings" printf "* Kernel log shows ata errors (%d) possibly caused by the configuration\n" $ecnt printf " SATA_LINKPWR_ON_AC/BAT=min_power or medium_power.\n" printf " Consider using medium_power or max_performance instead.\n" printf " See the FAQ: https://linrunner.de/en/tlp/docs/tlp-faq.html#warnings\n" printf " Details:\n" dmesg | grep -E -A 5 "${RE_ATA_ERROR}" echo elif [ "$show_warn" = "1" ]; then echo "No warnings detected." echo "" fi fi # show_warn if [ "$show_bat" = "1" ] || [ "$show_all" = "1" ]; then # -- show recommendations reout="" if [ "$show_all" = "1" ] && [ "$no_runtimepm" = "1" ]; then reout="${reout}Reconfigure your Linux kernel with PM_RUNTIME=y to reduce your laptop's power consumption.\n" fi if is_thinkpad; then # add ThinkPad specific recommendations if [ $_tpsmapi -eq 128 ]; then if supports_tpsmapi_and_tpacpi; then reout="${reout}Install tp-smapi kernel modules for extended battery info (e.g. the cycle count)\n" else reout="${reout}Install tp-smapi kernel modules for ThinkPad battery thresholds and recalibration\n" fi fi case $_tpacpi in 127) missing="tpacpi-bat program" ;; 128) missing="acpi_call kernel module" ;; *) missing="" ;; esac if [ -n "$missing" ]; then case $_natacpi in 0) ;; # natacpi covers it all 1) reout="${reout}Install $missing for ThinkPad battery recalibration\n" ;; *) reout="${reout}Install $missing for ThinkPad battery thresholds and recalibration\n" ;; esac fi fi # if ThinkPad if [ "$show_all" = "1" ]; then # add other recommendations cmd_exists ethtool || reout="${reout}Install ethtool to disable Wake On LAN (WOL)\n" cmd_exists smartctl || reout="${reout}Install smartmontools for disk drive health info\n" fi if [ -n "$reout" ]; then echo "+++ Recommendations" # shellcheck disable=SC2059 # don't change to %s, $reout contains blanks and \n! printf "$reout" | sed -r 's/^/\* /' echo fi fi # show_all if [ "$show_pev" = "1" ]; then # --- show udev power_supply events # check for udevadm if cmd_exists $UDEVADM; then echo "+++ Monitor power supply events -- cancel with ^C" echo $UDEVADM monitor --udev --property --subsystem-match=power_supply fi fi # show_pev if [ "$show_psup" = "1" ]; then # --- show power_supply diagnostic echo "+++ Power supply diagnostic" for ps in /sys/class/power_supply/*; do # shellcheck disable=SC2063 grep -s '.*' $ps/type $ps/online $ps/present $ps/status $ps/device/path done fi # show_sup if [ "$show_trace" = "1" ]; then # --- show debug log # check for systemd journal jdone=0 if cmd_exists $JOURNALCTL; then # retrieve trace output from journal, rc=1 if journald has no data available if [ $show_verbose -eq 1 ]; then # verbose: show all output $JOURNALCTL -p debug --no-pager SYSLOG_IDENTIFIER=tlp 2> /dev/null && jdone=1 else # non-verbose: show output since last reboot only $JOURNALCTL -p debug --no-pager SYSLOG_IDENTIFIER=tlp -b 2> /dev/null && jdone=1 fi fi if [ "$jdone" = "0" ]; then # no journald data available --> retrieve trace output from logfile if [ -f $DEBUGLOG ]; then grep 'tlp\[' $DEBUGLOG else echo "Error: $DEBUGLOG does not exist." 1>&2 echo 1>&2 echo "Solution: create an rsyslog conffile /etc/rsyslog.d/90-debug.conf with the following contents" 1>&2 echo " *.=debug;\\" 1>&2 echo " mail,authpriv,cron.none;\\" 1>&2 echo " local0,local1,local3,local4,\\" 1>&2 echo " local5,local6,local7.none -/var/log/debug" 1>&2 echo "and restart the rsyslog daemon." 1>&2 echo 1>&2 fi fi fi # show_trace do_exit 0 TLP-1.3.1/tlp-usb-udev.in000066400000000000000000000025671361705037700150550ustar00rootroot00000000000000#!/bin/sh # tlp - handle added usb devices # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Remark: the calling udev rule is triggered for "base" devices only, # not for the corresponding subdevices. # shellcheck disable=SC2086 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/20-tlp-func-usb; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 # quit if TLP or autosuspend disabled # shellcheck disable=SC2015 check_tlp_enabled && [ "$USB_AUTOSUSPEND" = "1" ] || do_exit 0 # USB autosuspend has two principal operation modes: # # Mode 1 (optional): # - System startup is handled by tlp-functions:set_usb_suspend() # - Startup completion is signaled by "flag file" $USB_DONE # - Newly added devices are handled by this udev script # - Mode 1 is enabled by the private config variable X_TLP_USB_MODE=1 # # Mode 2 (default): # - Everything - including system startup, but not shutdown - is handled by this udev script # quit if mode 1 and no startup completion flag [ "$X_TLP_USB_MODE" = "1" ] && ! check_run_flag $USB_DONE && do_exit 0 if [ "$X_USB_ENV_TRACE" = "1" ]; then echo_debug "usb" "tlp_usb_udev.env = $(printenv)" fi # handle device usb_suspend_device "/sys$1" "udev" do_exit 0 TLP-1.3.1/tlp-usblist000066400000000000000000000060741361705037700144000ustar00rootroot00000000000000#!/usr/bin/perl # tlp-usblist - list usb device info with autosuspend attributes # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. package tlp_usblist; use strict; use warnings; # --- Constants use constant USBD => "/sys/bus/usb/devices"; # --- Global vars my %usbdevices; my $no_runtimepm = 0; # --- Subroutines # Read content from a sysfile # $_[0]: input file # return: content / empty string if nonexistent or not readable sub catsysf { my $fname = "$_[0]"; my $sysval = ""; if ( open (my $sysf, "<", $fname) ) { chomp ($sysval = <$sysf>); close ($sysf); } return $sysval; } # Read device driver from DEVICE/uevent # $_[0]: (sub)device base path # return: driver / empty string if uevent nonexistent or not readable sub getdriver { my $dpath = "$_[0]"; my $driver = ""; if ( open (my $sysf, "<", $dpath . "/uevent") ) { # read file line by line while (<$sysf>) { # match line content and return DRIVER= value if ( s/^DRIVER=(.*)/$1/ ) { chomp ($driver = $_); last; # break loop } } close ($sysf); } return $driver } # Get drivers associated with USB device by iterating subdevices # $_[0]: device base path # return: driver list / "no driver" if none found sub usbdriverlist { my $dpath = "$_[0]"; my $driverlist = ""; # iterate subdevices foreach my $subdev (glob $dpath . "/*:*") { # get subdevice driver my $driver = getdriver ("$subdev"); if ( $driver ) { if (index ($driverlist, $driver) == -1) { if ($driverlist) { $driverlist = $driverlist . ", " . $driver; } else { $driverlist = $driver; } } # if index } # if $driver } # foreach $subdev if (! $driverlist) { $driverlist = "no driver"; } return $driverlist } # --- MAIN # Check if Runtime PM is enabled $no_runtimepm = 1 if ( ! glob USBD . "/*/power/autosuspend*"); # Read USB device tree attributes as arrays into %usbdevices hash, indexed by Bus_Device foreach my $udev (grep { ! /:/ } glob USBD . "/*") { my $usbv = "(autosuspend not available)"; # get device id my $usbk = sprintf ("%03d_%03d", catsysf ("$udev/busnum"), catsysf ("$udev/devnum") ); # get attributes if ( length (my $asv = catsysf ("$udev/power/autosuspend_delay_ms")) && length (my $cnv = catsysf ("$udev/power/control")) ) { $usbv = sprintf ("control = %-5s autosuspend_delay_ms = %4d", $cnv . ",", $asv); } # save formatted result in hash @{$usbdevices{$usbk}} = ($udev, $usbv); } # Output device list with attributes and drivers foreach (`lsusb 2> /dev/null`) { my ($bus, $dev, $usbid, $desc) = /Bus (\S+) Device (\S+): ID (\S+)[ ]+(.*)/; my $usbk = $bus . "_" . $dev; $desc ||= ""; print "Bus $bus Device $dev ID $usbid $usbdevices{$usbk}[1] -- $desc (" . usbdriverlist ($usbdevices{$usbk}[0]) . ")\n"; } exit 4 if ( $no_runtimepm == 1 ); exit 0; TLP-1.3.1/tlp.bash_completion000066400000000000000000000035611361705037700160600ustar00rootroot00000000000000# bash completion for TLP _batteries() { # show list of batteries local bats b bats=$( { for b in /sys/class/power_supply/*; do if echo "$b" | grep -E -v -q "hid" \ && [ "$(cat $b/present 2> /dev/null)" = "1" ] \ && [ "$(cat $b/type)" = "Battery" ]; then echo "${b##/*/} " fi done } ) if [ -n "$bats" ]; then COMPREPLY=( $(compgen -W "${bats}" -- ${cur}) ) fi } _tlp() { local cur prev words cword opts bats _init_completion || return opts="start ac bat usb bayoff discharge setcharge fullcharge chargeonce recalibrate diskid" case $cword in 1) # subcmds only COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; 2) case "${prev}" in fullcharge|chargeonce|discharge|recalibrate) _batteries return 0 ;; esac ;; 4) if [ "${COMP_WORDS[1]}" = "setcharge" ]; then _batteries return 0 fi ;; esac } && complete -F _tlp tlp _tlp_rf() { local cur prev words cword opts _init_completion || return opts="on off toggle" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_rf bluetooth complete -F _tlp_rf wwan complete -F _tlp_rf wifi _tlp_stat() { local cur prev words cword opts _init_completion || return opts="--battery --config --disk --graphics --pcie --pev --psup --processor --rfkill --system --temp --usb --warn --trace --verbose" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_stat tlp-stat TLP-1.3.1/tlp.conf000066400000000000000000000351301361705037700136340ustar00rootroot00000000000000# ------------------------------------------------------------------------------ # /etc/tlp.conf - TLP user configuration # See full explanation: https://linrunner.de/en/tlp/docs/tlp-configuration.html # # New configuration scheme (TLP 1.3). Settings are read in the following order: # 1. Intrinsic defaults # 2. /etc/tlp.d/*.conf - Drop-in customization snippets # 3. /etc/tlp.conf - User configuration (this file) # # Notes: # - In case of identical parameters, the last occurence has precedence # - This also means, parameters enabled here will override anything else # - IMPORTANT: all parameters here are disabled, remove the leading '#' # to enable them; shown values may be suggestions rather than defaults # - Default *: intrinsic default that is effective when the parameter is missing # or disabled by a leading '#'; use PARAM="" to disable an intrinsic default # - Default : do nothing or use kernel/hardware defaults # ------------------------------------------------------------------------------ # tlp - Parameters for power saving # Set to 0 to disable, 1 to enable TLP. # Default: 1 #TLP_ENABLE=1 # Operation mode when no power supply can be detected: AC, BAT. # Concerns some desktop and embedded hardware only. # Default: #TLP_DEFAULT_MODE=AC # Operation mode select: 0=depend on power source, 1=always use TLP_DEFAULT_MODE # Note: use in conjunction with TLP_DEFAULT_MODE=BAT for BAT settings on AC. # Default: 0 #TLP_PERSISTENT_DEFAULT=0 # Power supply class to ignore when determining operation mode: AC, BAT. # Note: try on laptops where operation mode AC/BAT is incorrectly detected. # Default: #TLP_PS_IGNORE=BAT # Seconds laptop mode has to wait after the disk goes idle before doing a sync. # Non-zero value enables, zero disables laptop mode. # Default: 0 (AC), 2 (BAT) #DISK_IDLE_SECS_ON_AC=0 #DISK_IDLE_SECS_ON_BAT=2 # Dirty page values (timeouts in secs). # Default: 15 (AC), 60 (BAT) #MAX_LOST_WORK_SECS_ON_AC=15 #MAX_LOST_WORK_SECS_ON_BAT=60 # Note: CPU parameters below are disabled by default, remove the leading # # to enable them, otherwise kernel defaults will be used. # # Select a CPU frequency scaling governor. # Intel Core i processor with intel_pstate driver: # powersave(*), performance. # Other hardware with acpi-cpufreq driver: # ondemand(*), powersave, performance, conservative, schedutil. # (*) is recommended. # Use tlp-stat -p to show the active driver and available governors. # Important: # powersave for intel_pstate and ondemand for acpi-cpufreq are power # efficient for *almost all* workloads and therefore kernel and most # distributions have chosen them as defaults. If you still want to change, # you should know what you're doing! # Default: #CPU_SCALING_GOVERNOR_ON_AC=powersave #CPU_SCALING_GOVERNOR_ON_BAT=powersave # Set the min/max frequency available for the scaling governor. # Possible values depend on your CPU. For available frequencies see # the output of tlp-stat -p. # Default: #CPU_SCALING_MIN_FREQ_ON_AC=0 #CPU_SCALING_MAX_FREQ_ON_AC=0 #CPU_SCALING_MIN_FREQ_ON_BAT=0 #CPU_SCALING_MAX_FREQ_ON_BAT=0 # Set Intel CPU energy/performance policies HWP.EPP and EPB: # performance, balance_performance, default, balance_power, power # Values are given in order of increasing power saving. # Notes: # - Requires an Intel Core i processor # - HWP.EPP requires kernel 4.10 and intel_pstate driver # - EPB requires kernel 5.2 or module msr and x86_energy_perf_policy # from linux-tools # - When HWP.EPP is available, EPB is not set # Default: balance_performance (AC), balance_power (BAT) #CPU_ENERGY_PERF_POLICY_ON_AC=balance_performance #CPU_ENERGY_PERF_POLICY_ON_BAT=balance_power # Set Intel CPU P-state performance: 0..100 (%). # Limit the max/min P-state to control the power dissipation of the CPU. # Values are stated as a percentage of the available performance. # Requires an Intel Core i processor with intel_pstate driver. # Default: #CPU_MIN_PERF_ON_AC=0 #CPU_MAX_PERF_ON_AC=100 #CPU_MIN_PERF_ON_BAT=0 #CPU_MAX_PERF_ON_BAT=30 # Set the CPU "turbo boost" feature: 0=disable, 1=allow # Requires an Intel Core i processor. # Important: # - This may conflict with your distribution's governor settings # - A value of 1 does *not* activate boosting, it just allows it # Default: #CPU_BOOST_ON_AC=1 #CPU_BOOST_ON_BAT=0 # Minimize number of used CPU cores/hyper-threads under light load conditions: # 0=disable, 1=enable. # Default: 0 (AC), 1 (BAT) #SCHED_POWERSAVE_ON_AC=0 #SCHED_POWERSAVE_ON_BAT=1 # Kernel NMI Watchdog: # 0=disable (default, saves power), 1=enable (for kernel debugging only). # Default: 0 #NMI_WATCHDOG=0 # Change CPU voltages aka "undervolting" - Kernel with PHC patch required. # Frequency voltage pairs are written to: # /sys/devices/system/cpu/cpu0/cpufreq/phc_controls # CAUTION: only use this, if you thoroughly understand what you are doing! # Default: . #PHC_CONTROLS="F:V F:V F:V F:V" # Disk devices; separate multiple devices with spaces. # Devices can be specified by disk ID also (lookup with: tlp diskid). # Note: DISK parameters below are effective only when this option is configured. # Default: "nvme0n1 sda" #DISK_DEVICES="nvme0n1 sda" # Disk advanced power management level: 1..254, 255 (max saving, min, off). # Levels 1..127 may spin down the disk; 255 allowable on most drives. # Separate values for multiple disks with spaces. Use the special value 'keep' # to keep the hardware default for the particular disk. # Default: 254 (AC), 128 (BAT) #DISK_APM_LEVEL_ON_AC="254 254" #DISK_APM_LEVEL_ON_BAT="128 128" # Hard disk spin down timeout: # 0: spin down disabled # 1..240: timeouts from 5s to 20min (in units of 5s) # 241..251: timeouts from 30min to 5.5 hours (in units of 30min) # See 'man hdparm' for details. # Separate values for multiple disks with spaces. Use the special value 'keep' # to keep the hardware default for the particular disk. # Default: #DISK_SPINDOWN_TIMEOUT_ON_AC="0 0" #DISK_SPINDOWN_TIMEOUT_ON_BAT="0 0" # Select I/O scheduler for the disk devices. # Multi queue (blk-mq) schedulers: # mq-deadline(*), none, kyber, bfq # Single queue schedulers: # deadline(*), cfq, bfq, noop # (*) recommended. # Separate values for multiple disks with spaces. Use the special value 'keep' # to keep the kernel default scheduler for the particular disk. # Notes: # - Multi queue (blk-mq) may need kernel boot option 'scsi_mod.use_blk_mq=1' # and 'modprobe mq-deadline-iosched|kyber|bfq' on kernels < 5.0 # - Single queue schedulers are legacy now and were removed together with # the old block layer in kernel 5.0 # Default: keep #DISK_IOSCHED="mq-deadline mq-deadline" # AHCI link power management (ALPM) for disk devices: # min_power, med_power_with_dipm(*), medium_power, max_performance. # (*) Kernel >= 4.15 required, then recommended. # Multiple values separated with spaces are tried sequentially until success. # Default: # - "med_power_with_dipm max_performance" (AC) # - "med_power_with_dipm min_power" (BAT) #SATA_LINKPWR_ON_AC="med_power_with_dipm max_performance" #SATA_LINKPWR_ON_BAT="med_power_with_dipm min_power" # Exclude host devices from AHCI link power management. # Separate multiple hosts with spaces. # Default: #SATA_LINKPWR_BLACKLIST="host1" # Runtime Power Management for AHCI host and disks devices: # on=disable, auto=enable. # EXPERIMENTAL ** WARNING: auto may cause system lockups/data loss. # Default: #AHCI_RUNTIME_PM_ON_AC=on #AHCI_RUNTIME_PM_ON_BAT=on # Seconds of inactivity before disk is suspended. # Note: effective only when AHCI_RUNTIME_PM_ON_AC/BAT is activated. # Default: 15 #AHCI_RUNTIME_PM_TIMEOUT=15 # PCI Express Active State Power Management (PCIe ASPM): # default(*), performance, powersave, powersupersave. # (*) keeps BIOS ASPM defaults (recommended) # Default: #PCIE_ASPM_ON_AC=default #PCIE_ASPM_ON_BAT=default # Set the min/max/turbo frequency for the Intel GPU. # Possible values depend on your hardware. For available frequencies see # the output of tlp-stat -g. # Default: #INTEL_GPU_MIN_FREQ_ON_AC=0 #INTEL_GPU_MIN_FREQ_ON_BAT=0 #INTEL_GPU_MAX_FREQ_ON_AC=0 #INTEL_GPU_MAX_FREQ_ON_BAT=0 #INTEL_GPU_BOOST_FREQ_ON_AC=0 #INTEL_GPU_BOOST_FREQ_ON_BAT=0 # Radeon graphics clock speed (profile method): low, mid, high, auto, default; # auto = mid on BAT, high on AC. # Default: default #RADEON_POWER_PROFILE_ON_AC=default #RADEON_POWER_PROFILE_ON_BAT=default # Radeon dynamic power management method (DPM): battery, performance. # Default: #RADEON_DPM_STATE_ON_AC=performance #RADEON_DPM_STATE_ON_BAT=battery # Radeon DPM performance level: auto, low, high; auto is recommended. # Note: effective only when RADEON_DPM_STATE_ON_AC/BAT is activated. # Default: auto #RADEON_DPM_PERF_LEVEL_ON_AC=auto #RADEON_DPM_PERF_LEVEL_ON_BAT=auto # WiFi power saving mode: on=enable, off=disable; not supported by all adapters. # Default: off (AC), on (BAT) #WIFI_PWR_ON_AC=off #WIFI_PWR_ON_BAT=on # Disable wake on LAN: Y/N. # Default: Y #WOL_DISABLE=Y # Enable audio power saving for Intel HDA, AC97 devices (timeout in secs). # A value of 0 disables, >=1 enables power saving (recommended: 1). # Default: 0 (AC), 1 (BAT) #SOUND_POWER_SAVE_ON_AC=0 #SOUND_POWER_SAVE_ON_BAT=1 # Disable controller too (HDA only): Y/N. # Note: effective only when SOUND_POWER_SAVE_ON_AC/BAT is activated. # Default: Y #SOUND_POWER_SAVE_CONTROLLER=Y # Power off optical drive in UltraBay/MediaBay: 0=disable, 1=enable. # Drive can be powered on again by releasing (and reinserting) the eject lever # or by pressing the disc eject button on newer models. # Note: an UltraBay/MediaBay hard disk is never powered off. # Default: 0 #BAY_POWEROFF_ON_AC=0 #BAY_POWEROFF_ON_BAT=0 # Optical drive device to power off # Default: sr0 #BAY_DEVICE="sr0" # Runtime Power Management for PCI(e) bus devices: on=disable, auto=enable. # Default: on (AC), auto (BAT) #RUNTIME_PM_ON_AC=on #RUNTIME_PM_ON_BAT=auto # Exclude PCI(e) device adresses the following list from Runtime PM # (separate with spaces). Use lspci to get the adresses (1st column). # Default: #RUNTIME_PM_BLACKLIST="bb:dd.f 11:22.3 44:55.6" # Exclude PCI(e) devices assigned to the listed drivers from Runtime PM. # Default when unconfigured is "amdgpu nouveau nvidia radeon" which # prevents accidential power-on of dGPU in hybrid graphics setups. # Separate multiple drivers with spaces. # Default: "amdgpu mei_me nouveau nvidia pcieport radeon", use "" to disable # completely. #RUNTIME_PM_DRIVER_BLACKLIST="amdgpu mei_me nouveau nvidia pcieport radeon" # Set to 0 to disable, 1 to enable USB autosuspend feature. # Default: 1 #USB_AUTOSUSPEND=1 # Exclude listed devices from USB autosuspend (separate with spaces). # Use lsusb to get the ids. # Note: input devices (usbhid) are excluded automatically # Default: #USB_BLACKLIST="1111:2222 3333:4444" # Bluetooth devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 0 #USB_BLACKLIST_BTUSB=0 # Phone devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude (enable charging). # Default: 0 #USB_BLACKLIST_PHONE=0 # Printers are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 1 #USB_BLACKLIST_PRINTER=1 # WWAN devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 0 #USB_BLACKLIST_WWAN=0 # Include listed devices into USB autosuspend even if already excluded # by the blacklists above (separate with spaces). Use lsusb to get the ids. # Default: #USB_WHITELIST="1111:2222 3333:4444" # Set to 1 to disable autosuspend before shutdown, 0 to do nothing # Note: use as a workaround for USB devices that cause shutdown problems. # Default: 0 #USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN=0 # Restore radio device state (Bluetooth, WiFi, WWAN) from previous shutdown # on system startup: 0=disable, 1=enable. # Note: the parameters DEVICES_TO_DISABLE/ENABLE_ON_STARTUP/SHUTDOWN below # are ignored when this is enabled. # Default: 0 #RESTORE_DEVICE_STATE_ON_STARTUP=0 # Radio devices to disable on startup: bluetooth, wifi, wwan. # Separate multiple devices with spaces. # Default: #DEVICES_TO_DISABLE_ON_STARTUP="bluetooth wifi wwan" # Radio devices to enable on startup: bluetooth, wifi, wwan. # Separate multiple devices with spaces. # Default: #DEVICES_TO_ENABLE_ON_STARTUP="wifi" # Radio devices to disable on shutdown: bluetooth, wifi, wwan. # Note: use as a workaround for devices that are blocking shutdown. # Default: #DEVICES_TO_DISABLE_ON_SHUTDOWN="bluetooth wifi wwan" # Radio devices to enable on shutdown: bluetooth, wifi, wwan. # (to prevent other operating systems from missing radios). # Default: #DEVICES_TO_ENABLE_ON_SHUTDOWN="wwan" # Radio devices to enable on AC: bluetooth, wifi, wwan. # Default: #DEVICES_TO_ENABLE_ON_AC="bluetooth wifi wwan" # Radio devices to disable on battery: bluetooth, wifi, wwan. # Default: #DEVICES_TO_DISABLE_ON_BAT="bluetooth wifi wwan" # Radio devices to disable on battery when not in use (not connected): # bluetooth, wifi, wwan. # Default: #DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth wifi wwan" # Battery charge thresholds (ThinkPad only). # May require external kernel module(s), refer to the output of tlp-stat -b. # Charging starts when the remaining capacity falls below the # START_CHARGE_THRESH value and stops when exceeding the STOP_CHARGE_THRESH # value. # Main / Internal battery (values in %) # Default: #START_CHARGE_THRESH_BAT0=75 #STOP_CHARGE_THRESH_BAT0=80 # Ultrabay / Slice / Replaceable battery (values in %) # Default: #START_CHARGE_THRESH_BAT1=75 #STOP_CHARGE_THRESH_BAT1=80 # Restore charge thresholds when AC is unplugged: 0=disable, 1=enable. # Default: 0 #RESTORE_THRESHOLDS_ON_BAT=1 # Battery feature drivers: 0=disable, 1=enable # Default: 1 (all) #NATACPI_ENABLE=1 #TPACPI_ENABLE=1 #TPSMAPI_ENABLE=1 # ------------------------------------------------------------------------------ # tlp-rdw - Parameters for the radio device wizard # Possible devices: bluetooth, wifi, wwan. # Separate multiple radio devices with spaces. # Default: (for all parameters below) # Radio devices to disable on connect. #DEVICES_TO_DISABLE_ON_LAN_CONNECT="wifi wwan" #DEVICES_TO_DISABLE_ON_WIFI_CONNECT="wwan" #DEVICES_TO_DISABLE_ON_WWAN_CONNECT="wifi" # Radio devices to enable on disconnect. #DEVICES_TO_ENABLE_ON_LAN_DISCONNECT="wifi wwan" #DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT="" #DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT="" # Radio devices to enable/disable when docked. #DEVICES_TO_ENABLE_ON_DOCK="" #DEVICES_TO_DISABLE_ON_DOCK="" # Radio devices to enable/disable when undocked. #DEVICES_TO_ENABLE_ON_UNDOCK="wifi" #DEVICES_TO_DISABLE_ON_UNDOCK="" TLP-1.3.1/tlp.in000066400000000000000000000332231361705037700133160ustar00rootroot00000000000000#!/bin/sh # tlp - adjust power settings # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2086 # --- Source libraries for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/[0-9][0-9]*; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- Constants # --- Subroutines apply_common_settings () { # apply settings common to all modes # $1: 0=ac mode, 1=battery mode set_laptopmode $1 set_dirty_parms $1 set_cpu_scaling_governor $1 set_cpu_scaling_min_max_freq $1 set_intel_cpu_perf_policy $1 set_intel_cpu_perf_pct $1 set_cpu_boost_all $1 set_sched_powersave $1 set_nmi_watchdog set_phc_controls $1 set_disk_apm_level $1 set_disk_spindown_timeout $1 set_disk_iosched set_sata_link_power $1 set_ahci_runtime_pm $1 set_pcie_aspm $1 set_intel_gpu_min_max_boost_freq $1 set_radeon_profile $1 set_wifi_power_mode $1 disable_wake_on_lan set_sound_power_mode $1 set_runtime_pm $1 return 0 } show_usage () { echo "Usage: tlp start|true|bat|false|ac|usb|bayoff|chargeonce|discharge|setcharge|fullcharge|recalibrate|diskid" 1>&2 } parse_args () { # parse command-line arguments # $@: arguments to parse # retval: $_cmd: command; # $_cmd2: subcommand; # $_carg1, # $_carg2, # $_carg3: command arguments # parsing control: 'nil' means that the element is still expected _cmd="nil" _cmd2="nil" _carg1="nil" _carg2="nil" _carg3="nil" # iterate arguments until exhausted or delimiter '--' reached while [ $# -gt 0 ]; do if [ "$1" = "--" ]; then break; elif [ "$_cmd" = "nil" ]; then # command case $1 in ac|auto|bat|bayoff|false|diskid|resume|suspend|start|true|usb) # commands without further arguments _cmd="$1" _cmd2="" _carg1="" _carg2="" _carg3="" ;; chargeonce|discharge|fullcharge|recalibrate) # commands with one or no arguments _cmd="$1" _cmd2="" _carg2="" _carg3="" ;; setcharge) # command with up to three arguments _cmd="$1" _cmd2="" ;; init) # command with subcommand and no arguments _cmd="$1" _carg1="" _carg2="" _carg3="" ;; stat) # unsupported command echo "Error: 'tlp stat' no longer supported, use 'tlp-stat' instead." 1>&2 do_exit 3 ;; noop) # no operation _cmd="$1" _cmd2="" ;; *) # unknown command echo "Error: unknown command \"$1\"." 1>&2 show_usage do_exit 3 ;; esac elif [ "$_cmd2" = "nil" ]; then # subcommand case $1 in start|stop|restart|force-reload) _cmd2="$1" ;; *) # unknown subcommand echo "Usage: tlp init {start|stop|restart|force-reload}" >&2 do_exit 3 ;; esac elif [ "$_carg1" = "nil" ]; then # first command argument _carg1="$1" elif [ "$_carg2" = "nil" ]; then # second command argument _carg2="$1" elif [ "$_carg3" = "nil" ]; then # third command argument _carg3="$1" fi shift # next argument done # while arguments if [ "$_cmd" = "nil" ]; then # no command parsed show_usage do_exit 3 fi # clear missing arguments [ "$_carg1" = "nil" ] && _carg1="" [ "$_carg2" = "nil" ] && _carg2="" [ "$_carg3" = "nil" ] && _carg3="" return 0 } # --- MAIN # read configuration: quit on error, trace allowed read_config 1 0 parse_args "$@" parse_args4config "$@" check_tlp_enabled 1 || do_exit 1 add_sbin2path check_laptop_mode_tools # get current power state get_power_mode; pwrmode=$? get_manual_mode if [ -z "$_cmd2" ]; then echo_debug "run" "+++ $_cmd ($TLPVER) ++++++++++++++++++++++++++++++++++++++++" else echo_debug "run" "+++ $_cmd $_cmd2 ($TLPVER) ++++++++++++++++++++++++++++++++++++++++" fi # shellcheck disable=SC2154 if [ -n "$_addpath" ]; then # shellcheck disable=SC2154 echo_debug "path" "PATH=$_oldpath[$_addpath]" else # shellcheck disable=SC2154 echo_debug "path" "PATH=$_oldpath" fi # determine new power state case "$_cmd" in init|start) # discard manual mode clear_manual_mode ;; auto|resume) # if manual mode is set, use instead of current power state # shellcheck disable=SC2154 [ "$_manual_mode" != "none" ] && pwrmode=$_manual_mode ;; true|bat) pwrmode=1 set_manual_mode 1 ;; false|ac) pwrmode=0 set_manual_mode 0 ;; esac # shellcheck disable=SC2154 case "$_syspwr" in 0) echo_debug "run" "power_source=ac" ;; 1) echo_debug "run" "power_source=bat" ;; *) echo_debug "run" "power_source=unknown ($_syspwr)" ;; esac case "$_manual_mode" in 0) echo_debug "run" "manual_mode=ac" ;; 1) echo_debug "run" "manual_mode=bat" ;; *) echo_debug "run" "manual_mode=none" ;; esac case "$pwrmode" in 0) echo_debug "run" "power_mode=ac" ;; 1) echo_debug "run" "power_mode=bat" ;; *) echo_debug "run" "power_mode=unknown ($pwrmode)" ;; esac exitcode=0 case "$_cmd" in init) # system initialization/shutdown: sysv, upstart, systemd, ... check_root # try to obtain lock (with timeout) locked=0 if lock_tlp; then locked=1 else echo "Failed to get lock, continuing anyway." 1>&2 fi # do init business ... case $_cmd2 in start) # apply power save settings compare_and_save_power_state $pwrmode echo -n "Applying power save settings..." apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto echo "done." # apply battery settings echo -n "Setting battery charge thresholds..." set_charge_thresholds echo "done." # apply radio states set_radio_device_states start ;; restart|force-reload) # apply power save settings compare_and_save_power_state $pwrmode echo -n "Applying power save settings..." apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto echo "done." # apply battery settings echo -n "Setting battery charge thresholds..." set_charge_thresholds echo "done." ;; stop) # remove usb startup flag [ -f $USB_DONE ] && rm $USB_DONE # clear saved power state clear_saved_power_state # apply ac settings for faster shutdown echo -n "Applying power save settings..." apply_common_settings 0 poweroff_drivebay $pwrmode 0 echo "done." # disable usb autosuspend if configured if [ "$USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN" = "1" ]; then echo -n "Disabling usb autosuspend..." set_usb_suspend 0 on echo "done." fi # apply radio states set_radio_device_states stop ;; *) echo "Usage: tlp init {start|stop|restart|force-reload}" >&2 do_exit 3 ;; esac save_runconf # unlock if necessary [ $locked -eq 0 ] || unlock_tlp ;; auto) # set mode depending on state (called by udev rule) # -- but only if not previously run for the same power state # rationale: filter out duplicate power_supply udev events check_root if lock_tlp_nb; then if compare_and_save_power_state $pwrmode; then apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 set_radio_device_states $pwrmode if [ "$RESTORE_THRESHOLDS_ON_BAT" = "1" ] \ && [ "$pwrmode" = "1" ]; then set_charge_thresholds fi save_runconf fi unlock_tlp fi ;; start) # set mode depending on state (interactive mode) check_root if lock_tlp; then compare_and_save_power_state $pwrmode apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 set_usb_suspend 0 auto set_charge_thresholds set_radio_device_states $pwrmode save_runconf unlock_tlp echo_started_mode $pwrmode else echo_tlp_locked fi ;; true|bat) # set battery power mode check_root if lock_tlp; then compare_and_save_power_state 1 apply_common_settings 1 poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto set_radio_device_states 1 save_runconf unlock_tlp echo_started_mode 1 else echo_tlp_locked fi ;; false|ac) # set ac power mode check_root if lock_tlp; then compare_and_save_power_state 0 apply_common_settings 0 poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto set_radio_device_states 0 save_runconf unlock_tlp echo_started_mode 0 else echo_tlp_locked fi ;; suspend) # handle suspend/hibernate check_root if lock_tlp; then save_device_states "bluetooth wwan" apply_common_settings 0 suspend_drivebay $pwrmode save_runconf unlock_tlp fi ;; resume) # handle resume check_root if lock_tlp; then restore_device_states compare_and_save_power_state $pwrmode apply_common_settings $pwrmode resume_drivebay $pwrmode save_runconf unlock_tlp fi ;; usb) # Enable usb autosuspend check_root set_usb_suspend 1 auto ;; bayoff) # power off drive bay check_root poweroff_drivebay $pwrmode 1 ;; setcharge) # set charge thresholds (temporarily) check_root setcharge_battery $_carg1 $_carg2 $_carg3 exitcode=$? ;; fullcharge) # charge battery to 100% (temporarily) if check_ac_power fullcharge; then check_root setcharge_battery 0 0 $_carg1 exitcode=$? if [ $exitcode -eq 0 ]; then echo "Charging starts now, keep AC connected." fi else exitcode=2 fi ;; chargeonce) # charge battery to stop threshold once if check_ac_power chargeonce; then check_root chargeonce_battery $_carg1 exitcode=$? if [ $exitcode -eq 0 ]; then echo "Charging starts now, keep AC connected." fi else exitcode=2 fi ;; discharge) # discharge battery completely (to recalibrate) if check_ac_power discharge; then check_root if lock_tlp_nb tlp_discharge; then discharge_battery $_carg1 exitcode=$? unlock_tlp tlp_discharge else echo_discharge_locked fi else exitcode=2 fi ;; recalibrate) # recalibrate battery, i.e. discharge and charge to 100% if check_ac_power recalibrate; then check_root if lock_tlp_nb tlp_discharge; then setcharge_battery 96 100 $_carg1 sleep 1 discharge_battery $_carg1 exitcode=$? if [ $exitcode -eq 0 ]; then echo "Charging starts now, for a complete recalibration" echo "keep AC connected until the battery is fully charged." fi unlock_tlp tlp_discharge else echo_discharge_locked fi else exitcode=2 fi ;; diskid) # show disk id's show_disk_ids ;; noop) # Debug: no operation check_root battery_present DEF save_runconf echo "Debug: no operation performed." ;; esac do_exit $exitcode TLP-1.3.1/tlp.init000066400000000000000000000014651361705037700136560ustar00rootroot00000000000000#!/bin/sh # tlp - system startup/shutdown # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # # chkconfig: 2345 98 01 ### BEGIN INIT INFO # Provides: tlp # Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: tlp start/stop script # Description: Initialize tlp ### END INIT INFO [ -r /lib/lsb/init-functions ] && . /lib/lsb/init-functions TLP=/usr/sbin/tlp [ -x $TLP ] || exit 0 case "$1" in status) tlp-stat -s ;; start|\ stop|\ restart|\ force-reload) $TLP init $1 ;; *) echo "Usage: $0 start|stop|restart|force-reload|status" 1>&2 exit 3 ;; esac exit 0 TLP-1.3.1/tlp.rules.in000066400000000000000000000006611361705037700144470ustar00rootroot00000000000000# tlp - udev rules # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # handle change of power source ac/bat ACTION=="change", SUBSYSTEM=="power_supply", RUN+="@TLP_SBIN@/tlp auto" # handle added usb devices (exclude subdevices via DRIVER=="USB") ACTION=="add", SUBSYSTEM=="usb", DRIVER=="usb", ENV{DEVTYPE}=="usb_device", RUN+="@TLP_ULIB@/tlp-usb-udev %p" TLP-1.3.1/tlp.service.in000066400000000000000000000007331361705037700147550ustar00rootroot00000000000000# tlp - systemd startup/shutdown service # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. [Unit] Description=TLP system startup/shutdown After=multi-user.target NetworkManager.service Before=shutdown.target Documentation=https://linrunner.de/tlp [Service] Type=oneshot RemainAfterExit=yes ExecStart=@TLP_SBIN@/tlp init start ExecStop=@TLP_SBIN@/tlp init stop [Install] WantedBy=multi-user.target TLP-1.3.1/tlp.upstart.in000066400000000000000000000006601361705037700150160ustar00rootroot00000000000000# tlp - system startup/shutdown # # Copyright (c) 2020 Thomas Koch and others. # This software is licensed under the GPL v2 or later. description "tlp" start on ( virtual-filesystems and runlevel [2345] ) stop on runlevel [!2345] env TLP=@TLP_SBIN@/tlp pre-start script [ -x $TLP ] || exit 4 $TLP init start end script post-stop script [ -x $TLP ] || exit 4 $TLP init stop end script TLP-1.3.1/tpacpi-bat000066400000000000000000000336441361705037700141450ustar00rootroot00000000000000#!/usr/bin/perl ########################################################################## # Thinkpad ACPI Battery Control # Copyright 2011 Elliot Wolk ########################################################################## # 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 . ########################################################################## # Exposes inhibit charge, start/stop charge threshold, and force discharge # through ACPI as an alternative to SMAPI, which is broken in W520, etc. # # Makes ACPI calls using the acpi_call kernel module, which is REQUIRED. # # Supports 2011-released thinkpads, and possibly later ones. # Tested and works on: W520, T420, X220, E130 # Tested and does NOT work on: T400, X201S # # Limitations/Known Issues: # 1) you cant force discharge on battery, so balancing is still impossible # 2) sometimes you cant convince a slice battery to charge before the main # ANYONE who figures out this second issue gets a cookie! # if my main is above 80, and my slice is at 80, my main charges. # i can inhibit the main, but i CANNOT trick the slice into charging # 3) you can only inhibit for 720 minutes instead of 1440 minutes # this seems like a BIOS error ########################################################################## use strict; use warnings; use File::Basename; my $acpiCallDev = '/proc/acpi/call'; my $psDeviceGlob = "/sys/class/power_supply/{BAT0,BAT1,AC,ADP0,ADP1}/device/path"; sub getMethod($$); sub setMethod($$@); sub readPeakShiftState($); sub readInhibitCharge($); sub readStartChargeThreshold($); sub readStopChargeThreshold($); sub readForceDischarge($); sub writePeakShiftState($); sub writeInhibitCharge($); sub writeStartChargeThreshold($); sub writeStopChargeThreshold($); sub writeForceDischarge($); sub acpiCall($); sub runAcpiCall($$); sub acpiCallGet($$); sub acpiCallSet($$); sub revpadzero($$); sub bitRangeToDec(\@$$); sub parseArgDecToBin($); sub parseStatusHexToBitArray($); sub decToBin($); sub binToDec($); sub hexToBin($); sub binToHex($); sub synList(@); our $verbose = 0; my ($methodST, $methodSP, $methodIC, $methodFD, $methodPS) = ("ST", "SP", "IC", "FD", "PS"); my $methodSyns = { $methodST => synList ("st", "startThreshold", "start"), $methodSP => synList ("sp", "stopThreshold", "stop"), $methodIC => synList ("ic", "inhibitCharge", "inhibit"), $methodFD => synList ("fd", "forceDischarge"), $methodPS => synList ("ps", "peakShiftState"), }; my $name = File::Basename::basename $0; my $usage = "Usage: Show this message: $name [-h|--help] Get charge thresholds / inhibit charge / force discharge: $name [-v] -g $methodST $name [-v] -g $methodSP $name [-v] -g $methodIC $name [-v] -g $methodFD Set charge thresholds / inhibit charge / force discharge: $name [-v] -s $methodST $name [-v] -s $methodSP $name [-v] -s $methodIC [] $name [-v] -s $methodFD [] Set peak shift state, which is mysterious and inhibits charge: $name [-v] -s $methodPS [] Synonyms: $methodST -> $$methodSyns{$methodST} $methodSP -> $$methodSyns{$methodSP} $methodIC -> $$methodSyns{$methodIC} $methodFD -> $$methodSyns{$methodFD} $methodPS -> $$methodSyns{$methodPS} Options: -v show ASL call and response 1 for main, 2 for secondary, 0 for either/both number of minutes, or 0 for never, or 65535 for forever 0 for default, 1-99 for percentage 1 for inhibit charge, 0 for stop inhibiting charge 1 for force discharge, 0 for stop forcing discharge 1 for stop forcing when AC is detached, 0 for do not [] means optional: sets value to 0 "; my $noReadMethods = join "|", ($methodPS); my $noBothReadMethods = join "|", ($methodST, $methodSP, $methodFD); my $noBothWriteMethods = join "|", ($methodFD); sub main(@){ if(@_ == 1 and $_[0] =~ /^(-h|--help)$/){ print $usage; exit 0; } if(@_ > 0 and $_[0] eq '-v'){ $verbose = 1; shift; } my $cmd = shift() || ''; my $method; my $methodSyn = shift() || ''; for my $m(keys %$methodSyns){ $method = $m if $methodSyn eq $m or $methodSyn =~ /^($$methodSyns{$m})$/; } die $usage if not defined $method or $cmd !~ /^(-g|-s)$/; my $bat; if($method eq $methodPS){ $bat = 0; }else{ $bat = shift; } die " missing or incorrect\n" if not defined $bat or $bat !~ /^0|1|2$/; if($cmd eq '-g' and @_ == 0){ print getMethod($method, $bat) . "\n"; }elsif($cmd eq '-s'){ print setMethod($method, $bat, @_); }else{ die $usage; } } sub getMethod($$){ my $method = shift; my $bat = shift; if($method =~ /^($noReadMethods)$/){ die "Cannot read $method\n"; } if($bat == 0 and $method =~ /^($noBothReadMethods)$/){ die "Cannot specify 'either/both' for reading $method\n"; } $bat = parseArgDecToBin $bat; if($method eq $methodST){ return readStartChargeThreshold(acpiCallGet 'BCTG', $bat); }elsif($method eq $methodSP){ return readStopChargeThreshold(acpiCallGet 'BCSG', $bat); }elsif($method eq $methodIC){ #this is actually reading peak shift state return readInhibitCharge(acpiCallGet 'PSSG', $bat); }elsif($method eq $methodFD){ return readForceDischarge(acpiCallGet 'BDSG', $bat); }else{ die $usage; } } sub setMethod($$@){ my $method = shift; my $bat = shift; if($bat == 0 and $method =~ /^($noBothWriteMethods)$/){ die "Cannot specify 'either/both' for writing $method\n"; } my %info; $info{bat} = $bat; if($method =~ /^($methodIC|$methodPS)$/){ $info{inhibit} = shift @_; if(not defined $info{inhibit} or $info{inhibit} !~ /^0|1$/){ die "missing or invalid value for \n"; } $info{min} = shift @_; $info{min} = 0 if not defined $info{min}; ############################################################ #they are shifting a bit somewhere; the limit should be 1440 #the same range in peak-shift-state is used, except shifted to the left #the value returned by peak-shift-state is the REAL duration, though $info{min} *= 2 if $method eq $methodIC and $info{min} != 65535; ############################################################ if($info{min} !~ /^\d+$/ or ($info{min} > 1440 and $info{min} != 65535)){ die "invalid value for \n"; } }elsif($method =~ /^($methodFD)$/){ $info{discharge} = shift @_; if(not defined $info{discharge} or $info{discharge} !~ /^0|1$/){ die "missing or invalid value for \n"; } $info{acbreak} = shift @_; $info{acbreak} = 0 if not defined $info{acbreak}; if($info{acbreak} !~ /^0|1$/){ die "invalid value for \n"; } }elsif($method =~ /^($methodST|$methodSP)$/){ $info{percent} = shift @_; if(not defined $info{percent} or $info{percent} !~ /^\d+$/ or $info{percent} > 99){ die "missing or invalid value for \n"; } } die $usage if @_ > 0; %info = map {$_ => parseArgDecToBin $info{$_}} keys %info; if($method eq $methodST){ acpiCallSet 'BCCS', writeStartChargeThreshold(\%info); }elsif($method eq $methodSP){ acpiCallSet 'BCSS', writeStopChargeThreshold(\%info); }elsif($method eq $methodIC){ acpiCallSet 'BICS', writeInhibitCharge(\%info); }elsif($method eq $methodFD){ acpiCallSet 'BDSS', writeForceDischarge(\%info); }elsif($method eq $methodPS){ acpiCallSet 'PSSS', writePeakShiftState(\%info); }else{ die $usage; } } sub readInhibitCharge($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[5] != 1){ die "\n"; } my $val; if($bits[0] == 1){ $val = "yes"; my $min = bitRangeToDec @bits, 8, 23; if($min == 0){ $val .= " (unspecified min)"; }elsif($min == 65535){ $val .= " (forever)"; }else{ $val .= " ($min min)"; } }else{ $val = "no"; } return $val; } sub readStartChargeThreshold($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val = bitRangeToDec @bits, 0, 7; if($val == 0){ $val .= " (default)"; }elsif($val > 0 and $val < 100){ $val .= " (relative percent)"; }else{ $val .= " (unknown)"; } return $val; } sub readStopChargeThreshold($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val = bitRangeToDec @bits, 0, 7; if($val == 0){ $val .= " (default)"; }elsif($val > 0 and $val < 100){ $val .= " (relative percent)"; }else{ $val .= " (unknown)"; } return $val; } sub readForceDischarge($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val; if($bits[0] == 1){ $val = 'yes'; }else{ $val = 'no'; } if($bits[1] == 1){ $val .= ' (break on AC detach)'; } return $val; } sub writePeakShiftState($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{inhibit}) . revpadzero( 3, 0) . revpadzero( 4, 0) . revpadzero(16, $$info{min}) . revpadzero( 8, 0) ; } sub writeInhibitCharge($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{inhibit}) . revpadzero( 3, 0) . revpadzero( 2, $$info{bat}) . revpadzero( 2, 0) . revpadzero(16, $$info{min}) . revpadzero( 8, 0) ; } sub writeStartChargeThreshold($){ my $info = shift; return reverse '' . revpadzero( 8, $$info{percent}) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub writeStopChargeThreshold($){ my $info = shift; return reverse '' . revpadzero( 8, $$info{percent}) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub writeForceDischarge($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{discharge}) . revpadzero( 1, $$info{acbreak}) . revpadzero( 6, 0) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub acpiCall($){ my $call = shift; if(not -e $acpiCallDev){ $ENV{'PATH'} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; system "modprobe acpi_call"; } if(not -e $acpiCallDev){ die "Could not find $acpiCallDev. Is module acpi_call loaded?\n"; } my $dmiVersion = `cat /sys/class/dmi/id/product_version 2>/dev/null`; chomp $dmiVersion; my %aslBaseOverrides = ( 'ThinkPad S2' => '\_SB.PCI0.LPCB.EC.HKEY', 'ThinkPad 13 2nd Gen' => '\_SB.PCI0.LPCB.EC.HKEY', 'ThinkPad Edge E130' => '\_SB.PCI0.LPCB.EC.HKEY', ); my $aslBase; if (exists($aslBaseOverrides{$dmiVersion})) { $aslBase = $aslBaseOverrides{$dmiVersion}; } else { my $psdev = ""; foreach my $dev (glob "$psDeviceGlob") { if(-e $dev) { $psdev = $dev; last; }; } if(not -e $psdev){ die "No power supply device path to read ASL base from: $psDeviceGlob\n"; } $aslBase = `cat $psdev`; } chomp $aslBase; $aslBase =~ s/_+(\.|$)/$1/g; #trim trailing _s from components $aslBase =~ s/(\.([A-Z,0-9])+)$/.HKEY/; #replace final .dddd with .HKEY my $val = runAcpiCall $call, $aslBase; if($val =~ /Error: AE_NOT_FOUND/){ die "Error: AE_NOT_FOUND for ASL base: $aslBase\n$val"; } print "Call : $aslBase.$call\n" if $verbose; print "Response: $val\n" if $verbose; return $val; } sub runAcpiCall($$){ my ($call, $aslBase) = @_; open FH, "> $acpiCallDev" or die "Cannot write to $acpiCallDev: $!"; print FH "$aslBase.$call\n"; close FH; open FH, "< $acpiCallDev" or die "Cannot read $acpiCallDev: $!"; my $val = ; close FH; return $val; } sub acpiCallGet($$){ my ($method, $bits) = @_; my $call = "$method 0x" . binToHex($bits); my $val = acpiCall $call; if($val eq '0x80000000'){ die "Call failure status returned: $val"; } return $val; } sub acpiCallSet($$){ my ($method, $bits) = @_; my $call = "$method 0x" . binToHex($bits); my $val = acpiCall $call; if($val eq '0x80000000'){ die "Call failure status returned: $val"; } } sub revpadzero($$){ return reverse ('0' x ($_[0] - length $_[1]) . $_[1]); } sub bitRangeToDec(\@$$){ my @bits = @{shift()}; my $start = shift; my $end = shift; my $bin = reverse(join '', @bits[$start .. $end]); return binToDec $bin; } sub parseArgDecToBin($){ my $dec = shift; die "not a positive integer: " . $dec . "\n\n$usage" if $dec !~ /^\d+$/; return decToBin $dec; } sub parseStatusHexToBitArray($){ my $hex = shift; if($hex !~ /0x([0-9a-f]+)/i){ my $msg = "Bad status returned: $hex\n"; if($hex =~ /Error: AE_NOT_FOUND/){ $msg .= '' . "ASL base not found for this machine\n" . " {perhaps it does not have the ThinkPad ACPI interface}\n" . "ASL base parsed using: $psDeviceGlob\n" ; } die $msg; } return split //, revpadzero 32, hexToBin($1); } sub decToBin($){ my $bits = unpack("B32", pack("N", $_[0])); $bits =~ s/^0*//; return $bits; } sub binToDec($){ return oct "0b$_[0]"; } sub hexToBin($){ return decToBin(oct "0x$_[0]"); } sub binToHex($){ return sprintf("%x", binToDec $_[0]); } sub synList(@){ return join "|", ((map {"--$_"} @_), @_); } &main(@ARGV);