pax_global_header00006660000000000000000000000064141657575770014542gustar00rootroot0000000000000052 comment=fac9cdbbf976a65ed34397f6daac9d775c4a56f0 TLP-1.5.0/000077500000000000000000000000001416575757700122045ustar00rootroot00000000000000TLP-1.5.0/.github/000077500000000000000000000000001416575757700135445ustar00rootroot00000000000000TLP-1.5.0/.github/Bug_Reporting_Howto.md000066400000000000000000000024701416575757700200170ustar00rootroot00000000000000## How to submit a bug report ### Before you report a bug Make sure you have: * Followed the appropiate [Installation instructions](https://linrunner.de/tlp/installation) * Read the [Settings guide](https://linrunner.de/tlp/settings) * Carefully checked the [FAQ](https://linrunner.de/tlp/faq) * Checked [existing bug reports](https://github.com/linrunner/TLP/issues) * Tried to isolate the cause as described in [Troubleshooting](https://linrunner.de/tlp/support/troubleshooting.html) ### What not to report * **Any issues with packages not provided by the project, in particular 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](https://github.com/linrunner/TLP/blob/master/.github/ISSUE_TEMPLATE/bug_report.md)**. Bug reports not providing the necessary information get flagged *incomplete* and may be closed without further notice. TLP-1.5.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000062331416575757700163470ustar00rootroot00000000000000# 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.5.0/.github/CONTRIBUTING.md000066400000000000000000000003461416575757700160000ustar00rootroot00000000000000## How can I contribute to TLP? Contributing is not only about coding and pull requests. Volunteers helping with support and testing are always welcome! Please read [Contribute](https://linrunner.de/tlp/contribute) for details. TLP-1.5.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001416575757700157275ustar00rootroot00000000000000TLP-1.5.0/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000016041416575757700204220ustar00rootroot00000000000000--- 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) [x] I've provided all required `tlp-stat` outputs via [Gist](https://gist.github.com/) (see below) **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** (not as file attachment, no screenshots) **Additional context** Add any other context about the problem here. TLP-1.5.0/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000013361416575757700214570ustar00rootroot00000000000000--- 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.5.0/.gitignore000066400000000000000000000000101416575757700141630ustar00rootroot00000000000000*.geany TLP-1.5.0/.perlcriticrc000066400000000000000000000002331416575757700146700ustar00rootroot00000000000000[ValuesAndExpressions::ProhibitConstantPragma] severity = 1 [InputOutput::RequireBriefOpen] severity = 1 [Subroutines::RequireArgUnpacking] severity = 1 TLP-1.5.0/00-template.conf000066400000000000000000000002461416575757700151050ustar00rootroot00000000000000# 00-template.conf - Template for TLP drop-in customizations # See full explanation: https://linrunner.de/tlp/settings # # PARAMETER="value" # PARAMETER+="add value" TLP-1.5.0/AUTHORS000066400000000000000000000003041416575757700132510ustar00rootroot00000000000000Main author: Thomas Koch - Contributors: André Erdmann Pali Rohár https://github.com/linrunner/TLP/graphs/contributors TLP-1.5.0/COPYING000066400000000000000000000013411416575757700132360ustar00rootroot00000000000000Main Author: Thomas Koch Copyright: Copyright (c) 2022 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.5.0/LICENSE000066400000000000000000000431031416575757700132120ustar00rootroot00000000000000 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.5.0/Makefile000066400000000000000000000174131416575757700136520ustar00rootroot00000000000000# 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_BATD ?= /usr/share/tlp/bat.d TLP_NMDSP ?= /etc/NetworkManager/dispatcher.d TLP_CONFUSR ?= /etc/tlp.conf TLP_CONFDIR ?= /etc/tlp.d TLP_CONFDEF ?= /usr/share/tlp/defaults.conf TLP_CONFREN ?= /usr/share/tlp/rename.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) _BATD = $(DESTDIR)$(TLP_BATD) _NMDSP = $(DESTDIR)$(TLP_NMDSP) _CONFUSR = $(DESTDIR)$(TLP_CONFUSR) _CONFDIR = $(DESTDIR)$(TLP_CONFDIR) _CONFDEF = $(DESTDIR)$(TLP_CONFDEF) _CONFREN = $(DESTDIR)$(TLP_CONFREN) _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_BATD@|$(TLP_BATD)|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_CONFREN@|$(TLP_CONFREN)|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 \ nfc.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/* \ bat.d/*[a-z] \ 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 BATDRVFILES = $(foreach drv,$(wildcard bat.d/[0-9][0-9]-*[a-z]),$(drv)~) # Make targets all: $(INFILES) $(INFILES): %: %.in $(SED) $< > $@ clean: rm -f $(INFILES) rm -f bat.d/*~ install-tlp: all # Package tlp install -D -m 755 tlp $(_SBIN)/tlp install -D -m 755 tlp-rf $(_BIN)/bluetooth ln -sf bluetooth $(_BIN)/nfc 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 -t $(_TLIB)/func.d func.d/* install -m 755 tlp-func-base $(_TLIB)/ install -D -m 755 -t $(_TLIB)/bat.d bat.d/* 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) install -D -m 644 rename.conf $(_CONFREN) 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)/nfc 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)/nfc 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)/nfc 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 checkbatdrv 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|WIP)" $(SHFILES) $(PLFILES) || true bat.d/TEMPLATE~: bat.d/TEMPLATE @awk '/^batdrv_[a-z_]+ ()/ { print $$1; }' $< | grep -v 'batdrv_is' | sort > $@ bat.d/%~: bat.d/% @printf "*** checkbatdrv %-25s ***********************************************\n" "$<" @awk '/^batdrv_[a-z_]+ ()/ { print $$1; }' $< | grep -v 'batdrv_is' | sort > $@ @diff -U 1 -s bat.d/TEMPLATE~ $@ || true checkbatdrv: bat.d/TEMPLATE~ $(BATDRVFILES) rm -f bat.d/*~ TLP-1.5.0/README.d000066400000000000000000000005731416575757700133130ustar00rootroot00000000000000This directory is intended to contain drop-in customizations for TLP. See full explanation: https://linrunner.de/tlp/settings 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.5.0/README.rst000066400000000000000000000030331416575757700136720ustar00rootroot00000000000000TLP - Optimize Linux Laptop Battery Life ======================================== TLP is a feature-rich command line utility for Linux, saving laptop battery power without the need to delve deeper into technical details. TLP’s default settings are already optimized for battery life, so you may just install and forget it. Nevertheless TLP is highly customizable to fulfil your specific requirements. Documentation ------------- Read the full documentation at the website ``_. For a summary of how TLP works and its features see `Introduction `_. Installation ------------ TLP packages are available for all major Linux distributions: `Installation `_. Settings -------- Settings are organized into two profiles, enabling you to adjust between savings and performance independently for battery (BAT) and AC operation. Refer to `Settings `_ to learn how to customize the configuration if desired. Support ------- Please visit your favorite Linux community for help and support questions. Make shure to check `Support `_ first. Bug reports ----------- Refer to the `Bug Reporting Howto `_. Contribute ---------- Contributing is not only about coding. Volunteers helping with support, testing and documentation are always welcome! See `Contributing `_. TLP-1.5.0/VERSION000066400000000000000000000000061416575757700132500ustar00rootroot000000000000001.5.0 TLP-1.5.0/bat.d/000077500000000000000000000000001416575757700131745ustar00rootroot00000000000000TLP-1.5.0/bat.d/05-thinkpad000066400000000000000000001411361416575757700151510ustar00rootroot00000000000000#!/bin/sh # 05-thinkpad - Battery Plugin for ThinkPads using natacpi and/or # tpacpi-bat/acpi-call for thresholds and forced discharge, i.e. X220/T420 and newer. # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly SMAPIBATDIR=/sys/devices/platform/smapi 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|L512|SL[345]00|X121e)$' readonly MODINFO=modinfo readonly MOD_TPSMAPI="tp_smapi" readonly MOD_TPACPI="acpi_call" 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}" } check_thinkpad () { # check for ThinkPad hardware and save model string # rc: 0=ThinkPad, 1=other hardware # retval: $_tpmodel local pv _tpmodel="" if [ -d $TPACPID ]; 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 # ThinkPad echo_debug "bat" "check_thinkpad: tpmodel=$_tpmodel" return 0 else # not a ThinkPad echo_debug "bat" "check_thinkpad.not_a_thinkpad: model=$pv" return 1 fi } # --- Plugin API functions batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $_batdrv_kmod # # 1. check for native kernel acpi (Linux 4.19 or higher required) # --> retval $_natacpi: # 0=thresholds and discharge/ # 1=thresholds only/ # 32=disabled/ # 128=no kernel support/ # 254=ThinkPad not supported # # 2. check for acpi-call external kernel module and test with integrated # tpacpi-bat [ThinkPads only] # --> retval $_tpacpi: # 0=thresholds and discharge/ # 1=thresholds only/ # 32=disabled/ # 64=acpi_call module not loaded/ # 127=tpacpi-bat not installed/ # 128=acpi_call module not installed/ # 137=kernel error (oops)/ # 253=tpacpi-bat error/ # 254=ThinkPad not supported/ # 255=superseded by natacpi/ # 256=superseded and kernel module not loaded # # 3. check for tp-smapi external kernel module # --> retval $_tpsmapi: # 1=readonly/ # 32=disabled/ # 64=tp_smapi module not loaded/ # 128=tp_smapi module not installed # # 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. determine sysfile basenames for natacpi # start threshold --> retval $_bn_start, # stop threshold --> retval $_bn_stop, # force discharge --> retval $_bn_dischg; # # 6. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 7. define charge threshold defaults # start threshold --> retval $_bt_def_start, # stop threshold --> retval $_bt_def_stop; _batdrv_plugin="thinkpad" _batdrv_kmod="thinkpad_acpi" # kernel module for natacpi if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if ThinkPad if ! check_thinkpad; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.not_a_thinkpad" return 1 elif supports_no_tp_bat_funcs; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.unsupported_model" return 1 fi fi # presume no features at all _natacpi=128 _tpacpi=255 _tpsmapi=254 _bm_read="natacpi" _bm_thresh="none" _bm_dischg="none" _bn_start="" _bn_stop="" _bn_dischg="" _batteries="" # shellcheck disable=SC2034 _bt_def_start=96 # shellcheck disable=SC2034 _bt_def_stop=100 # --- 1. iterate batteries and check for native kernel ACPI local bd bs local done=0 for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi # skip natacpi detection for 2nd and subsequent batteries [ $done -eq 1 ] && continue done=1 if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 continue fi if [ -f $bd/charge_control_start_threshold ] \ && [ -f $bd/charge_control_end_threshold ]; then # sysfiles for thresholds exist (kernel 5.9 and newer) _bn_start="charge_control_start_threshold" _bn_stop="charge_control_end_threshold" _natacpi=254 elif [ -f $bd/charge_start_threshold ] \ && [ -f $bd/charge_stop_threshold ]; then # sysfiles for thresholds exist (kernel 4.17 and newer) _bn_start="charge_start_threshold" _bn_stop="charge_stop_threshold" _natacpi=254 else # nothing detected _natacpi=128 continue fi if readable_sysf $bd/$_bn_start \ && readable_sysf $bd/$_bn_stop; then # start/stop thresholds are actually readable _natacpi=1 _bm_thresh="natacpi" if readable_sysf $bd/charge_behaviour; then # sysfile for force-discharge exists and is actually readable _natacpi=0 _bm_dischg="natacpi" _bn_dischg="charge_behaviour" fi fi fi done # quit if no battery detected, there is no point in activating the plugin if [ -z "$_batteries" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_batteries" return 2 fi # Consider legacy ThinkPads with Coreboot/natacpi if supports_tpsmapi_only && [ $_natacpi -ge 32 ]; then # no natacpi --> do not probe acpi_call/tpacpi-bat but quit echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_natacpi: batteries=$_batteries; natacpi=$_natacpi; tpacpi=$_tpacpi; tpsmapi=$_tpsmapi" return 1 fi # --- 2. probe acpi_call external kernel module and test with integrated tpacpi-bat if [ $_natacpi -gt 0 ]; then load_modules $MOD_TPACPI if [ ! -e /proc/acpi/call ]; then # call API not present if $MODINFO $MOD_TPACPI > /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 if $TPACPIBAT -g ST 1 > /dev/null 2>&1; then # thresholds capable _tpacpi=1 if $TPACPIBAT -g FD 1 > /dev/null 2>&1; then # force_discharge capable _tpacpi=0 fi else # tpacpi-bat failed case $? in 255) # acpi call non-existent (AE_NOT_FOUND) _tpacpi=254 ;; 137) # kernel error (oops) _tpacpi=137 ;; * ) # tpacpi-bat error _tpacpi=253 ;; esac fi if [ $_tpacpi -le 1 ]; then # tpacpi capable if [ "$TPACPI_ENABLE" = "0" ]; then # disabled by configuration _tpacpi=32 else # not disabled case $_natacpi in 1) # use for discharge only [ $_tpacpi -eq 0 ] && _bm_dischg="tpacpi" ;; *) # use for thresholds and discharge _bm_thresh="tpacpi" [ $_tpacpi -eq 0 ] && _bm_dischg="tpacpi" ;; esac fi fi fi elif [ ! -e /proc/acpi/call ]; then # superseded and kernel module not loaded _tpacpi=256 fi # --- 3. probe tp-smapi external kernel module (relevant models only) if supports_tpsmapi_and_tpacpi; then load_modules $MOD_TPSMAPI if [ -d $SMAPIBATDIR ]; then # module loaded --> tp-smapi available if [ "$TPSMAPI_ENABLE" = "0" ]; then # tpsmapi disabled by configuration _tpsmapi=32 else # reading battery data via tpsmapi is preferred over natacpi # because it provides cycle count and more _tpsmapi=1 _bm_read="tpsmapi" fi elif $MODINFO $MOD_TPSMAPI > /dev/null 2>&1; then # module installed but not loaded _tpsmapi=64 else # module neither installed nor builtin _tpsmapi=128 fi fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; tpacpi=$_tpacpi; tpsmapi=$_tpsmapi" echo_debug "bat" "batdrv_init.${_batdrv_plugin}: read=$_bm_read; thresh=$_bm_thresh; bn_start=$_bn_start; bn_stop=$_bn_stop; dischg=$_bm_dischg; bn_dischg=$_bn_dischg" return 0 } batdrv_select_battery () { # determine battery sysfiles and tpacpi-bat index # $1: BAT0/BAT1/DEF # 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 # prerequisite: batdrv_init() # defaults _bat_idx=0 # no index _bat_str="" # no bat _bd_read="" # no directories _bf_start="" _bf_stop="" _bf_dischg="" # validate battery param local bs case $1 in DEF) # 1st battery is default bs="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then bs=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine bat index for tpacpi and main/aux distinction case $bs in BAT0) _bat_str="$bs" # BAT0 is always assumed main battery _bat_idx=1 ;; BAT1) _bat_str="$bs" if [ $_tpacpi -le 1 ]; 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 else # without tpacpi: BAT1 is aux _bat_idx=2 fi ;; esac # determine natacpi sysfiles if [ "$_bm_thresh" = "natacpi" ]; then _bf_start="$ACPIBATDIR/$bs/$_bn_start" _bf_stop="$ACPIBATDIR/$bs/$_bn_stop" fi if [ "$_bm_dischg" = "natacpi" ]; then _bf_dischg="$ACPIBATDIR/$bs/$_bn_dischg" fi case "$_bm_read" in natacpi) _bd_read="$ACPIBATDIR/$bs" ;; tpsmapi) _bd_read="$SMAPIBATDIR/$bs" ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bat_idx=$_bat_idx; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop; bf_dischg=$_bf_dischg" return 0 } batdrv_read_threshold () { # read and print charge threshold # $1: start/stop # $2: 0=api/1=tlp-stat output # global param: $_bm_thresh, $_bf_start, $_bf_stop, $_bat_idx # out: # - api: 0..100/"" on error # - tlp-stat: 0..100/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local bf out="" rc=0 case $1 in start) out="$X_THRESH_SIMULATE_START" ;; stop) out="$X_THRESH_SIMULATE_STOP" ;; esac if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1).simulate: bm_thresh=$_bm_thresh; bf=$bf; bat_idx=$_bat_idx; out=$out; rc=$rc" return 0 fi case $_bm_thresh in natacpi) # read threshold from sysfile case $1 in start) bf=$_bf_start ;; stop) bf=$_bf_stop ;; esac if ! out=$(read_sysf $bf); then # not readable/non-existent if [ "$2" != "1" ]; then out="" else out="(not available)" fi rc=4 fi # workaround: read threshold sysfile a second time to mitigate # the annoying firmware issue on ThinkPad A/E/L/S/X series # (refer to issue #369 and FAQ) if ! out=$(read_sysf $bf); then # not readable/non-existent if [ "$2" != "1" ]; then out="" else out="(not available)" fi rc=4 fi ;; tpacpi) case $1 in start) out=$($TPACPIBAT -g ST $_bat_idx 2> /dev/null | cut -f1 -d' ') # workaround: read threshold a second time (see above) out=$($TPACPIBAT -g ST $_bat_idx 2> /dev/null | cut -f1 -d' ') ;; stop) out=$($TPACPIBAT -g SP $_bat_idx 2> /dev/null | cut -f1 -d' ') # workaround: read threshold a second time (see above) out=$($TPACPIBAT -g SP $_bat_idx 2> /dev/null | cut -f1 -d' ') ;; esac # shellcheck disable=SC2181 if [ $? -eq 0 ] && is_uint "$out"; then if [ $out -ge 128 ]; then # remove offset of 128 for Edge S430 et al. out=$((out - 128)) fi if [ "$1" = "stop" ] && [ $out -eq 0 ]; then # stop: 0 (hardware default) means 100 out=100 fi else if [ "$2" != "1" ]; then out="" else out="(not available)" fi rc=4 fi ;; *) # no threshold api rc=255 ;; esac # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else if [ "$2" = "1" ]; then printf "(not available)\n" fi rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1): bm_thresh=$_bm_thresh; bf=$bf; bat_idx=$_bat_idx; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write both charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold 0(disabled)..99/DEF(default) # $2: new stop threshold 1..100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bm_thresh, $_bat_str, $_bat_idx, $_bf_start, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_start=${1:-} local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_start old_stop # insert defaults [ "$new_start" = "DEF" ] && new_start=$_bt_def_start [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_start" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # start: check for 3 digits max, ensure min 0 / max 99 if ! is_uint "$new_start" 3 || \ ! is_within_bounds $new_start 0 99; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_start: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at START_CHARGE_THRESH_${cfg_bat}=\"${new_start}\": not specified, invalid or out of range (0..99). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at START_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (0..99). Aborted.\n" "$cfg_bat" "$new_start" 1>&2 else printf "Error: charge start threshold (%s) for %s is not specified, invalid or out of range (0..99). Aborted.\n" "$new_start" "$_bat_str" 1>&2 fi ;; esac return 2 fi # stop: check for 3 digits max, ensure min 1 / max 100 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 1 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_stop: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": not specified, invalid or out of range (1..100). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (1..100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: charge stop threshold (%s) for %s is not specified, invalid or out of range (1..100). Aborted.\n" "$new_stop" "$_bat_str" 1>&2 fi ;; esac return 2 fi # check if start < stop if [ $new_start -ge $new_stop ]; then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_diff: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration: START_CHARGE_THRESH_${cfg_bat} >= STOP_CHARGE_THRESH_${cfg_bat}. Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration: START_CHARGE_THRESH_%s >= STOP_CHARGE_THRESH_%s. Aborted.\n" "$cfg_bat" "$cfg_bat" 1>&2 else printf "Error: start threshold >= stop threshold for %s. Aborted.\n" "$_bat_str" 1>&2 fi ;; esac return 3 fi # read active threshold values if ! old_start=$(batdrv_read_threshold start 0) || \ ! old_stop=$(batdrv_read_threshold stop 0); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).read_error: bat=$_bat_str" case $verb in 1) echo_message "Warning: could not read current charge threshold(s) for $_bat_str. Battery skipped." ;; 2) printf "Error: could not read current charge threshold(s) for %s. Aborted.\n" "$_bat_str" 1>&2 ;; esac return 4 fi if [ $old_start -ge $old_stop ]; then # invalid threshold reading, happens on ThinkPad E/L series old_start="none" old_stop="none" fi # 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/ECP enforces something) local rc=0 steprc tseq if [ "$old_stop" != "none" ] && [ $new_start -gt $old_stop ]; then tseq="stop start" else tseq="start stop" fi # write new thresholds in determined sequence if [ "$verb" = "2" ]; then printf "Setting temporary charge thresholds for %s:\n" "$_bat_str" fi for step in $tseq; do local old_thresh new_thresh steprc case $step in start) old_thresh=$old_start new_thresh=$new_start ;; stop) old_thresh=$old_stop new_thresh=$new_stop ;; esac if [ "$old_thresh" != "$new_thresh" ]; then # new threshold differs from effective one --> write it case $_bm_thresh in natacpi) 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=5 ;; tpacpi) case $step in start) $TPACPIBAT -s ST $_bat_idx $new_thresh > /dev/null 2>&1 ;; stop) if [ $new_thresh -eq 100 ]; then # tpacpi-bat won't accept 100 $TPACPIBAT -s SP $_bat_idx 0 > /dev/null 2>&1 else $TPACPIBAT -s SP $_bat_idx $new_thresh > /dev/null 2>&1 fi ;; esac steprc=$?; [ $steprc -ne 0 ] && rc=5 ;; *) # no threshold api steprc=255 rc=5 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.write: bat=$_bat_str; old=$old_thresh; new=$new_thresh; steprc=$steprc" case $verb in 2) if [ $steprc -eq 0 ]; then if [ "$step" = "start" ] && [ $new_thresh -eq 0 ]; then printf " %-5s = %3d (disabled)\n" $step $new_thresh else printf " %-5s = %3d\n" $step $new_thresh fi else printf " %-5s = %3d (Error: write failed)\n" $step $new_thresh 1>&2 fi ;; 1) if [ $steprc -gt 0 ]; then echo_message "Error: writing charge $step threshold for $_bat_str failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.no_change: bat=$_bat_str; old=$old_thresh; new=$new_thresh" if [ "$verb" = "2" ]; then printf " %-5s = %3d (no change)\n" $step $new_thresh fi fi done # for step echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).complete: bat=$_bat_str; rc=$rc" return $rc } batdrv_chargeonce () { # charge battery to stop threshold once # use pre-determined method and sysfiles from global parms # global param: $_bm_thresh, $_bat_str, $_bat_idx, $_bf_start, $_bf_stop # rc: 0=ok/ # 2=charge level read error # 3=charge level too high/ # 4=threshold read error/ # 5=threshold write error/ # prerequisite: batdrv_init(), batdrv_select_battery() local ccharge cur_stop efull enow temp_start local rc=0 if ! cur_stop=$(batdrv_read_threshold stop 0); then printf "Error: reading charge stop threshold for %s failed. Aborted.\n" $_bat_str 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).thresh_unknown: stop=$cur_stop; rc=4" return 4 fi # get current charge level (in %) case $_bm_read in natacpi) # 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 ;; tpsmapi) # use tp-smapi sysfile ccharge=$(read_sysval $_bd_read/remaining_percent) || ccharge=-1 ;; esac if [ $ccharge -eq -1 ]; then printf "Error: cannot determine charge level for %s.\n" "$_bat_str" 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).charge_level_unknown: enow=$enow; efull=$efull; rc=2" return 2 fi temp_start=$(( cur_stop - 1 )) if [ $ccharge -gt $temp_start ]; then printf "Error: the %s charge level is %s%%. " "$_bat_str" "$ccharge" 1>&2 printf "For this command to work, it must not exceed %s%% (stop threshold - 1).\n" "$temp_start" 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).charge_level_too_high: soc=$ccharge; stop=$cur_stop; rc=3" return 3 fi printf "Setting temporary charge threshold for %s:\n" "$_bat_str" case $_bm_thresh in natacpi) write_sysf "$temp_start" $_bf_start || rc=5 ;; tpacpi) $TPACPIBAT -s ST $_bat_idx $temp_start > /dev/null 2>&1 || rc=5 ;; esac if [ $rc -eq 0 ]; then printf " start = %3d\n" $temp_start else printf " start = %3d (Error: write failed)\n" $temp_start 1>&2 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str): soc=$ccharge; start=$temp_start; stop=$cur_stop; rc=$rc" return $rc } batdrv_apply_configured_thresholds () { # apply configured thresholds from configuration to all batteries # output parameter errors only if batdrv_select_battery BAT0; then batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi if batdrv_select_battery BAT1; then # write configured thresholds, output parameter errors batdrv_write_thresholds "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1" 1 "BAT1"; rc=$? fi return 0 } batdrv_read_force_discharge () { # read and print force-discharge state # $1: 0=api/1=tlp-stat output # global param: $_bm_dischg, $_bf_dischg, $_bat_idx # out: # - api: 0=off/1=on/"" on error # - tlp-stat: status text/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local rc=0 out="" case $_bm_dischg in natacpi) # read state from sysfile if out=$(read_sysf $_bf_dischg); then if [ "$1" != "1" ]; then if echo "$out" | grep -q "\[force-discharge\]"; then out=1 else out=0 fi fi else # not readable/non-existent if [ "$1" != "1" ]; then out="" else out="(not available)" fi rc=4 fi ;; tpacpi) # read via tpacpi-bat case $($TPACPIBAT -g FD $_bat_idx 2> /dev/null) in yes) out=1 ;; no) out=0 ;; *) if [ "$1" != "1" ]; then out="" else out="(not available)" fi rc=4 ;; esac ;; *) # no discharge api if [ "$1" = "1" ]; then out="(not available)" fi rc=255 ;; esac printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge($_bat_str): bm_dischg=$_bm_dischg; bf_dischg=$_bf_dischg; bat_idx=$_bat_idx; out=$out; rc=$rc" return $rc } batdrv_write_force_discharge () { # write force discharge state # $1: 0=off/1=on # global param: $_bat_str, $_bat_idx, $_bm_dischg, $_bf_dischg # rc: 0=done/5=write error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local rc=0 case $_bm_dischg in natacpi) # write force_discharge case "$1" in 0) write_sysf "auto" $_bf_dischg || rc=5 ;; 1) write_sysf "force-discharge" $_bf_dischg || rc=5 ;; esac ;; # natacpi tpacpi) # use tpacpi-bat $TPACPIBAT -s FD $_bat_idx $1 > /dev/null 2>&1 || rc=5 ;; # tpcpaci *) # no discharge api rc=255 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge($_bat_str, $1): bm_dischg=$_bm_dischg; bf_dischg=$_bf_dischg; bat_idx=$_bat_idx; rc=$rc" return $rc } batdrv_cancel_force_discharge () { # trap: called from batdrv_discharge # global param: $_bat_str # prerequisite: batdrv_discharge() batdrv_write_force_discharge 0 unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.cancelled($_bat_str)" printf " Cancelled.\n" do_exit 0 } batdrv_force_discharge_active () { # check if battery is in 'force_discharge' state # global param: $_bat_str, $_bm_read, $_bd_read # rc: 0=discharging/1=not discharging/2=AC detached/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local bsys rc=0 st if [ "$(batdrv_read_force_discharge 0)" = "0" ]; then # force_discharge is off --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active($_bat_str): rc=1" return 1 fi if ! get_sys_power_supply; then # AC detached --> cancel discharge rc=2 else # force_discharge is still on, but quirky firmware (e.g. ThinkPad E-series) # may keep force_discharge on --> check battery status sysfile too case $_bm_read in natacpi) bsys=$_bd_read/status # use ACPI sysfile ;; tpsmapi) bsys=$_bd_read/state # use tpsmapi sysfile ;; *) # no read api bsys="" rc=255 ;; esac # read battery state if [ -f "$bsys" ]; then st="$(read_sysf $bsys)" case "$st" in [Dd]ischarging) rc=0 ;; *) rc=1 ;; esac fi fi echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active($_bat_str): bm_read=$_bm_read; bf=$bsys; st=$st; rc=$rc" return $rc } batdrv_discharge () { # discharge battery # global param: $_bm_dischg, $_bat_idx, $_bf_dischg # rc: 0=done/1=malfunction/2=not emptied/3=ac removed/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local en ef pn rc rp wt # start discharge if ! batdrv_write_force_discharge 1; then echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.force_discharge_malfunction($_bat_str)" echo "Error: discharge $_bat_str malfunction -- check your hardware (battery, charger)." 1>&2 return 1 fi trap batdrv_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 ! batdrv_force_discharge_active && [ $wt -gt 0 ] ; do sleep 1 printf "." wt=$((wt - 1)) done printf "\n" if batdrv_force_discharge_active; then # discharge initiated sucessfully --> wait for completion == while status "discharging" echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.running($_bat_str)" while batdrv_force_discharge_active; do clear printf "Currently discharging battery %s:\n" "$_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 printf "force-discharge = %s\n" "$(batdrv_read_force_discharge 0)" echo "Press Ctrl+C to cancel." sleep 5 done unlock_tlp tlp_discharge # read charge level one last time case $_bm_read in natacpi|tpacpi) # use ACPI sysfiles en=$(read_sysval $_bd_read/energy_now) ef=$(read_sysval $_bd_read/energy_full) if [ "$ef" != "0" ]; then rp=$(perl -e 'printf ("%d", 100.0 * '$en' / '$ef' );') else rp=0 fi ;; # natacpi, tpsmapi tpsmapi) # use tp-smapi sysfiles rp=$(read_sysf $_bd_read/remaining_percent) ;; esac if [ $rp -gt 0 ]; then # battery not emptied --> determine cause get_sys_power_supply # shellcheck disable=SC2154 if [ $_syspwr -eq 1 ]; then # system on battery --> AC power removed echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.ac_removed($_bat_str)" echo "Warning: battery $_bat_str was not discharged completely -- AC/charger removed." 1>&2 rc=3 else # discharging terminated by unknown reason echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_emptied($_bat_str)" echo "Error: battery $_bat_str was not discharged completely i.e. terminated by the firmware -- check your hardware (battery, charger)." 1>&2 rc=2 fi fi else # discharge malfunction --> cancel discharge and abort batdrv_write_force_discharge 0 echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.malfunction($_bat_str)" echo "Error: discharge $_bat_str malfunction -- check your hardware (battery, charger)." 1>&2 rc=1 fi trap - INT # remove ^C hook # ThinkPad E-series firmware may keep force_discharge active --> cancel it [ "$(batdrv_read_force_discharge 0)" = "1" ] && batdrv_write_force_discharge 0 if [ $rc -eq 0 ]; then echo echo "Done: battery $_bat_str was completely discharged." echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.complete($_bat_str)" fi return $rc } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" local fs="" [ "$_bm_thresh" != "none" ] && fs="charge thresholds" if [ "$_bm_dischg" != "none" ]; then [ -n "$fs" ] && fs="${fs}, " fs="${fs}recalibration" fi [ -n "$fs" ] || fs="none available" printf "Supported features: %s\n" "$fs" printf "Driver usage:\n" # native kernel ACPI battery API case $_natacpi in 0|1) printf "* natacpi (%s) = active " "$_batdrv_kmod"; print_methods_per_driver "natacpi" ;; 32) printf "* natacpi (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* natacpi (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* natacpi (%s) = inactive (ThinkPad not supported)\n" "$_batdrv_kmod" ;; *) printf "* natacpi (%s) = unknown status\n" "$_batdrv_kmod" ;; esac # ThinkPad-specific battery APIs case $_tpacpi in 0) printf "* tpacpi-bat (acpi_call) = active "; print_methods_per_driver "tpacpi" ;; 1) printf "* tpacpi-bat (acpi_call) = inactive (none)\n" ;; 32) printf "* tpacpi-bat (acpi_call) = inactive (disabled by configuration)\n" ;; 64) printf "* tpacpi-bat (acpi_call) = inactive (kernel module 'acpi_call' load error)\n" ;; 127) printf "* tpacpi-bat (acpi_call) = inactive (program 'tpacpi-bat' not installed)\n" ;; 128) printf "* tpacpi-bat (acpi_call) = inactive (kernel module 'acpi_call' not installed)\n" ;; 137) printf "* tpacpi-bat (acpi_call) = inactive (kernel error)\n" ;; 253) printf "* tpacpi-bat (acpi_call) = inactive (tpacpi-bat error)\n" ;; 254) printf "* tpacpi-bat (acpi_call) = inactive (ThinkPad not supported)\n" ;; 255) printf "* tpacpi-bat (acpi_call) = inactive (superseded by natacpi)\n" ;; 256) ;; # superseded and kernel module not loaded--> be quiet *) printf "* tpacpi-bat = unknown status" ;; esac case $_tpsmapi in 1) printf "* tp-smapi (tp_smapi) = readonly "; print_methods_per_driver "tpsmapi" ;; 32) printf "* tp-smapi (tp_smapi) = inactive (disabled by configuration)\n" ;; 64) printf "* tp-smapi (tp_smapi) = inactive (kernel module 'tp_smapi' load error)\n" ;; 128) printf "* tp-smapi (tp_smapi) = inactive (kernel module 'tp_smapi' not installed)\n" ;; esac if [ "$_bm_thresh" != "none" ]; then printf "Parameter value ranges:\n" printf "* START_CHARGE_THRESH_BAT0/1: 0(off)..96(default)..99\n" printf "* STOP_CHARGE_THRESH_BAT0/1: 1..100(default)\n" fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate detected batteries batdrv_select_battery $bat case $_bat_idx in 1) printf "+++ ThinkPad Battery Status: %s (Main / Internal)\n" "$bat" ;; 2) printf "+++ ThinkPad Battery Status: %s (Ultrabay / Slice / Replaceable)\n" "$bat" ;; 0) printf "+++ ThinkPad Battery Status: %s\n" "$bat" ;; esac # --- show basic data case $_bm_read in natacpi) # use ACPI data printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi ;; # natacpi tpsmapi) # ThinkPad with active tp-smapi printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model printparm "%-59s = ##%s##" $_bd_read/manufacture_date printparm "%-59s = ##%s##" $_bd_read/first_use_date printparm "%-59s = ##%6d##" $_bd_read/cycle_count if [ -f $_bd_read/temperature ]; then # shellcheck disable=SC2046 perl -e 'printf ("%-59s = %6d [°C]\n", "'$_bd_read/temperature'", '$(read_sysval $_bd_read/temperature)' / 1000.0);' fi printparm "%-59s = ##%6d## [mWh]" $_bd_read/design_capacity printparm "%-59s = ##%6d## [mWh]" $_bd_read/last_full_capacity printparm "%-59s = ##%6d## [mWh]" $_bd_read/remaining_capacity printparm "%-59s = ##%6d## [%%]" $_bd_read/remaining_percent printparm "%-59s = ##%6s## [min]" $_bd_read/remaining_running_time_now printparm "%-59s = ##%6s## [min]" $_bd_read/remaining_charging_time printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now printparm "%-59s = ##%6d## [mW]" $_bd_read/power_avg print_batstate $_bd_read/state printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/design_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group0_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group1_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group2_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group3_voltage printf "\n" fi # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/design_capacity) ef=$(read_sysval $_bd_read/last_full_capacity) en=$(read_sysval $_bd_read/remaining_capacity) efsum=$((efsum + ef)) ensum=$((ensum + en)) ;; # tp-smapi esac # $_bm_read # --- show battery features: thresholds, force_discharge local lf=0 case $_bm_thresh in natacpi) printf "%-59s = %6s [%%]\n" "$_bf_start" "$(batdrv_read_threshold start 1)" printf "%-59s = %6s [%%]\n" "$_bf_stop" "$(batdrv_read_threshold stop 1)" lf=1 ;; tpacpi) printf "%-59s = %6s [%%]\n" "tpacpi-bat.${_bat_str}.startThreshold" "$(batdrv_read_threshold start 1)" printf "%-59s = %6s [%%]\n" "tpacpi-bat.${_bat_str}.stopThreshold" "$(batdrv_read_threshold stop 1)" lf=1 ;; esac case $_bm_dischg in natacpi) printf "%-59s = %6s\n" "$_bf_dischg" "$(batdrv_read_force_discharge 1)" lf=1 ;; tpacpi) printf "%-59s = %6s\n" "tpacpi-bat.${_bat_str}.forceDischarge" "$(batdrv_read_force_discharge 1)" lf=1 ;; esac [ $lf -gt 0 ] && printf "\n" # --- show charge level (SOC) and capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # output ThinkPad specific recommendations # prerequisite: batdrv_init() local missing case "$_tpacpi" in 127) missing="tpacpi-bat program" ;; 128) missing="acpi_call kernel module" ;; *) missing="" ;; esac if [ -n "$missing" ]; then case "$_natacpi" in "") ;; # undefined 0) ;; # natacpi covers it all 1) printf "Install %s for ThinkPad battery recalibration\n" "$missing" ;; *) printf "Install %s for ThinkPad battery thresholds and recalibration\n" "$missing";; esac fi if [ "$_tpsmapi" = "128" ]; then printf "Install tp-smapi kernel modules for extended battery status (e.g. the cycle count)\n" fi return 0 } TLP-1.5.0/bat.d/10-thinkpad-legacy000066400000000000000000001004351416575757700164040ustar00rootroot00000000000000#!/bin/sh # 10-thinkpad-legacy - Battery Plugin for ThinkPads using tp_smapi # for thresholds and forced discharge, # i.e. X201/T410 and older. # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat, 05-thinkpad # shellcheck disable=SC2086 # --- Plugin API functions batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for tp-smapi external kernel module # --> retval $_tpsmapi # 0=supported/ # 32=disabled/ # 64=tp_smapi module not loaded/ # 128=tp_smapi module not installed/ # 254=ThinkPad not supported # # 2. determine 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 # # 3. determine sysfile basenames for tpsmapi # start threshold --> retval $_bn_start, # stop threshold --> retval $_bn_stop, # force discharge --> retval $_bn_dischg; # # 4. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 5. define charge threshold defaults # start threshold --> retval $_bt_def_start, # stop threshold --> retval $_bt_def_stop; _batdrv_plugin="thinkpad-legacy" _batdrv_kmod="tp_smapi" # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! check_thinkpad; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.not_a_thinkpad" return 1 elif ! supports_tpsmapi_only; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.unsupported_model" return 1 fi fi # presume no features at all _tpsmapi=254 _bm_read="natacpi" _bm_thresh="none" _bm_dischg="none" _bn_start="start_charge_thresh" _bn_stop="stop_charge_thresh" _bn_dischg="force_discharge" _batteries="" # shellcheck disable=SC2034 _bt_def_start=96 # shellcheck disable=SC2034 _bt_def_stop=100 local bs bd # iterate batteries for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # quit if no battery detected, there is no point in activating the plugin if [ -z "$_batteries" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_batteries" return 2 fi # --- probe tp-smapi external kernel module load_modules $MOD_TPSMAPI if [ -d $SMAPIBATDIR ]; then # module loaded --> tp-smapi available if [ "$TPSMAPI_ENABLE" = "0" ]; then # tpsmapi disabled by configuration _tpsmapi=32 else # check if all required sysfiles exist for 1st battery for bs in $_batteries; do bds="$SMAPIBATDIR/$bs" if readable_sysf "$bds/$_bn_start" \ && readable_sysf "$bds/$_bn_stop" \ && readable_sysf "$bds/$_bn_dischg"; then # all sysfiles are actually readable _tpsmapi=0 _bm_read="tpsmapi" _bm_thresh="tpsmapi" _bm_dischg="tpsmapi" break # quit loop fi done fi elif $MODINFO $MOD_TPSMAPI > /dev/null 2>&1; then # module installed but not loaded _tpsmapi=64 else # module neither installed nor builtin _tpsmapi=128 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; tpsmapi=$_tpsmapi" echo_debug "bat" "batdrv_init.${_batdrv_plugin}: read=$_bm_read; thresh=$_bm_thresh; bn_start=$_bn_start; bn_stop=$_bn_stop; dischg=$_bm_dischg; bn_dischg=$_bn_dischg" return 0 } batdrv_select_battery () { # determine battery sysfiles and bat index # $1: BAT0/BAT1/DEF # 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 # prerequisite: batdrv_init() # defaults _bat_idx=0 # no index _bat_str="" # no bat _bd_read="" # no directories _bf_start="" _bf_stop="" _bf_dischg="" # validate battery param local bs case $1 in DEF) # 1st battery is default bs="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then bs=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine bat index for main/aux distinction _bat_str="$bs" case $bs in BAT0) _bat_idx=1 ;; BAT1) _bat_idx=2 ;; esac # determine tpsmapi sysfiles if [ "$_bm_thresh" = "tpsmapi" ]; then _bf_start="$SMAPIBATDIR/$bs/$_bn_start" _bf_stop="$SMAPIBATDIR/$bs/$_bn_stop" fi if [ "$_bm_dischg" = "tpsmapi" ]; then _bf_dischg="$SMAPIBATDIR/$bs/$_bn_dischg" fi case "$_bm_read" in natacpi) _bd_read="$ACPIBATDIR/$bs" ;; tpsmapi) _bd_read="$SMAPIBATDIR/$bs" ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bat_idx=$_bat_idx; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop; bf_dischg=$_bf_dischg" return 0 } batdrv_read_threshold () { # read and print charge threshold # $1: start/stop # $2: 0=api/1=tlp-stat output # global param: $_bm_thresh, $_bf_start, $_bf_stop # out: # - api: 0..100/"" on error # - tlp-stat: 0..100/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local bf out="" rc=0 case $1 in start) out="$X_THRESH_SIMULATE_START" ;; stop) out="$X_THRESH_SIMULATE_STOP" ;; esac if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1, $2).simulate: bm_thresh=$_bm_thresh; bf=$bf; out=$out; rc=$rc" return 0 fi case $_bm_thresh in tpsmapi) # read threshold from sysfile case $1 in start) bf=$_bf_start ;; stop) bf=$_bf_stop ;; esac if ! out=$(read_sysf $bf); then # not readable/non-existent if [ "$2" != "1" ]; then out="" else out="(not available)" fi rc=4 fi ;; *) # no threshold api rc=255 ;; esac # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else if [ "$2" = "1" ]; then printf "(not available)\n" fi rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1, $2): bm_thresh=$_bm_thresh; bf=$bf; bat_idx=$_bat_idx; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write both charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold 0(disabled)..99/DEF(default) # $2: new stop threshold 1..100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bm_thresh, $_bat_str, $_bf_start, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_start=${1:-} local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_start old_stop # insert defaults [ "$new_start" = "DEF" ] && new_start=$_bt_def_start [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_start" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # start: check for 3 digits max, ensure min 2 / max 96 if ! is_uint "$new_start" 3 || \ ! is_within_bounds $new_start 2 96; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_start: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at START_CHARGE_THRESH_${cfg_bat}=\"${new_start}\": not specified, invalid or out of range (2..96). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at START_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (2..96). Aborted.\n" "$cfg_bat" "$new_start" 1>&2 else printf "Error: charge start threshold (%s) for %s is not specified, invalid or out of range (2..96). Aborted.\n" "$new_start" "$_bat_str" 1>&2 fi ;; esac return 2 fi # stop: check for 3 digits max, ensure min 6 / max 100 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 6 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_stop: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": not specified, invalid or out of range (6..100). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (6..100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: charge stop threshold (%s) for %s is not specified, invalid or out of range (6..100). Aborted.\n" "$new_stop" "$_bat_str" 1>&2 fi ;; esac return 2 fi # check minimum start - stop difference if [ $((new_start + 4)) -gt $new_stop ]; then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_diff: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration: START_CHARGE_THRESH_${cfg_bat} > STOP_CHARGE_THRESH_${cfg_bat} - 4. Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration: START_CHARGE_THRESH_%s > STOP_CHARGE_THRESH_%s - 4. Aborted.\n" "$cfg_bat" "$cfg_bat" 1>&2 else printf "Error: start threshold > stop threshold - 4 for %s. Aborted.\n" "$_bat_str" 1>&2 fi ;; esac return 3 fi # read active threshold values if ! old_start=$(batdrv_read_threshold start 0) || \ ! old_stop=$(batdrv_read_threshold stop 0); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).read_error: bat=$_bat_str" case $verb in 1) echo_message "Error: could not read current charge threshold(s) for $_bat_str. Battery skipped." ;; 2) printf "Error: could not read current charge threshold(s) for %s. Aborted.\n" $_bat_str 1>&2 ;; esac return 4 fi # determine write sequence because driver's intrinsic boundary conditions # must be met in all write stages: # - tpsmapi: start <= stop - 4 (changes value for compliance) local rc=0 steprc tseq if [ $new_start -gt $old_stop ]; then tseq="stop start" else tseq="start stop" fi # write new thresholds in determined sequence if [ "$verb" = "2" ]; then printf "Setting temporary charge thresholds for %s:\n" "$_bat_str" fi for step in $tseq; do local old_thresh new_thresh steprc case $step in start) old_thresh=$old_start new_thresh=$new_start ;; stop) old_thresh=$old_stop new_thresh=$new_stop ;; esac if [ "$old_thresh" != "$new_thresh" ]; then # new threshold differs from effective one --> write it case $_bm_thresh in 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=5 ;; *) # no threshold api steprc=255 rc=5 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.write: bat=$_bat_str; old=$old_thresh; new=$new_thresh; steprc=$steprc" case $verb in 2) if [ $steprc -eq 0 ]; then printf " %-5s = %3d\n" $step $new_thresh else printf " %-5s = %3d (Error: write failed)\n" $step $new_thresh 1>&2 fi ;; 1) if [ $steprc -gt 0 ]; then echo_message "Error: writing charge $step threshold for $_bat_str failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.no_change: bat=$_bat_str; old=$old_thresh; new=$new_thresh" if [ "$verb" = "2" ]; then printf " %-5s = %3d (no change)\n" $step $new_thresh fi fi done # for step echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).complete: bat=$_bat_str; rc=$rc" return $rc } batdrv_chargeonce () { # charge battery to stop threshold once # use pre-determined method and sysfiles from global parms # global param: $_bm_thresh, $_bat_str, $_bf_start, $_bf_stop # rc: 0=ok/ # 2=charge level read error # 3=charge level too high/ # 4=threshold read error/ # 5=threshold write error/ # 6=stop threshold too low # prerequisite: batdrv_init(), batdrv_select_battery() local ccharge cur_stop efull enow temp_start local rc=0 if ! cur_stop=$(batdrv_read_threshold stop 0); then printf "Error: reading charge stop threshold for %s failed. Aborted.\n" $_bat_str 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).thresh_unknown: stop=$cur_stop; rc=4" return 4 elif [ $cur_stop -lt 6 ]; then printf "Error: the %s charge stop threshold is %s. " "$_bat_str" "$ccharge" 1>&2 printf "For this command to work, it must be 6 at least.\n" 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).stop_thresh_too_low: stop=$cur_stop; rc=6" return 6 fi # get current charge level (in %) case $_bm_read in tpsmapi) # use tp-smapi sysfile ccharge=$(read_sysval $_bd_read/remaining_percent) || ccharge=-1 ;; esac if [ $ccharge -eq -1 ] ; then printf "Error: cannot determine charge level for %s.\n" "$_bat_str" 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).charge_level_unknown: enow=$enow; efull=$efull; rc=2" return 2 fi temp_start=$(( cur_stop - 4 )) if [ $ccharge -gt $temp_start ]; then printf "Error: the %s charge level is %s%%. " "$_bat_str" "$ccharge" 1>&2 printf "For this command to work, it must not exceed %s%% (stop threshold - 4).\n" "$temp_start" 1>&2 echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str).charge_level_too_high: soc=$ccharge; stop=$cur_stop; rc=3" return 3 fi printf "Setting temporary charge threshold for %s:" "$_bat_str" case $_bm_thresh in tpsmapi) write_sysf "$temp_start" $_bf_start || rc=5 ;; esac if [ $rc -eq 0 ]; then printf " start = %3d\n" $temp_start else printf " start = %3d (Error: write failed)\n" $temp_start 1>&2 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce($_bat_str): soc=$ccharge; start=$temp_start; stop=$cur_stop; rc=$rc" return $rc } batdrv_apply_configured_thresholds () { # apply configured thresholds from configuration to all batteries # output parameter errors only if batdrv_select_battery BAT0; then batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi if batdrv_select_battery BAT1; then # write configured thresholds, output parameter errors batdrv_write_thresholds "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1" 1 "BAT1"; rc=$? fi return 0 } batdrv_read_force_discharge () { # read and print force discharge state # $1: 0=api/1=tlp-stat output # global param: $_bm_dischg, $_bf_dischg # - api: 0=off/1=on/"" on error # - tlp-stat: 0=off/1=on/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local rc=0 out="" case $_bm_dischg in tpsmapi) # read force discharge from sysfile out=$(read_sysf $_bf_dischg); if ! out=$(read_sysf $_bf_dischg); then # not readable/non-existent if [ "$1" = "1" ]; then out="(not available)" fi rc=4 fi ;; *) # no discharge api if [ "$1" = "1" ]; then out="(not available)" fi rc=255 ;; esac printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge($_bat_str): bm_dischg=$_bm_dischg; bf_dischg=$_bf_dischg; out=$out; rc=$rc" return $rc } batdrv_write_force_discharge () { # write force discharge state # $1: 0=off/1=on/255=no api # global param: $_bat_str, $_bat_idx, $_bm_dischg, $_bf_dischg # rc: 0=done/5=write error # prerequisite: batdrv_init(), batdrv_select_battery() local rc=0 case $_bm_dischg in tpsmapi) # write force_discharge write_sysf "$1" $_bf_dischg || rc=5 ;; *) # no discharge api rc=255 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge($_bat_str, $1): bm_dischg=$_bm_dischg; bf_dischg=$_bf_dischg; bat_idx=$_bat_idx; rc=$rc" return $rc } batdrv_cancel_force_discharge () { # trap: called from batdrv_discharge # global param: $_bat_str # prerequisite: batdrv_discharge() batdrv_write_force_discharge 0 unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.cancelled($_bat_str)" printf " Cancelled.\n" do_exit 0 } batdrv_force_discharge_active () { # check if battery is in 'force_discharge' state # global param: $_bat_str, $_bm_read, $_bd_read # rc: 0=discharging/1=not discharging or read error # prerequisite: batdrv_init(), batdrv_select_battery() local rc=1 # check if force_discharge is on [ "$(batdrv_read_force_discharge 0)" = "1" ] && rc=0 if [ $rc -eq 0 ] && ! get_sys_power_supply; then # AC unplugged --> cancel discharge batdrv_write_force_discharge 0 rc=1 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active($_bat_str): bm_read=$_bm_read; rc=$rc" return $rc } batdrv_discharge () { # discharge battery # global param: $_bm_dischg, $_bat_idx, $_bf_dischg # rc: 0=done/1=malfunction/2=not emptied/3=ac removed/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local rc rp wt # start discharge if ! batdrv_write_force_discharge 1; then echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.force_discharge_malfunction($_bat_str)" printf "Error: discharge %s malfunction -- check your hardware (battery, charger).\n" "$_bat_str" 1>&2 return 1 fi trap batdrv_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 ! batdrv_force_discharge_active && [ $wt -gt 0 ] ; do sleep 1 printf "." wt=$((wt - 1)) done printf "\n" if batdrv_force_discharge_active; then # discharge initiated sucessfully --> wait for completion == while status "discharging" echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.running($_bat_str)" while batdrv_force_discharge_active; do clear printf "Currently discharging battery %s:\n" "$_bat_str" # show current battery state case $_bm_read in 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 printf "force discharge = %s\n" "$(batdrv_read_force_discharge 0)" printf "Press Ctrl+C to cancel.\n" sleep 5 done unlock_tlp tlp_discharge # read charge level one last time case $_bm_read in tpsmapi) # use tp-smapi sysfiles rp=$(read_sysf $_bd_read/remaining_percent) ;; esac if [ $rp -gt 0 ]; then # battery not emptied --> determine cause get_sys_power_supply # shellcheck disable=SC2154 if [ $_syspwr -eq 1 ]; then # system on battery --> AC power removed echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.ac_removed($_bat_str)" printf "Warning: battery %s was not discharged completely -- AC/charger removed.\n" "$_bat_str" 1>&2 rc=3 else # discharging terminated by unknown reason echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_emptied($_bat_str)" printf "Error: battery %s was not discharged completely i.e. terminated by the firmware -- check your hardware (battery, charger).\n" "$_bat_str" 1>&2 rc=2 fi fi else # discharge malfunction --> cancel discharge and abort batdrv_write_force_discharge 0 echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.malfunction($_bat_str)" printf "Error: discharge %s malfunction -- check your hardware (battery, charger).\n" "$_bat_str" 1>&2 rc=1 fi trap - INT # remove ^C hook if [ $rc -eq 0 ]; then printf "\n" printf "Done: battery %s was completely discharged.\n" "$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.complete($_bat_str)" fi return $rc } batdrv_show_battery_data () { # output battery data # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" local fs="" [ "$_bm_thresh" != "none" ] && fs="charge thresholds" if [ "$_bm_dischg" != "none" ]; then [ -n "$fs" ] && fs="${fs}, " fs="${fs}recalibration" fi [ -n "$fs" ] || fs="none available" printf "Supported features: %s\n" "$fs" printf "Driver usage:\n" # ThinkPad-specific battery API case $_tpsmapi in 0) printf "* tp-smapi (%s) = active " "$_batdrv_kmod"; print_methods_per_driver "tpsmapi" ;; 32) printf "* tp-smapi (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 64) printf "* tp-smapi (%s) = inactive (kernel module 'tp_smapi' load error)\n" "$_batdrv_kmod" ;; 128) printf "* tp-smapi (%s) = inactive (kernel module 'tp_smapi' not installed)\n" "$_batdrv_kmod" ;; 254) printf "* tp-smapi (%s) = inactive (ThinkPad not supported)\n" "$_batdrv_kmod" ;; *) printf "* tp-smapi (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" != "none" ]; then printf "Parameter value ranges:\n" printf "* START_CHARGE_THRESH_BAT0/1: 2..96(default)\n" printf "* STOP_CHARGE_THRESH_BAT0/1: 6..100(default)\n" fi printf "\n" local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate detected batteries batdrv_select_battery $bat case $_bat_idx in 1) printf "+++ ThinkPad Battery Status: %s (Main / Internal)\n" "$bat" ;; 2) printf "+++ ThinkPad Battery Status: %s (Ultrabay / Slice / Replaceable)\n" "$bat" ;; 0) printf "+++ ThinkPad Battery Status: %s\n" "$bat" ;; esac # --- show basic data case $_bm_read in natacpi) # use ACPI data printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi ;; # natacpi tpsmapi) # ThinkPad with active tp-smapi printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model printparm "%-59s = ##%s##" $_bd_read/manufacture_date printparm "%-59s = ##%s##" $_bd_read/first_use_date printparm "%-59s = ##%6d##" $_bd_read/cycle_count if [ -f $_bd_read/temperature ]; then # shellcheck disable=SC2046 perl -e 'printf ("%-59s = %6d [°C]\n", "'$_bd_read/temperature'", '$(read_sysval $_bd_read/temperature)' / 1000.0);' fi printparm "%-59s = ##%6d## [mWh]" $_bd_read/design_capacity printparm "%-59s = ##%6d## [mWh]" $_bd_read/last_full_capacity printparm "%-59s = ##%6d## [mWh]" $_bd_read/remaining_capacity printparm "%-59s = ##%6d## [%%]" $_bd_read/remaining_percent printparm "%-59s = ##%6s## [min]" $_bd_read/remaining_running_time_now printparm "%-59s = ##%6s## [min]" $_bd_read/remaining_charging_time printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now printparm "%-59s = ##%6d## [mW]" $_bd_read/power_avg print_batstate $_bd_read/state printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/design_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group0_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group1_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group2_voltage printparm "%-59s = ##%6s## [mV]" $_bd_read/group3_voltage printf "\n" fi # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/design_capacity) ef=$(read_sysval $_bd_read/last_full_capacity) en=$(read_sysval $_bd_read/remaining_capacity) efsum=$((efsum + ef)) ensum=$((ensum + en)) ;; # tp-smapi esac # $_bm_read # --- show battery features: thresholds, force_discharge local lf=0 if [ "$_bm_thresh" = "tpsmapi" ]; then printf "%-59s = %6s [%%]\n" "$_bf_start" "$(batdrv_read_threshold start 1)" printf "%-59s = %6s [%%]\n" "$_bf_stop" "$(batdrv_read_threshold stop 1)" lf=1 fi if [ "$_bm_dischg" = "tpsmapi" ]; then printf "%-59s = %6s\n" "$_bf_dischg" "$(batdrv_read_force_discharge 1)" lf=1 fi [ $lf -gt 0 ] && printf "\n" # --- show charge level (SOC) and capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # output Legacy ThinkPad specific recommendations # prerequisite: batdrv_init() if [ "$_tpsmapi" = "128" ]; then printf "Install tp-smapi kernel modules for ThinkPad battery thresholds and recalibration\n" fi return 0 } TLP-1.5.0/bat.d/15-asus000066400000000000000000000407331416575757700143240ustar00rootroot00000000000000#!/bin/sh # 15-asus - Battery Plugin for ASUS laptops w/ asus_wmi driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_ASUS_MD=/sys/module/asus_wmi batdrv_is_asus () { # check if kernel module loaded # rc: 0=ASUS, 1=other hardware [ -d $BATDRV_ASUS_MD ] } # --- Plugin API functions batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for native kernel acpi (Linux 5.4 or higher required) # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. define sysfile basename for natacpi # stop threshold --> retval $_bn_stop, # # 4. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 5. define charge threshold defaults # stop threshold --> retval $_bt_def_stop; _batdrv_plugin="asus" _batdrv_kmod="asus_wmi" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_asus; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _bn_stop="" _batteries="" _bt_def_stop=100 # iterate batteries and check for native kernel ACPI local bd bs local done=0 for bd in "$ACPIBATDIR"/BAT[01CT]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi # skip natacpi detection for 2nd and subsequent batteries [ $done -eq 1 ] && continue done=1 if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif [ -f $bd/charge_control_end_threshold ] && readable_sysf $bd/charge_control_end_threshold; then # sysfile for stop threshold exists and is actually readable _natacpi=0 _bm_thresh="natacpi" _bn_stop="charge_control_end_threshold" elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bn_stop="charge_control_end_threshold" else # nothing detected _natacpi=254 fi fi done # quit if no battery detected, there is no point in activating the plugin if [ -z "$_batteries" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_batteries" return 2 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; stop=$_bn_stop;" return 0 } batdrv_select_battery () { # determine battery acpidir and sysfile # $1: BAT0/BATC/BATT/BAT1/DEF # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1/BATT; # $_bd_read: directory with battery data sysfiles; # $_bf_stop: sysfile for stop threshold; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory _bf_stop="" # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfile _bd_read="$ACPIBATDIR/$_bat_str" if [ "$_bm_thresh" = "natacpi" ]; then _bf_stop="$ACPIBATDIR/$_bat_str/$_bn_stop" fi echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read; bf_stop=$_bf_stop" return 0 } batdrv_read_threshold () { # read and print charge threshold (stop only) # $1: 0=api/1=tlp-stat output # global param: $_bf_stop # out: # - api: 0..100/"" on error # - tlp-stat: 0..100/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local out="" rc=0 out="$X_THRESH_SIMULATE_STOP" if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bf_stop=$_bf_stop; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then if ! out=$(read_sysf $_bf_stop); then # not readable/non-existent if [ "$1" != "1" ]; then out="" else out="(not available)" fi rc=4 fi else # no threshold api if [ "$1" = "1" ]; then out="(not available)" fi rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else if [ "$1" = "1" ]; then printf "(not available)\n" fi rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bf_stop=$_bf_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold -- unused dummy for plugin api compatibility # $2: new stop threshold 1..100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bat_str, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold out of range or non-numeric/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" # insert defaults [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds if [ -n "$cfg_bat" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # stop: check for 3 digits max, ensure min 1 / max 100 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 1 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": not specified, invalid or out of range (1..100). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (1..100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: charge stop threshold (%s) for %s is not specified, invalid or out of range (1..100). Aborted.\n" "$new_stop" "$_bat_str" 1>&2 fi ;; esac return 2 fi # give hint if threshold is not 40, 60, 80 or 100(off) if ! wordinlist "$new_stop" "40 60 80 100"; then case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\" works as expected." fi ;; 2) if [ -n "$cfg_bat" ]; then echo_message "Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\" works as expected." else printf "Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if %s works as expected.\n" "$new_stop" fi ;; esac fi # write new threshold local rc=0 if [ "$verb" = "2" ]; then printf "Setting temporary charge threshold for %s:\n" "$_bat_str" fi write_sysf "$new_stop" $_bf_stop || rc=5 echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).write: bat=$_bat_str; new=$new_stop; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " stop = %3d\n" $new_stop else printf " stop = %3d (Error: write failed)\n" $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing charge stop threshold for $_bat_str failed." fi ;; esac return $rc } batdrv_chargeonce () { # function not implemented for ASUS laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured stop thresholds from configuration to all batteries # output parameter errors only if batdrv_select_battery BAT0; then batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi # BATC, BATT use BAT0 settings if batdrv_select_battery BATC; then batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi if batdrv_select_battery BATT; then batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi if batdrv_select_battery BAT1; then batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT1" 1 "BAT1"; rc=$? fi return 0 } batdrv_read_force_discharge () { # function not implemented for ASUS laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for ASUS laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for ASUS laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for ASUS laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for ASUS laptops # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge threshold\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # native kernel ACPI battery API case $_natacpi in 0) printf "* natacpi (%s) = active (charge threshold)\n" "$_batdrv_kmod" ;; 32) printf "* natacpi (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* natacpi (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* natacpi (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* natacpi (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then printf "Parameter value range:\n" printf "* STOP_CHARGE_THRESH_BAT0/1: 0(off)..100(default)\n" fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds if [ "$_bm_thresh" = "natacpi" ]; then printf "%-59s = %6s [%%]\n" "$_bf_stop" "$(batdrv_read_threshold 1)" printf "\n" fi # --- show charge level (SOC) and capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for ASUS laptops return 0 } TLP-1.5.0/bat.d/20-huawei000066400000000000000000000412711416575757700146250ustar00rootroot00000000000000#!/bin/sh # 20-huawei - Battery Plugin for Huawei MateBooks w/ huawei_wmi driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_HUAWEI_MD=/sys/devices/platform/huawei-wmi batdrv_is_huawei () { # check if kernel module loaded # rc: 0=Huawei, 1=other hardware [ -d $BATDRV_HUAWEI_MD ] } # --- Plugin API functions readonly BATDRV_HUAWEI_THRESH="${BATDRV_HUAWEI_MD}/charge_control_thresholds" batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $_batdrv_kmod # # 1. check for native kernel acpi (Linux 5.4 or higher required) # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 4. determine charge threshold config, sysfile and defaults # START/STOP_CHARGE_THRESH_ suffix --> retval $_bt_cfg_bat, # sysfile (start and stop threshold) --> retval $_bf_start_stop, # start default --> retval $_bt_def_start, # stop default --> retval $_bt_def_stop; _batdrv_plugin="huawei" _batdrv_kmod="huawei_wmi" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_huawei; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _batteries="" _bt_def_start=0 _bt_def_stop=100 # iterate batteries local bd bs for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # check for vendor specific kernel api if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif [ -f "$BATDRV_HUAWEI_THRESH" ] && readable_sysf "$BATDRV_HUAWEI_THRESH"; then # sysfile exists and is actually readable _natacpi=0 _bm_thresh="natacpi" _bf_start_stop="$BATDRV_HUAWEI_THRESH" elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bf_start_stop="$BATDRV_HUAWEI_THRESH" else # nothing detected _natacpi=254 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; start_stop=$_bf_start_stop;" return 0 } batdrv_select_battery () { # determine battery acpidir # $1: BAT0/BAT1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1; # $_bd_read: directory with battery data sysfiles; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read;" return 0 } batdrv_read_threshold () { # read and print both charge thresholds # global param: $_bm_thresh, $_bf_start_stop # out: both thresholds 0..100 separated with a blank/"" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local rc=0 local out="$X_THRESH_SIMULATE_START $X_THRESH_SIMULATE_STOP" if [ "$out" != " " ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bm_thresh=$_bm_thresh; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then out=$(read_sysf $_bf_start_stop) || rc=4 else rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bm_thresh=$_bm_thresh; bf_start_stop=$_bf_start_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write both charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold 0(off)..100/DEF(default) # $2: new stop threshold 0(off)..100(off)/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bm_thresh, $_bf_start_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_start=${1:-} local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local new_threshs local rc=0 # insert defaults [ "$new_start" = "DEF" ] && new_start=$_bt_def_start [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds if [ -n "$cfg_bat" ] && [ -z "$new_start" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).not_configured" return 1 fi # start: check for 3 digits max, ensure min 0 / max 99 if ! is_uint "$new_start" 3 || \ ! is_within_bounds $new_start 0 99; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_start" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at START_CHARGE_THRESH_${cfg_bat}=\"${new_start}\": not specified, invalid or out of range (0..99). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at START_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (0..99). Aborted.\n" "$cfg_bat" "$new_start" 1>&2 else printf "Error: charge start threshold (%s) is not specified, invalid or out of range (0..99). Aborted.\n" "$new_start" 1>&2 fi ;; esac return 2 fi # stop: check for 3 digits max, ensure min 1 / max 100 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 1 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_stop" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": not specified, invalid or out of range (1..100). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (1..100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: charge stop threshold (%s) is not specified, invalid or out of range (1..100). Aborted.\n" "$new_stop" 1>&2 fi ;; esac return 2 fi # check start <= stop if [ $new_start -gt $new_stop ]; then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_diff" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration: START_CHARGE_THRESH_${cfg_bat} > STOP_CHARGE_THRESH_${cfg_bat}. Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration: START_CHARGE_THRESH_%s > STOP_CHARGE_THRESH_%s. Aborted.\n" "$cfg_bat" "$cfg_bat" 1>&2 else printf "Error: start threshold > stop threshold. Aborted.\n" 1>&2 fi ;; esac return 3 fi # write new thresholds if [ "$verb" = "2" ]; then printf "Setting temporary charge thresholds:\n" fi new_threshs="$new_start $new_stop" case $_bm_thresh in natacpi) write_sysf "$new_threshs" $_bf_start_stop || rc=5 ;; *) # no threshold api rc=5 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4): new=$new_threshs; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " %s = %d, %s = %d\n" start $new_start stop $new_stop else printf " %s = %d, %s = %d (Error: write failed)\n" start $new_start stop $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing charge thresholds failed." fi ;; esac return $rc } batdrv_chargeonce () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured stop thresholds from configuration to all batteries # output parameter errors only batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0" return 0 } batdrv_read_force_discharge () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for Huawei MateBooks # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} local threshs printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge thresholds\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # native kernel ACPI battery API case $_natacpi in 0) printf "* vendor (%s) = active (charge thresholds)\n" "$_batdrv_kmod" ;; 32) printf "* vendor (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* vendor (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* vendor (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* vendor (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then printf "Parameter value ranges:\n" printf "* START_CHARGE_THRESH_BAT0: 0(default)..99\n" printf "* STOP_CHARGE_THRESH_BAT0: 1..100(default)\n\n" if threshs=$(batdrv_read_threshold); then case "$threshs" in "0 0"|"0 100") printf "%-59s = %s [%%] (disabled)\n" "$_bf_start_stop" "$threshs" ;; *) printf "%-59s = %s [%%]\n" "$_bf_start_stop" "$threshs" ;; esac else printf "%-59s = %s\n" "$_bf_start_stop" "(not available)" fi fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show charge level (SOC) and capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for Huawei MateBooks return 0 } TLP-1.5.0/bat.d/25-lenovo000066400000000000000000000362301416575757700146510ustar00rootroot00000000000000#!/bin/sh # 25-ideapad - Battery Plugin for Lenovo laptops w/ ideapad_laptop driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_LENOVO_MD=/sys/bus/platform/drivers/ideapad_acpi batdrv_is_ideapad () { # check if kernel module loaded # rc: 0=Ideapad, 1=other hardware [ -d $BATDRV_LENOVO_MD ] } # --- Plugin API functions readonly BATDRV_LENOVO_CMODE="${BATDRV_LENOVO_MD}/VPC2004:00/conservation_mode" batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for vendor specific kernel api (Linux 4.14 or higher required) # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 4. define conservation mode config, sysfile and default # START/STOP_CHARGE_THRESH_ suffix --> retval $_bt_cfg_bat, # sysfile --> retval $_bf_stop, # default --> retval $_bt_def_stop; _batdrv_plugin="lenovo" _batdrv_kmod="ideapad_laptop" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_ideapad; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _batteries="" # shellcheck disable=SC2034 _bt_cfg_bat="BAT0" _bf_stop="" _bt_def_stop=0 # iterate batteries local bd bs for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # check for vendor specific kernel api if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif [ -f "$BATDRV_LENOVO_CMODE" ] && readable_sysf "$BATDRV_LENOVO_CMODE"; then # sysfile exists and is actually readable _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_LENOVO_CMODE" elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_LENOVO_CMODE" else # nothing detected _natacpi=254 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bf_stop=$_bf_stop" return 0 } batdrv_select_battery () { # determine battery acpidir # $1: BAT0/BAT1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1; # $_bd_read: directory with battery data sysfiles; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read;" return 0 } batdrv_read_threshold () { # read and print charge threshold (stop only) # global param: $_bf_stop # out: threshold 0/1/"" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local out="" rc=0 out="$X_THRESH_SIMULATE_STOP" if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bf_stop=$_bf_stop; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then out=$(read_sysf $_bf_stop) || rc=4 else # no threshold api rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bf_stop=$_bf_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold -- unused dummy for plugin api compatibility # $2: new stop threshold 0/1/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bat_str, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold out of range or non-numeric/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_stop # insert defaults [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # stop: check for 3 digits max, ensure 0 or 1 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 0 1; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": conservation mode not specified or invalid (must be 0 or 1). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": conservation mode not specified or invalid (must be 0 or 1). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: conservation mode (%s) not specified or invalid (must be 0 or 1). Aborted.\n" "$new_stop" 1>&2 fi ;; esac return 2 fi # read active stop threshold value if ! old_stop=$(batdrv_read_threshold); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).read_error" case $verb in 1) echo_message "Error: could not read current conservation mode. Skipped." ;; 2) printf "Error: could not read current conservation mode. Aborted.\n" 1>&2 ;; esac return 4 fi # write new threshold if [ "$verb" = "2" ]; then printf "Setting temporary charge threshold for all batteries:\n" fi local rc=0 if [ "$old_stop" != "$new_stop" ]; then # new threshold differs from effective one --> write it write_sysf "$new_stop" $_bf_stop || rc=5 echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).write: old=$old_stop; new=$new_stop; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " conservation mode = %d\n" $new_stop else printf " conservation mode = %d (Error: write failed)\n" $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing conservation mode failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).no_change: old=$old_stop; new=$new_stop" if [ "$verb" = "2" ]; then printf " conservation mode = %d (no change)\n" $new_stop fi fi return $rc } batdrv_chargeonce () { # function not implemented for Ideapads echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured conservation mode from configuration (concerns all batteries) # output parameter errors only batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0" return 0 } batdrv_read_force_discharge () { # function not implemented for Ideapads echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for Ideapads echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for Ideapads echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for Ideapads echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for Ideapads # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge threshold\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # vendor specific kernel api case $_natacpi in 0) printf "* vendor (%s) = active (charge threshold)\n" "$_batdrv_kmod" ;; 32) printf "* vendor (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* vendor (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* vendor (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* vendor (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then local th sfx= printf "Parameter value range:\n" printf "* STOP_CHARGE_THRESH_BAT0: 0(off), 1(on) -- battery conservation mode\n\n" if th=$(batdrv_read_threshold); then case $th in 0) sfx=" (100%)" ;; 1) sfx=" (60%)" ;; *) sfx=" (invalid)" ;; esac printf "%-59s = %d%s\n" "$_bf_stop" "$th" "$sfx" else printf "%-59s = %s\n" "$_bf_stop" "(not available)" fi fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds # --- show charge level (SOC) and capacity if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for Ideapads return 0 } TLP-1.5.0/bat.d/30-samsung000066400000000000000000000361771416575757700150320ustar00rootroot00000000000000#!/bin/sh # samsung - Battery Plugin for Samsung laptops w/ samsung_laptop driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_SAMSUNG_MD=/sys/devices/platform/samsung batdrv_is_samsung () { # check if kernel module loaded # rc: 0=Samsung, 1=other hardware [ -d $BATDRV_SAMSUNG_MD ] } # --- Plugin API functions readonly BATDRV_SAMSUNG_BLE="${BATDRV_SAMSUNG_MD}/battery_life_extender" batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for vendor specific kernel api # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 4. define battery life extender config, sysfile and default # START/STOP_CHARGE_THRESH_ suffix --> retval $_bt_cfg_bat, # sysfile --> retval $_bf_stop, # default --> retval $_bt_def_stop; _batdrv_plugin="samsung" _batdrv_kmod="samsung_laptop" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_samsung; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _batteries="" # shellcheck disable=SC2034 _bt_cfg_bat="BAT0" _bf_stop="" _bt_def_stop=0 # iterate batteries local bd bs for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # check for vendor specific kernel api if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif [ -f "$BATDRV_SAMSUNG_BLE" ] && readable_sysf "$BATDRV_SAMSUNG_BLE"; then # sysfile exists and is actually readable _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_SAMSUNG_BLE" elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_SAMSUNG_BLE" else # nothing detected _natacpi=254 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bf_stop=$_bf_stop" return 0 } batdrv_select_battery () { # determine battery acpidir # $1: BAT0/BAT1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1; # $_bd_read: directory with battery data sysfiles; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read;" return 0 } batdrv_read_threshold () { # read and print charge threshold (stop only) # global param: $_bf_stop # out: threshold 0/1/"" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local out="" rc=0 out="$X_THRESH_SIMULATE_STOP" if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bf_stop=$_bf_stop; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then out=$(read_sysf $_bf_stop) || rc=4 else # no threshold api rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bf_stop=$_bf_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold -- unused dummy for plugin api compatibility # $2: new stop threshold 0/1/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bat_str, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold out of range or non-numeric/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_stop # insert defaults [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # stop: check for 3 digits max, ensure 0 or 1 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 0 1; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": life extender not specified or invalid (must be 0 or 1). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": life extender not specified or invalid (must be 0 or 1). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: life extender (%s) not specified or invalid (must be 0 or 1). Aborted.\n" "$new_stop" 1>&2 fi ;; esac return 2 fi # read active stop threshold value if ! old_stop=$(batdrv_read_threshold); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).read_error" case $verb in 1) echo_message "Error: could not read current life extender. Skipped." ;; 2) printf "Error: could not read current life extender. Aborted.\n" 1>&2 ;; esac return 4 fi # write new threshold if [ "$verb" = "2" ]; then printf "Setting temporary charge threshold for all batteries:\n" fi local rc=0 if [ "$old_stop" != "$new_stop" ]; then # new threshold differs from effective one --> write it write_sysf "$new_stop" $_bf_stop || rc=5 echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).write: old=$old_stop; new=$new_stop; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " life extender = %d\n" $new_stop else printf " life extender = %d (Error: write failed)\n" $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing life extender failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).no_change: old=$old_stop; new=$new_stop" if [ "$verb" = "2" ]; then printf " life extender = %d (no change)\n" $new_stop fi fi return $rc } batdrv_chargeonce () { # function not implemented for Samsung laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured battery life extender from configuration (concerns all batteries) # output parameter errors only batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? return 0 } batdrv_read_force_discharge () { # function not implemented for Samsung laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for Samsung laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for Samsung laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for Samsung laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for Samsung laptops # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge threshold\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # vendor specific kernel api case $_natacpi in 0) printf "* vendor (%s) = active (charge threshold)\n" "$_batdrv_kmod" ;; 32) printf "* vendor (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* vendor (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* vendor (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* vendor (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then local th sfx= printf "Parameter value range:\n" printf "* STOP_CHARGE_THRESH_BAT0: 0(off), 1(on) -- -- battery life extender\n\n" if th=$(batdrv_read_threshold); then case $th in 0) sfx=" (100%)" ;; 1) sfx=" (80%)" ;; *) sfx=" (invalid)";; esac printf "%-59s = %d%s\n" "$_bf_stop" "$th" "$sfx" else printf "%-59s = %s\n" "$_bf_stop" "(not available)" fi fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds # --- show charge level (SOC) and capacity if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for Samsung laptops return 0 } TLP-1.5.0/bat.d/35-lg000066400000000000000000000366251416575757700137620ustar00rootroot00000000000000#!/bin/sh # lg - Battery Plugin for LG laptops w/ lg_laptop driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_LG_MD=/sys/devices/platform/lg-laptop batdrv_is_lg () { # check if kernel module loaded # rc: 0=LG, 1=other hardware [ -d $BATDRV_LG_MD ] } # --- Plugin API functions readonly BATDRV_LG_BCL="${BATDRV_LG_MD}/battery_care_limit" batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for vendor specific kernel api # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 4. define battery care limit config, sysfile and default # START/STOP_CHARGE_THRESH_ suffix --> retval $_bt_cfg_bat, # sysfile --> retval $_bf_stop, # default --> retval $_bt_def_stop; _batdrv_plugin="lg" _batdrv_kmod="lg_laptop" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_lg; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _batteries="" # shellcheck disable=SC2034 _bt_cfg_bat="BAT0" _bf_stop="" _bt_def_stop=100 # iterate batteries local bd bs for bd in "$ACPIBATDIR"/CMB[01] "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # check for vendor specific kernel api if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif sp="${X_THRESH_SIMULATE_STOP:-$(read_sysf $BATDRV_LG_BCL)}"; then # sysfile exists and is actually readable --> check threshold value if [ "$sp" != "0" ]; then # threshold is non-zero _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_LG_BCL" else # zero means laptop not supported via the kernel driver _natacpi=254 fi elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_LG_BCL" else # nothing detected _natacpi=254 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bf_stop=$_bf_stop" return 0 } batdrv_select_battery () { # determine battery acpidir # $1: BAT0/BAT1/CMB0/CMB1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1/CMB0/CMB1; # $_bd_read: directory with battery data sysfiles; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read;" return 0 } batdrv_read_threshold () { # read and print charge threshold (stop only) # global param: $_bf_stop # out: threshold 80,100/"" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local out="" rc=0 out="$X_THRESH_SIMULATE_STOP" if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bf_stop=$_bf_stop; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then out=$(read_sysf $_bf_stop) || rc=4 else # no threshold api rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bf_stop=$_bf_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold -- unused dummy for plugin api compatibility # $2: new stop threshold 80,100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bat_str, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold out of range or non-numeric/ # 4=threshold read error/ # 5=threshold write error/ # 6=threshold write discarded by kernel or firmware # prerequisite: batdrv_init(), batdrv_select_battery() local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_stop # insert defaults [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # stop: check for 3 digits max, ensure 80 or 100 if ! is_uint "$new_stop" 3 || \ [ $new_stop -ne 80 ] && [ $new_stop -ne 100 ]; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": care limit not specified or invalid (must be 80 or 100). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": care limit not specified or invalid (must be 80 or 100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: care limit (%s) not specified or invalid (must be 80 or 100). Aborted.\n" "$new_stop" 1>&2 fi ;; esac return 2 fi # read active stop threshold value if ! old_stop=$(batdrv_read_threshold); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).read_error" case $verb in 1) echo_message "Error: could not read current care limit. Skipped." ;; 2) printf "Error: could not read current care limit. Aborted.\n" 1>&2 ;; esac return 4 fi # write new threshold if [ "$verb" = "2" ]; then printf "Setting temporary charge threshold for all batteries:\n" fi local rc=0 if [ "$old_stop" != "$new_stop" ]; then # new threshold differs from effective one --> write it if write_sysf "$new_stop" $_bf_stop; then if [ "$(read_sysf $_bf_stop)" != "$new_stop" ]; then # write discarded by kernel or firmware rc=6 fi else # write error rc=5 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).write: old=$old_stop; new=$new_stop; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " care limit = %d\n" $new_stop else printf " care limit = %d (Error: write failed)\n" $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing care limit failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).no_change: old=$old_stop; new=$new_stop" if [ "$verb" = "2" ]; then printf " care limit = %d (no change)\n" $new_stop fi fi return $rc } batdrv_chargeonce () { # function not implemented for LG laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured battery care limit from configuration (concerns all batteries) # output parameter errors only batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? return 0 } batdrv_read_force_discharge () { # function not implemented for LG laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for LG laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for LG laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for LG laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for LG laptops # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge threshold\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # vendor specific kernel api case $_natacpi in 0) printf "* vendor (%s) = active (charge threshold)\n" "$_batdrv_kmod" ;; 32) printf "* vendor (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* vendor (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* vendor (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* vendor (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then local th printf "Parameter value range:\n" printf "* STOP_CHARGE_THRESH_BAT0: 80(on), 100(off) -- battery care limit\n\n" if th=$(batdrv_read_threshold); then printf "%-59s = %d [%%]\n" "$_bf_stop" "$th" else printf "%-59s = %s\n" "$_bf_stop" "(not available)" fi fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds # --- show charge level (SOC) and capacity if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for LG laptops return 0 } TLP-1.5.0/bat.d/40-sony000066400000000000000000000364521416575757700143420ustar00rootroot00000000000000#!/bin/sh # sony - Battery Plugin for Sony laptops w/ sony_laptop driver # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_SONY_MD=/sys/devices/platform/sony-laptop batdrv_is_sony () { # check if kernel module loaded # rc: 0=Sony, 1=other hardware [ -d $BATDRV_SONY_MD ] } # --- Plugin API functions readonly BATDRV_SONY_BLE="${BATDRV_SONY_MD}/battery_care_limiter" batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $batdrv_kmod # # 1. check for vendor specific kernel api # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 4. define battery care limiter config, sysfile and default # START/STOP_CHARGE_THRESH_ suffix --> retval $_bt_cfg_bat, # sysfile --> retval $_bf_stop, # default --> retval $_bt_def_stop; _batdrv_plugin="sony" _batdrv_kmod="sony_laptop" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_sony; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _batteries="" # shellcheck disable=SC2034 _bt_cfg_bat="BAT0" _bf_stop="" _bt_def_stop=100 # iterate batteries local bd bs for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # check for vendor specific kernel api if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 elif [ -f "$BATDRV_SONY_BLE" ] && readable_sysf "$BATDRV_SONY_BLE"; then # sysfile exists and is actually readable _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_SONY_BLE" elif [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then # simulate api _natacpi=0 _bm_thresh="natacpi" _bf_stop="$BATDRV_SONY_BLE" else # nothing detected _natacpi=254 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bf_stop=$_bf_stop" return 0 } batdrv_select_battery () { # determine battery acpidir # $1: BAT0/BAT1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1; # $_bd_read: directory with battery data sysfiles; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read;" return 0 } batdrv_read_threshold () { # read and print charge threshold (stop only) # global param: $_bf_stop # out: threshold 50/80/100/"" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local out="" rc=0 out="$X_THRESH_SIMULATE_STOP" if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold.simulate: bf_stop=$_bf_stop; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then out=$(read_sysf $_bf_stop) || rc=4 else # no threshold api rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold: bf_stop=$_bf_stop; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold -- unused dummy for plugin api compatibility # $2: new stop threshold 50/80/100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bat_str, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold out of range or non-numeric/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_stop local sony_stop # insert defaults [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # stop: check for 3 digits max, ensure 50, 80 or 100 if ! is_uint "$new_stop" 3 || \ ! wordinlist "$new_stop" "50 80 100"; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).invalid_stop" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": care limiter not specified or invalid (must be 50, 80 or 100). Skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": care limiter not specified or invalid (must be 50, 80 or 100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: care limiter (%s) not specified or invalid (must be 50, 80 or 100). Aborted.\n" "$new_stop" 1>&2 fi ;; esac return 2 fi # convention to disable the stop threshold is 100 --> translate to 0 for Sony if [ "$new_stop" = "100" ]; then sony_stop="0" else sony_stop="$new_stop" fi # read active stop threshold value if ! old_stop=$(batdrv_read_threshold); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).read_error" case $verb in 1) echo_message "Error: could not read current care limiter. Skipped." ;; 2) printf "Error: could not read current care limiter. Aborted.\n" 1>&2 ;; esac return 4 fi # write new threshold if [ "$verb" = "2" ]; then printf "Setting temporary charge threshold for all batteries:\n" fi local rc=0 if [ "$old_stop" != "$sony_stop" ]; then # new threshold differs from effective one --> write it write_sysf "$sony_stop" $_bf_stop || rc=5 echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).write: old=$old_stop; new=$sony_stop; rc=$rc" case $verb in 2) if [ $rc -eq 0 ]; then printf " care limiter = %d\n" $new_stop else printf " care limiter = %d (Error: write failed)\n" $new_stop 1>&2 fi ;; 1) if [ $rc -gt 0 ]; then echo_message "Error: writing care limiter failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($2, $3, $4).no_change: old=$old_stop; new=$sony_stop" if [ "$verb" = "2" ]; then printf " care limiter = %d (no change)\n" $new_stop fi fi return $rc } batdrv_chargeonce () { # function not implemented for Sony laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured battery life extender from configuration (concerns all batteries) # output parameter errors only batdrv_write_thresholds "DEF" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? return 0 } batdrv_read_force_discharge () { # function not implemented for Sony laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for Sony laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for Sony laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for Sony laptops echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for Sony laptops # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" = "natacpi" ]; then printf "Supported features: charge threshold\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # vendor specific kernel api case $_natacpi in 0) printf "* vendor (%s) = active (charge threshold)\n" "$_batdrv_kmod" ;; 32) printf "* vendor (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* vendor (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* vendor (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* vendor (%s) = unknown status\n" "$_batdrv_kmod" ;; esac if [ "$_bm_thresh" = "natacpi" ]; then local sp printf "Parameter value range:\n" printf "* STOP_CHARGE_THRESH_BAT0: 50, 80, 100(off) -- battery care limiter\n\n" if sp=$(batdrv_read_threshold); then if [ "$sp" = "0" ]; then printf "%-59s = %6d (100) [%%]\n" "$_bf_stop" "$sp" else printf "%-59s = %6d [%%]\n" "$_bf_stop" "$sp" fi else printf "%-59s = %s [%%]\n" "$_bf_stop" "(not available)" fi fi printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds # --- show charge level (SOC) and capacity if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for Sony laptops return 0 } TLP-1.5.0/bat.d/90-generic000066400000000000000000000162031416575757700147630ustar00rootroot00000000000000#!/bin/sh # 90-generic - Battery plugin catchall for laptops that either not provide # a kernel interface for battery care or TLP doesn't support it yet. # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 batdrv_init () { # detect hardware and initialize driver # rc: 0 (catchall) # retval: $_batdrv_plugin # # 1. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 2. designate battery care as unsupported # reading battery data --> retval $_bm_read = "none", # reading/writing charging thresholds --> retval $_bm_thresh = "none", # reading/writing force discharge --> retval $_bm_dischg = "none": _batdrv_plugin="generic" # iterate batteries local bs bd _batteries="" for bd in "$ACPIBATDIR"/*; do if [ "$(read_sysf $bd/type)" = "Battery" ] \ && [ "$(read_sysf $bd/present)" = "1" ]; then bs=${bd##/*/} # ignore atypical power supplies and batteries printf '%s\n' "$bs" | grep -E -q "$RE_PS_IGNORE" && continue # record detected batteries and directories if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi fi done # shellcheck disable=SC2034 _bm_read="none" # shellcheck disable=SC2034 _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries" # catchall: always return 0 return 0 } batdrv_select_battery () { # determine battery acpidir # $1: battery # retval: $_bd_read: directory with battery data sysfiles _bd_read="$ACPIBATDIR/$bat" return 0 } batdrv_read_threshold () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.read_treshold.not_implemented" return 255 } batdrv_write_thresholds () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.write_tresholds.not_implemented" return 255 } batdrv_chargeonce () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.chargeonce.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.apply_configured_thresholds.not_implemented" return 255 } batdrv_read_force_discharge () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented for generic hardware echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented for generic hardware # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery data # $1: 1=verbose # global param: $_batteries # rc: 0=ok/1=no batteries specified local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" printf "Supported features: none available\n\n" local bat lf local bcnt=0 local ed ef en local efsum=0 local ensum=0 if [ -z "$_batteries" ]; then printf "+++ Battery Status\n" printf "No battery data available.\n\n" return 1 fi for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi printparm "%-59s = ##%6d## [%%]" $_bd_read/charge_control_start_threshold "not available" printparm "%-59s = ##%6d## [%%]" $_bd_read/charge_control_end_threshold "not available" printf "\n" # charge + capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for generic hardware return 0 } TLP-1.5.0/bat.d/TEMPLATE000066400000000000000000000515601416575757700143410ustar00rootroot00000000000000#!/bin/sh # Battery Plugin TEMPLATE for laptops providing standard sysfs nodes for # charge thresholds: # * /sys/class/power_supply/BAT0/charge_control_start_threshold # * /sys/class/power_supply/BAT0/charge_control_end_threshold # # IMPORTANT: customize at least all places marked with "TEMPLATE" # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 35-tlp-func-batt, tlp-func-stat # shellcheck disable=SC2086 # --- Hardware Detection readonly BATDRV_TEMPLATE_MD=/sys/module/TEMPLATE_MODULE_DIR batdrv_is_TEMPLATE () { # check if vendor specific kernel module is loaded # rc: 0=ok, 1=other hardware [ -d $BATDRV_TEMPLATE_MD ] } # --- Plugin API functions batdrv_init () { # detect hardware and initialize driver # rc: 0=matching hardware detected/1=not detected/2=no batteries detected # retval: $_batdrv_plugin, $_batdrv_kmod # # 1. check for native kernel acpi (Linux 5.4 or higher required) # --> retval $_natacpi: # 0=thresholds/ # 32=disabled/ # 128=no kernel support/ # 254=laptop not supported # # 2. determine method for # reading battery data --> retval $_bm_read, # reading/writing charging thresholds --> retval $_bm_thresh, # reading/writing force discharge --> retval $_bm_dischg: # none/natacpi # # 3. define sysfile basenames for natacpi # start threshold --> retval $_bn_start, # stop threshold --> retval $_bn_stop, # # 4. determine present batteries # list of batteries (space separated) --> retval $_batteries; # # 5. define charge threshold defaults # start threshold --> retval $_bt_def_start, # stop threshold --> retval $_bt_def_stop; _batdrv_plugin="TEMPLATE" _batdrv_kmod="TEMPLATE_MOD" # kernel module for natacpi # check plugin simulation override and denylist if [ -n "$X_BAT_PLUGIN_SIMULATE" ]; then if [ "$X_BAT_PLUGIN_SIMULATE" = "$_batdrv_plugin" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate" else echo_debug "bat" "batdrv_init.${_batdrv_plugin}.simulate_skip" return 1 fi elif wordinlist "$_batdrv_plugin" "$X_BAT_PLUGIN_DENYLIST"; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.denylist" return 1 else # check if hardware matches if ! batdrv_is_TEMPLATE; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_match" return 1 fi fi # presume no features at all _natacpi=128 # shellcheck disable=SC2034 _bm_read="natacpi" _bm_thresh="none" # shellcheck disable=SC2034 _bm_dischg="none" _bn_start="" _bn_stop="" _batteries="" # TEMPLATE: customize to vendor defaults _bt_def_start=96 _bt_def_stop=100 # iterate batteries and check for native kernel ACPI local bd bs local done=0 # TEMPLATE: customize battery names if necessary for bd in "$ACPIBATDIR"/BAT[01]; do if [ "$(read_sysf $bd/present)" = "1" ]; then # record detected batteries and directories bs=${bd##/*/} if [ -n "$_batteries" ]; then _batteries="$_batteries $bs" else _batteries="$bs" fi # skip natacpi detection for 2nd and subsequent batteries [ $done -eq 1 ] && continue done=1 if [ "$NATACPI_ENABLE" = "0" ]; then # natacpi disabled in configuration --> skip actual detection _natacpi=32 continue fi if [ -f $bd/charge_control_start_threshold ] \ && [ -f $bd/charge_control_end_threshold ]; then # threshold sysfiles exist _bn_start="charge_control_start_threshold" _bn_stop="charge_control_end_threshold" _natacpi=254 else # nothing detected _natacpi=254 continue fi if readable_sysf $bd/$_bn_start \ && readable_sysf $bd/$_bn_stop; then # threshold sysfiles are actually readable _natacpi=0 _bm_thresh="natacpi" fi fi done # quit if no battery detected, there is no point in activating the plugin if [ -z "$_batteries" ]; then echo_debug "bat" "batdrv_init.${_batdrv_plugin}.no_batteries" return 2 fi # shellcheck disable=SC2034 _batdrv_selected=$_batdrv_plugin echo_debug "bat" "batdrv_init.${_batdrv_plugin}: batteries=$_batteries; natacpi=$_natacpi; thresh=$_bm_thresh; bn_start=$_bn_start; bn_stop=$_bn_stop;" return 0 } batdrv_select_battery () { # determine battery acpidir and sysfiles # $1: BAT0/BAT1/DEF # # rc: 0=bat exists/1=bat non-existent # retval: $_bat_str: BAT0/BAT1/BATT; # $_bd_read: directory with battery data sysfiles; # $_bf_start: sysfile for stop threshold; # $_bf_stop: sysfile for stop threshold; # prerequisite: batdrv_init() # defaults _bat_str="" # no bat _bd_read="" # no directory _bf_start="" _bf_stop="" # validate battery param local bs case $1 in DEF) # 1st battery is default _bat_str="${_batteries%% *}" ;; *) if wordinlist "$1" "$_batteries"; then _bat_str=$1 else # battery not present --> quit echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1).not_present" return 1 fi ;; esac # determine natacpi sysfiles _bd_read="$ACPIBATDIR/$_bat_str" if [ "$_bm_thresh" = "natacpi" ]; then _bf_start="$ACPIBATDIR/$_bat_str/$_bn_start" _bf_stop="$ACPIBATDIR/$_bat_str/$_bn_stop" fi echo_debug "bat" "batdrv.${_batdrv_plugin}.select_battery($1): bat_str=$_bat_str; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop" return 0 } batdrv_read_threshold () { # read and print charge threshold # $1: start/stop # $2: 0=api/1=tlp-stat output # global param: $_bm_thresh, $_bf_start, $_bf_stop # out: # - api: 0..100/"" on error # - tlp-stat: 0..100/"(not available)" on error # rc: 0=ok/4=read error/255=no api # prerequisite: batdrv_init(), batdrv_select_battery() local bf out="" rc=0 case $1 in start) out="$X_THRESH_SIMULATE_START" ;; stop) out="$X_THRESH_SIMULATE_STOP" ;; esac if [ -n "$out" ]; then printf "%s" "$out" echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1).simulate: bm_thresh=$_bm_thresh; bf=$bf; out=$out; rc=$rc" return 0 fi if [ "$_bm_thresh" = "natacpi" ]; then # read threshold from sysfile case $1 in start) bf=$_bf_start ;; stop) bf=$_bf_stop ;; esac if ! out=$(read_sysf $bf); then # not readable/non-existent if [ "$2" != "1" ]; then out="" else out="(not available)" fi rc=4 fi else # no threshold api if [ "$2" = "1" ]; then out="(not available)" fi rc=255 fi # "return" threshold if [ "$X_THRESH_SIMULATE_READERR" != "1" ]; then printf "%s" "$out" else if [ "$2" = "1" ]; then printf "(not available)\n" fi rc=4 fi echo_debug "bat" "batdrv.${_batdrv_plugin}.read_threshold($1): bm_thresh=$_bm_thresh; bf=$bf; out=$out; rc=$rc" return $rc } batdrv_write_thresholds () { # write both charge thresholds for a battery # use pre-determined method and sysfiles from global parms # $1: new start threshold 0(disabled)..100/DEF(default) # $2: new stop threshold 0..100/DEF(default) # $3: 0=quiet/1=output parameter errors/2=output progress and errors # $4: battery - non-empty string indicates thresholds stem from configuration # global param: $_bm_thresh, $_bat_str, $_bf_start, $_bf_stop # rc: 0=ok/ # 1=not configured/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=threshold read error/ # 5=threshold write error # prerequisite: batdrv_init(), batdrv_select_battery() local new_start=${1:-} local new_stop=${2:-} local verb=${3:-0} local cfg_bat="$4" local old_start old_stop # insert defaults [ "$new_start" = "DEF" ] && new_start=$_bt_def_start [ "$new_stop" = "DEF" ] && new_stop=$_bt_def_stop # --- validate thresholds local rc if [ -n "$cfg_bat" ] && [ -z "$new_start" ] && [ -z "$new_stop" ]; then # do nothing if unconfigured echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).not_configured: bat=$_bat_str" return 1 fi # TEMPLATE: customize boundaries to vendor specs # start: check for 3 digits max, ensure min 0 / max 100 if ! is_uint "$new_start" 3 || \ ! is_within_bounds $new_start 0 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_start: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at START_CHARGE_THRESH_${cfg_bat}=\"${new_start}\": not specified, invalid or out of range (0..100). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at START_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (0..100). Aborted.\n" "$cfg_bat" "$new_start" 1>&2 else printf "Error: charge start threshold (%s) for %s is not specified, invalid or out of range (0..100). Aborted.\n" "$new_start" "$_bat_str" 1>&2 fi ;; esac return 2 fi # TEMPLATE: customize boundaries to vendor specs # stop: check for 3 digits max, ensure min 0 / max 100 if ! is_uint "$new_stop" 3 || \ ! is_within_bounds $new_stop 0 100; then # threshold out of range echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_stop: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration at STOP_CHARGE_THRESH_${cfg_bat}=\"${new_stop}\": not specified, invalid or out of range (0..100). Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration at STOP_CHARGE_THRESH_%s=\"%s\": not specified, invalid or out of range (0..100). Aborted.\n" "$cfg_bat" "$new_stop" 1>&2 else printf "Error: charge stop threshold (%s) for %s is not specified, invalid or out of range (0..100). Aborted.\n" "$new_stop" "$_bat_str" 1>&2 fi ;; esac return 2 fi # TEMPLATE: customize assertion to vendor specs # check start <= stop if [ $new_start -gt $new_stop ]; then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).invalid_diff: bat=$_bat_str" case $verb in 1) if [ -n "$cfg_bat" ]; then echo_message "Error in configuration: START_CHARGE_THRESH_${cfg_bat} > STOP_CHARGE_THRESH_${cfg_bat}. Battery skipped." fi ;; 2) if [ -n "$cfg_bat" ]; then printf "Error in configuration: START_CHARGE_THRESH_%s > STOP_CHARGE_THRESH_%s. Aborted.\n" "$cfg_bat" "$cfg_bat" 1>&2 else printf "Error: start threshold > stop threshold for %s. Aborted.\n" "$_bat_str" 1>&2 fi ;; esac return 3 fi # read active threshold values if ! old_start=$(batdrv_read_threshold start 0) || \ ! old_stop=$(batdrv_read_threshold stop 0); then echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).read_error: bat=$_bat_str" case $verb in 1) echo_message "Error: could not read current charge threshold(s) for $_bat_str. Battery skipped." ;; 2) printf "Error: could not read current charge threshold(s) for %s. Aborted.\n" $_bat_str 1>&2 ;; esac return 4 fi # TEMPLATE: customize assertion to vendor specs # determine write sequence too meet boundary condition start <= stop # disclaimer: the driver doesn't enforce it but we don't know about the # firmware and it's reasonable anyway local rc=0 steprc tseq if [ $new_start -gt $old_stop ]; then tseq="stop start" else tseq="start stop" fi # write new thresholds in determined sequence if [ "$verb" = "2" ]; then printf "Setting temporary charge thresholds for %s:\n" "$_bat_str" fi for step in $tseq; do local old_thresh new_thresh steprc case $step in start) old_thresh=$old_start new_thresh=$new_start ;; stop) old_thresh=$old_stop new_thresh=$new_stop ;; esac if [ "$old_thresh" != "$new_thresh" ]; then # new threshold differs from effective one --> write it case $_bm_thresh in natacpi) case $step in start) write_sysf "$new_thresh" $_bf_start ;; stop) write_sysf "$new_thresh" $_bf_stop ;; esac steprc=$?; [ $steprc -ne 0 ] && rc=5 ;; *) # no threshold api steprc=255 rc=5 ;; esac echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.write: bat=$_bat_str; old=$old_thresh; new=$new_thresh; steprc=$steprc" case $verb in 2) if [ $steprc -eq 0 ]; then printf " %-5s = %3d\n" $step $new_thresh else printf " %-5s = %3d (Error: write failed)\n" $step $new_thresh 1>&2 fi ;; 1) if [ $steprc -gt 0 ]; then echo_message "Error: writing charge $step threshold for $_bat_str failed." fi ;; esac else echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).$step.no_change: bat=$_bat_str; old=$old_thresh; new=$new_thresh" if [ "$verb" = "2" ]; then printf " %-5s = %3d (no change)\n" $step $new_thresh fi fi done # for step echo_debug "bat" "batdrv.${_batdrv_plugin}.write_thresholds($1, $2, $3, $4).complete: bat=$_bat_str; rc=$rc" return $rc } batdrv_chargeonce () { # function not implemented for Huawei MateBooks echo_debug "bat" "batdrv.${_batdrv_plugin}.charge_once.not_implemented" return 255 } batdrv_apply_configured_thresholds () { # apply configured stop thresholds from configuration to all batteries # output parameter errors only if batdrv_select_battery BAT0; then batdrv_write_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" 1 "BAT0"; rc=$? fi if batdrv_select_battery BAT1; then batdrv_write_thresholds "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1" 1 "BAT1"; rc=$? fi return 0 } batdrv_read_force_discharge () { # function not implemented echo_debug "bat" "batdrv.${_batdrv_plugin}.read_force_discharge.not_implemented" return 255 } batdrv_write_force_discharge () { # function not implemented echo_debug "bat" "batdrv.${_batdrv_plugin}.write_force_discharge.not_implemented" return 255 } batdrv_cancel_force_discharge () { # function not implemented echo_debug "bat" "batdrv.${_batdrv_plugin}.cancel_force_discharge.not_implemented" return 255 } batdrv_force_discharge_active () { # function not implemented echo_debug "bat" "batdrv.${_batdrv_plugin}.force_discharge_active.not_implemented" return 255 } batdrv_discharge () { # function not implemented # Important: release lock from caller unlock_tlp tlp_discharge echo_debug "bat" "batdrv.${_batdrv_plugin}.discharge.not_implemented" return 255 } batdrv_show_battery_data () { # output battery status # $1: 1=verbose # global param: $_batteries # prerequisite: batdrv_init() local verbose=${1:-0} printf "+++ Battery Care\n" printf "Plugin: %s\n" "$_batdrv_plugin" if [ "$_bm_thresh" != "none" ]; then printf "Supported features: charge thresholds\n" else printf "Supported features: none available\n" fi printf "Driver usage:\n" # native kernel ACPI battery API case $_natacpi in 0) printf "* natacpi (%s) = active (charge thresholds)\n" "$_batdrv_kmod" ;; 32) printf "* natacpi (%s) = inactive (disabled by configuration)\n" "$_batdrv_kmod" ;; 128) printf "* natacpi (%s) = inactive (no kernel support)\n" "$_batdrv_kmod" ;; 254) printf "* natacpi (%s) = inactive (laptop not supported)\n" "$_batdrv_kmod" ;; *) printf "* natacpi (%s) = unknown status\n" "$_batdrv_kmod" ;; esac printf "\n" # -- show battery data local bat local bcnt=0 local ed ef en local efsum=0 local ensum=0 for bat in $_batteries; do # iterate batteries batdrv_select_battery $bat printf "+++ Battery Status: %s\n" "$bat" printparm "%-59s = ##%s##" $_bd_read/manufacturer printparm "%-59s = ##%s##" $_bd_read/model_name print_battery_cycle_count "$_bd_read/cycle_count" "$(read_sysf $_bd_read/cycle_count)" if [ -f $_bd_read/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $_bd_read/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $_bd_read/power_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/energy_full_design) ef=$(read_sysval $_bd_read/energy_full) en=$(read_sysval $_bd_read/energy_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) elif [ -f $_bd_read/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $_bd_read/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $_bd_read/current_now "" 000 # store values for charge / capacity calculation below ed=$(read_sysval $_bd_read/charge_full_design) ef=$(read_sysval $_bd_read/charge_full) en=$(read_sysval $_bd_read/charge_now) efsum=$((efsum + ef)) ensum=$((ensum + en)) else ed=0 ef=0 en=0 fi print_batstate $_bd_read/status printf "\n" if [ $verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $_bd_read/voltage_now "" 000 printf "\n" fi # --- show battery features: thresholds if [ "$_bm_thresh" = "natacpi" ]; then printf "%-59s = %6s [%%]\n" "$_bf_start" "$(batdrv_read_threshold start)" printf "%-59s = %6s [%%]\n" "$_bf_stop" "$(batdrv_read_threshold stop)" printf "\n" fi # --- show charge level (SOC) and capacity lf=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lf=1 fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lf=1 fi [ $lf -gt 0 ] && printf "\n" bcnt=$((bcnt+1)) done # for bat if [ $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');' printf "\n" fi return 0 } batdrv_recommendations () { # no recommendations for Huawei MateBooks return 0 } TLP-1.5.0/changelog000066400000000000000000001326451416575757700140710ustar00rootroot00000000000000+++ 1.5.0 --- 07.01.2022 +++ * Bugfixes - tlp-stat: drop PM_RUNTIME=y kernel config recommendation +++ 1.5.0-beta.1 --- 20.12.2021 +++ * Beta release * Features Battery: - Sony laptops: stop threshold 50/80/100% aka "battery care limiter" - ThinkPads: - Use new sysfs attribute 'charge_behaviour' for recalibration - Support T400 running coreboot via natacpi (Issue #601) - tlp-stat -b: display "cycle_count = 0" with the note "or not supported" Radio Devices: - Add support for switching NFC devices - Remove support for wireless-tools (iwconfig) * Bugfixes Battery: - ASUS laptops: apply stop threshold reliably on boot and after hibernate (Issue #589) Disks: - tlp-stat -d: display correct sysfs path for disk runtime pm: /sys/block//device/power/control (Issue #606) +++ 1.4.0 --- 24.09.2021 +++ * Features Audio: - SOUND_POWER_SAVE_ON_AC: change default to enabled (Issue #495) Battery: - "Battery Features" renamed to "Battery Care" - Introduce plugins to support Battery Care for non-ThinkPads: - ASUS laptops: stop threshold - Huawei MateBooks: start and stop threshold - LG Gram laptops: stop threshold at 80% aka "battery care limit" - Lenovo laptops: stop threshold at 60% aka "battery conservation mode" - Samsung laptops: stop threshold at 80% aka "battery life extender" - ThinkPads - Allow deactivation of the start threshold (START_CHARGE_THRESH_BATx=0) - Discharge malfunction: hint to check battery and charger - Use standard sysfs attributes for charge thresholds (Issue #513) - Flag ThinkPad L512 as "unsupported" - Validate charge threshold configuration, don't fail silently - tlp-stat -b - Battery Care: show plugin, supported features, driver usage and related kernel module(s) - Map battery state "Unknown" to "Idle" for clarity Configuration: - tlp-stat --cdiff: show configuration differing from defaults - PARAMETER+="add values": append values to a parameter already defined as intrinsic default or in a *previously* read file (Issue #457) - TLP_WARN_LEVEL: warn about invalid settings, configurable for background tasks and command line - Rename parameters (backwards compatible to legacy config files): SATA_LINKPWR_BLACKLIST -> SATA_LINKPWR_DENYLIST RUNTIME_PM_BLACKLIST -> RUNTIME_PM_DENYLIST RUNTIME_PM_DRIVER_BLACKLIST -> RUNTIME_PM_DRIVER_DENYLIST USB_BLACKLIST -> USB_DENYLIST USB_BLACKLIST_BTUSB -> USB_EXCLUDE_BTUSB USB_BLACKLIST_PHONE -> USB_EXCLUDE_PHONE USB_BLACKLIST_PRINTER -> USB_EXCLUDE_PRINTER USB_BLACKLIST_WWAN -> USB_EXCLUDE_WWAN USB_WHITELIST -> USB_ALLOWLIST Disks: - AHCI_RUNTIME_PM_ON_AC/BAT: - Works only on disks defined in DISK_DEVICES - Works on NVMe (new), SATA/ATA and plugged in USB (new) disks - Works on SATA ports - No longer experimental i.e. now enabled by default; the previously existing risk of system freezes (and data loss) with the multiqueue scheduler is now eliminated: - Kernel >= 4.19 itself locks unsafe disk runtime pm - TLP generally locks it for kernel < 4.19 - DISK_APM_CLASS_DENYLIST: exclude disk classes from APM, i.e. you may now activate it for USB and IEEE1394 drives (Issue #523) - DISK_APM_LEVEL_ON_AC/BAT, DISK_SPINDOWN_TIMEOUT_ON_AC, DISK_IOSCHED: now also work when plugging in USB disks; provided disk is contained in DISK_DEVICES and 'usb' is removed from DISK_APM_CLASS_DENYLIST - tlp-stat -d - Explain why AHCI_RUNTIME_PM is locked for a disk - Show disks attached to SATA links/ports - Show a disk's host (for SATA_LINKPWR_DENYLIST) - Show ALPM state in the sysfs directory of the AHCI host - Show IDs for all disks configured in DISK_DEVICES - Distinguish SATA from ATA(IDE) disks - Show NVMe disk temperature General: - power-profiles-daemon: issue error messages about conflicting service - tlp-stat -s: determine OS release without the lsb_release utility Graphics: - Add support for amdgpu (Issue #498) - Improve powerdown of unused GPUs with drivers amdgpu, nouveau, nvidia and without driver (Issues #488, #495, #498) - tlp-stat -g: - List all GPUs with at least the driver info - Show hybrid graphics switch state (switcheroo) - Intel GPU: - Show hardware min/max frequency instead of list of available frequencies (kernel change) - Show more informative RC6, FBC and PSR status where available (Issue #203) Operation Mode AC/BAT: - Speed up system shutdown/suspend by not applying AC settings anymore - TLP_PS_IGNORE: add USB; allow to ignore multiple power supply classes - tlp-stat -s: speed up power source detection - tlp-stat --psup/udev: check if udev rules for power source changes and connecting USB devices are active PCI(e) devices: - RUNTIME_PM_ENABLE/DISABLE: permanently enable/disable runtime PM for PCI(e) devices based on address (independent of the power source) - RUNTIME_PM_DENYLIST: remove amdgpu, nvidia, pcieport from defaults - tlp-stat -e -v: show device runtime_status Platform: - PLATFORM_PROFILE_ON_AC/BAT: select platform profile to control system operating characteristics around power/performance levels, thermal and fan speed - tlp-stat -p: show state of ThinkPad lapmode Processor: - CPU_HWP_DYN_BOOST_ON_AC/BAT: Intel CPU HWP dynamic boost (Issue #468) - Remove backwards compatibility of CPU_ENERGY_PERF_POLICY_ON_AC/BAT with ENERGY_PERF_POLICY_ON_AC/BAT to prevent performance issues caused by the value 'power' in legacy configurations - PHC_CONTROLS removed (obsolete) - tlp-stat -p: - cpu1..cpuN omitted for clarity, use -v to show all - Sort more than 10 CPU cores in proper numerical order - Show intel_pstate operation mode ("status") - Omit "EPB: unsupported" for AMD or non-intel_pstate CPUs USB: - USB_EXCLUDE_AUDIO: exclude audio devices from autosuspend (Issue #556) - tlp-stat -u -v: show device runtime_status * Bugfixes Battery Care: - tlp discharge/recalibrate: - Terminate properly when AC/charger is removed - Check support on ThinkPads because of Coreboot (Issue #547) - [Try to] mitigate false threshold readouts caused by a firmware issue on ThinkPad A/E/L/S/X series (Issue #369) Disks: - Issue #474: confine AHCI_RUNTIME_PM_ON_AC/BAT to SATA disks Graphics: - Issue #488: Idle temperature 20 °C higher on battery (Nvidia GPU) - Issue #495: SOUND_POWER_SAVE_ON_AC=0 prevents powerdown of Nvidia GPU - Issue #522: Intel GPU settings fail -> validate configuration Operation Mode AC/BAT: - Issue #573: power mode not updating when switching from AC to battery Processor: - Issue #570: no_turbo=1 decreases scaling_max_freq on ThinkPad X1 Gen9 * 1.4.0 Bugfixes since beta.2 Battery: - tlp start: catch missing threshold method Configuration: - Ignore trailing blanks on non-quoted parameter values - tlp-stat --cdiff: do not show user config lines matching the default Disks: - Confine plugin event handling to USB +++ 1.4.0-beta.2 --- 09.09.2021 +++ * Beta release * Bugfixes Battery: - Huawei: use legacy interface 'charge_control_thresholds' for reliability - LG: fix battery and battery_care_limit detection (Issue #568) - ThinkPad: fix enforcement via NATACPI_ENABLE, TPAPCI_ENABLE Operation Mode AC/BAT: - Issue #573: power mode not updating when switching from AC to battery Platform: - tlp-stat -p: show state of ThinkPad lapmode Processor: - Issue #570: no_turbo=1 decreases scaling_max_freq on ThinkPad X1 Gen9 - tlp-stat -p: omit "EPB: unsupported" for AMD or non-intel_pstate CPUs +++ 1.4.0-beta.1 --- 29.07.2021 +++ * Beta release * Features Audio: - SOUND_POWER_SAVE_ON_AC: change default to enabled (Issue #495) Battery: - "Battery Features" renamed to "Battery Care" - Introduce plugins to support Battery Care for non-ThinkPads: - ASUS laptops: stop threshold - Huawei MateBooks: start and stop threshold - LG Gram laptops: stop threshold at 80% aka "battery care limit" - Lenovo laptops: stop threshold at 60% aka "battery conservation mode" - Samsung laptops: stop threshold at 80% aka "battery life extender" - ThinkPads - Allow deactivation of the start threshold (START_CHARGE_THRESH_BATx=0) - Discharge malfunction: hint to check battery and charger - Use standard sysfs attributes for charge thresholds (Issue #513) - Flag ThinkPad L512 as "unsupported" - Validate charge threshold configuration, don't fail silently - tlp-stat -b - Battery Care: show plugin, supported features, driver usage and related kernel module(s) - Map battery state "Unknown" to "Idle" for clarity Configuration: - tlp-stat --cdiff: show configuration differing from defaults - PARAMETER+="add values": append values to a parameter already defined as intrinsic default or in a *previously* read file (Issue #457) - TLP_WARN_LEVEL: warn about invalid settings, configurable for background tasks and command line - Rename parameters (backwards compatible to legacy config files): SATA_LINKPWR_BLACKLIST -> SATA_LINKPWR_DENYLIST RUNTIME_PM_BLACKLIST -> RUNTIME_PM_DENYLIST RUNTIME_PM_DRIVER_BLACKLIST -> RUNTIME_PM_DRIVER_DENYLIST USB_BLACKLIST -> USB_DENYLIST USB_BLACKLIST_BTUSB -> USB_EXCLUDE_BTUSB USB_BLACKLIST_PHONE -> USB_EXCLUDE_PHONE USB_BLACKLIST_PRINTER -> USB_EXCLUDE_PRINTER USB_BLACKLIST_WWAN -> USB_EXCLUDE_WWAN USB_WHITELIST -> USB_ALLOWLIST Disks: - AHCI_RUNTIME_PM_ON_AC/BAT: - Works only on disks defined in DISK_DEVICES - Works on NVMe (new), SATA/ATA and plugged in USB (new) disks - Works on SATA ports - No longer experimental i.e. now enabled by default; the previously existing risk of system freezes (and data loss) with the multiqueue scheduler is now eliminated: - Kernel >= 4.19 itself locks unsafe disk runtime pm - TLP generally locks it for kernel < 4.19 - DISK_APM_CLASS_DENYLIST: exclude disk classes from APM, i.e. you may now activate it for USB and IEEE1394 drives (Issue #523) - DISK_APM_LEVEL_ON_AC/BAT, DISK_SPINDOWN_TIMEOUT_ON_AC, DISK_IOSCHED: now also work when plugging in USB disks; provided disk is contained in DISK_DEVICES and 'usb' is removed from DISK_APM_CLASS_DENYLIST - tlp-stat -d - Explain why AHCI_RUNTIME_PM is locked for a disk - Show disks attached to SATA links/ports - Show a disk's host (for SATA_LINKPWR_DENYLIST) - Show ALPM state in the sysfs directory of the AHCI host - Show IDs for all disks configured in DISK_DEVICES - Distinguish SATA from ATA(IDE) disks - Show NVMe disk temperature General: - power-profiles-daemon: issue error messages about conflicting service - tlp-stat -s: determine OS release without the lsb_release utility Graphics: - Add support for amdgpu (Issue #498) - Improve powerdown of unused GPUs with drivers amdgpu, nouveau, nvidia and without driver (Issues #488, #495, #498) - tlp-stat -g: - List all GPUs with at least the driver info - Show hybrid graphics switch state (switcheroo) - Intel GPU: - Show hardware min/max frequency instead of list of available frequencies (kernel change) - Show more informative RC6, FBC and PSR status where available (Issue #203) Operation Mode AC/BAT: - Speed up system shutdown/suspend by not applying AC settings anymore - TLP_PS_IGNORE: add USB; allow to ignore multiple power supply classes - tlp-stat -s: speed up power source detection - tlp-stat --psup/udev: check if udev rules for power source changes and connecting USB devices are active PCI(e) devices: - RUNTIME_PM_ENABLE/DISABLE: permanently enable/disable runtime PM for PCI(e) devices based on address (independent of the power source) - RUNTIME_PM_DENYLIST: remove amdgpu, nvidia, pcieport from defaults - tlp-stat -e -v: show device runtime_status Platform: - PLATFORM_PROFILE_ON_AC/BAT: select platform profile to control system operating characteristics around power/performance levels, thermal and fan speed Processor: - CPU_HWP_DYN_BOOST_ON_AC/BAT: Intel CPU HWP dynamic boost (Issue #468) - Remove backwards compatibility of CPU_ENERGY_PERF_POLICY_ON_AC/BAT with ENERGY_PERF_POLICY_ON_AC/BAT to prevent performance issues caused by the value 'power' in legacy configurations - PHC_CONTROLS removed (obsolete) - tlp-stat -p: - cpu1..cpuN omitted for clarity, use -v to show all - Sort more than 10 CPU cores in proper numerical order - Show intel_pstate operation mode ("status") USB: - USB_EXCLUDE_AUDIO: exclude audio devices from autosuspend (Issue #556) - tlp-stat -u -v: show device runtime_status * Bugfixes Battery Care: - tlp discharge/recalibrate: - Terminate properly when AC/charger is removed - Check support on ThinkPads because of Coreboot (Issue #547) - [Try to] mitigate false threshold readouts caused by a firmware issue on ThinkPad A/E/L/S/X series (Issue #369) Disks: - Issue #474: confine AHCI_RUNTIME_PM_ON_AC/BAT to SATA disks Graphics: - Issue #488: Idle temperature 20 °C higher on battery (Nvidia GPU) - Issue #495: SOUND_POWER_SAVE_ON_AC=0 prevents powerdown of Nvidia GPU - Issue #522: Intel GPU settings fail -> validate configuration +++ 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.5.0/de.linrunner.tlp.metainfo.xml000066400000000000000000000032131416575757700177270ustar00rootroot00000000000000 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.5.0/defaults.conf000066400000000000000000000030441416575757700146630ustar00rootroot00000000000000# /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_WARN_LEVEL=3 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_APM_CLASS_DENYLIST="usb ieee1394" 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_ON_AC=on AHCI_RUNTIME_PM_ON_BAT=auto AHCI_RUNTIME_PM_TIMEOUT=15 PCIE_ASPM_ON_AC=default PCIE_ASPM_ON_BAT=default RADEON_DPM_PERF_LEVEL_ON_AC=auto RADEON_DPM_PERF_LEVEL_ON_BAT=auto RADEON_POWER_PROFILE_ON_AC=default RADEON_POWER_PROFILE_ON_BAT=default WIFI_PWR_ON_AC=off WIFI_PWR_ON_BAT=on WOL_DISABLE=Y SOUND_POWER_SAVE_ON_AC=1 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_DENYLIST="mei_me nouveau radeon" USB_AUTOSUSPEND=1 USB_EXCLUDE_AUDIO=1 USB_EXCLUDE_BTUSB=0 USB_EXCLUDE_PHONE=0 USB_EXCLUDE_PRINTER=1 USB_EXCLUDE_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.5.0/func.d/000077500000000000000000000000001416575757700133615ustar00rootroot00000000000000TLP-1.5.0/func.d/05-tlp-func-pm000066400000000000000000000155311416575757700156750ustar00rootroot00000000000000#!/bin/sh # tlp-func-pm - Device Power Management Functions # # Copyright (c) 2022 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 local pci_enable_adr pci_disable_adr 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 # permanent addresses pci_enable_adr=${RUNTIME_PM_ENABLE:-} pci_disable_adr=${RUNTIME_PM_DISABLE:-} # driver specific denylist: # - undefined = use intrinsic default from /usr/share/tlp/defaults.conf # - empty = disable feature drv_bl="$RUNTIME_PM_DRIVER_DENYLIST" # pci address denylisting pci_bl_adr=${RUNTIME_PM_DENYLIST:-} # pci driver denylisting: corresponding pci addresses pci_bl_drv="" # cumulate pci addresses for devices with denylisted 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_enable_adr"; then # device should be permanently 'auto' (enabled) write_sysf "auto" $device/power/control echo_debug "pm" "set_runtime_pm($1).perm_auto: $device [$class]; rc=$?" elif wordinlist "$address" "$pci_disable_adr"; then # device should be permanently 'on' (disabled) write_sysf "on" $device/power/control echo_debug "pm" "set_runtime_pm($1).perm_on: $device [$class]; rc=$?" elif wordinlist "$address" "$pci_bl_adr"; then # device is in address denylist echo_debug "pm" "set_runtime_pm($1).deny_address: $device [$class]" elif wordinlist "$address" "$pci_bl_drv"; then # device is in driver denylist echo_debug "pm" "set_runtime_pm($1).deny_driver: $device [$class]" else control=$ccontrol case $control in auto|on) write_sysf $control $device/power/control echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]; rc=$?" ;; *) # deny_* --> do nothing echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]" ;; esac fi # if denylist 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" 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.5.0/func.d/10-tlp-func-cpu000066400000000000000000000354001416575757700160410ustar00rootroot00000000000000#!/bin/sh # tlp-func-cpu - Processor Functions # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants readonly CPUD=/sys/devices/system/cpu readonly CPU_BOOST_ALL_CTRL=${CPUD}/cpufreq/boost readonly INTEL_PSTATED=${CPUD}/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_HWP_DYN_BOOST=$INTEL_PSTATED/hwp_dynamic_boost readonly CPU_INFO=/proc/cpuinfo readonly ENERGYPERF=x86_energy_perf_policy readonly FWACPID=/sys/firmware/acpi # ---------------------------------------------------------------------------- # 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 available_govs cfg cpu ec gov if [ "$1" = "1" ]; then gov=${CPU_SCALING_GOVERNOR_ON_BAT:-} cfg="CPU_SCALING_GOVERNOR_ON_BAT" else gov=${CPU_SCALING_GOVERNOR_ON_AC:-} cfg="CPU_SCALING_GOVERNOR_ON_AC" fi if [ -n "$gov" ]; then # check if configured governor is available available_govs="$(read_sysf "${CPUD}"/cpu0/cpufreq/scaling_available_governors)" if [ -n "$available_govs" ] && ! wordinlist "$gov" "$available_govs"; then echo_debug "pm" "set_cpu_scaling_governor($1).not_available: $gov; available=$available_govs" echo_message "Error in configuration at ${cfg}=\"${gov}\": governor not available. Skipped." return 1 fi # apply governor ec=0 for cpu in "${CPUD}"/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=$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 "${CPUD}"/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 "${CPUD}"/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) # # 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:-} else perf=${CPU_ENERGY_PERF_POLICY_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 ;; [0-9]|[0-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) # HWP.EPP accepts 0 to 255 --> 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 "${CPUD}"/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 "${CPUD}"/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_intel_cpu_hwp_dyn_boost () { # set the Intel CPU dynamic boost feature (available in HWP active mode) # $1: 0=ac mode, 1=battery mode local val if ! check_intel_pstate; then echo_debug "pm" "set_intel_cpu_hwp_dyn_boost($1).no_intel_pstate" return 0 fi if [ "$1" = "1" ]; then val=${CPU_HWP_DYN_BOOST_ON_BAT:-} else val=${CPU_HWP_DYN_BOOST_ON_AC:-} fi if [ ! -f $CPU_HWP_DYN_BOOST ]; then echo_debug "pm" "set_intel_cpu_hwp_dyn_boost($1).not_supported" elif [ -n "$val" ]; then write_sysf "$val" $CPU_HWP_DYN_BOOST echo_debug "pm" "set_intel_cpu_hwp_dyn_boost($1): $val; rc=$?" else echo_debug "pm" "set_intel_cpu_hwp_dyn_boost($1).not_configured" 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="${CPUD}/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 } # --- Platform set_platform_profile () { # set platform profile # $1: 0=ac mode, 1=battery mode local pwr if [ "$1" = "1" ]; then pwr=${PLATFORM_PROFILE_ON_BAT:-} else pwr=${PLATFORM_PROFILE_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_platform_profile($1).not_configured" return 0 fi if [ -f $FWACPID/platform_profile ]; then if write_sysf "$pwr" $FWACPID/platform_profile; then echo_debug "pm" "set_platform_profile($1): $pwr" else echo_debug "pm" "set_platform_profile($1).write_error" fi else echo_debug "pm" "set_platform_profile($1).not_available" fi return 0 } TLP-1.5.0/func.d/15-tlp-func-disk000066400000000000000000000514461416575757700162210ustar00rootroot00000000000000#!/bin/sh # tlp-func-disk - Storage Device and Filesystem Functions # # Copyright (c) 2022 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 ALPMD=$AHCID'/host*/scsi_host/host*' readonly DISK_NOP_WORDS="_ keep" # ---------------------------------------------------------------------------- # Functions # --- Device Helpers get_disk_dev () { # translate disk id to device (sdX) # and determine disk type and capabilities # $1: id or device basename # $2: target disk device (optional) # rc: 0=disk exists (optional: amd matches target) # 1=disk non-existent (optional: or does not match target) # retval: $_disk_dev: device basename - below /dev or /sys/block; # $_disk_id: id basename - below /dev/disk/by-id; # $_disk_type: nvme/ata/sata/usb/ieee1394; # $_disk_apm: 0=no apm/1=apm capable; # $_disk_mq: scheduler: 0=single queue/1=multi queue; # $_disk_runpm: runtime pm: 0=allowed/1=denied by kernel/2=denied by tlp/3=incapable local dev id idpath local target="$2" _disk_dev="" 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="" if [ -b /dev/$_disk_dev ]; then # disk exists, lookup id for idpath in /dev/disk/by-id/*; do id="${idpath##*/}" # filter partitions [ -n "${id%%*-part[1-9]*}" ] || continue case "$id" in ata-*|usb-*) dev=$(readlink $idpath) dev=${dev##*/} if [ "$dev" = "$_disk_dev" ]; then _disk_id="$id" break fi ;; nvme-*) # filter 'nvme-eui.*' if [ -n "${id##nvme-eui.*}" ]; then dev=$(readlink $idpath) dev=${dev##*/} if [ "$dev" = "$_disk_dev" ]; then _disk_id="$id" break fi fi ;; esac done fi fi if [ -b /dev/$_disk_dev ]; then # retrieve device attributes local bus dpath path udevadm_data local DEVPATH= local ID_PATH= local ID_BUS= local ID_ATA_FEATURE_SET_PM_ENABLED= if udevadm_data="$( $UDEVADM info -q property /dev/$_disk_dev 2>/dev/null | \ grep -E '^(DEVPATH|ID_BUS|ID_PATH|ID_ATA_FEATURE_SET_PM_ENABLED)=' )"; then eval "${udevadm_data}" fi # determine device type (bus) path="$ID_PATH" bus="$ID_BUS" 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="${DEVPATH##*/}" case $dpath in nvme*) _disk_type="nvme" ;; *) _disk_type="unknown" ;; esac ;; esac esac # distinguish sata from ata(ide) disks if [ "$_disk_type" = "ata" ]; then if glob_files "/link_power_management_policy" "/sys${DEVPATH%/target*}/scsi_host/host*" > /dev/null 2>&1; then _disk_type=sata fi fi # determine APM capability _disk_apm=0 if [ "$ID_ATA_FEATURE_SET_PM_ENABLED" = "1" ]; then _disk_apm=1 fi # determine if single- or multi-queue scheduler if [ -d /sys/block/$_disk_dev/mq ]; then _disk_mq="1" else _disk_mq="0" fi # determine if runtime pm is possible and allowed if [ -f /sys/block/$_disk_dev/device/power/control ]; then # disk has a runtime pm control sysfile case "$_disk_type" in nvme) # nvme disks do not have a readable autosuspend_delay_ms, # nevertheless runtime pm changes are safe _disk_runpm="0" ;; sata|ata|usb) if ! readable_sysf /sys/block/$_disk_dev/device/power/autosuspend_delay_ms; then # autosuspend_delay_ms is missing or not readable # --> kernel itself denies runtime pm for the disk _disk_runpm="1" else # kernel allows runtime pm for the disk # --> tlp decides if it is safe if [ "$_disk_mq" = "0" ] || kernel_version_ge 4.19; then # singlequeue scheduler is considered safe for all kernels, # multiqueue and kernel >= 4.19 too _disk_runpm="0" else # multiqueue scheduler and kernel < 4.19 is considered unsafe, # because system freezes and data loss may occur when enabling # runtime pm for a sata or ata disk. # only with kernel 4.19 a lock was introduced which prevents that mq # is forced via command line options: # https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v4.19&id=b233f127042dba991229e3882c6217c80492f6ef # distribution kernels < 4.19 may be patched too, but better safe than sorry. _disk_runpm="2" fi fi ;; *) # tlp shall not (yet) touch runtime pm for other disk types _disk_runpm="2" ;; esac else # disk is not runtime pm capable _disk_runpm="3" fi if [ -n "$target" ]; then # in target mode trace output only in case of match if [ "$target" = "$_disk_dev" ]; then echo_debug "disk" "get_disk_dev($1).target: dev=$_disk_dev; id=$_disk_id; type=$_disk_type; path=$path; bus=$bus; dpath=$dpath; apm=$_disk_apm; mq=$_disk_mq; runpm=$_disk_runpm" return 0 else # no match return 1 fi else echo_debug "disk" "get_disk_dev($1): dev=$_disk_dev; id=$_disk_id; type=$_disk_type; path=$path; bus=$bus; dpath=$dpath; apm=$_disk_apm; mq=$_disk_mq; runpm=$_disk_runpm" return 0 fi else _disk_type="none" echo_debug "disk" "get_disk_dev($1).missing" return 1 fi } show_disk_ids () { # show disk id's local dev { # iterate SATA and NVMe disks for dev in $(glob_files '/*' /dev/disk/by-id/ | sed -rn 's/.*\/((ata|ieee1394|nvme|usb))/\1/p' | 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 # $2: target disk device (optional) local pwrmode="$1" local target="$2" 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 if [ -z "$target" ]; then echo_debug "disk" "*** set_disk_apm_level($pwrmode).all" else echo_debug "disk" "*** set_disk_apm_level($pwrmode).target: $target" fi # pairwise iteration DISK_DEVICES[1,n], apmlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], apmlist[min(j,m)] # operation modes: # 1. work on all disks in $DISK_DEVICES # 2. work on $target only -- when called by udev event for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_APM_LEVEL list handling} if get_disk_dev $dev $target; then log_message="set_disk_apm_level($pwrmode): $_disk_dev [$_disk_id] $1" if wordinlist "$_disk_type" "$DISK_APM_CLASS_DENYLIST"; then echo_debug "disk" "${log_message}; class denylist" elif [ "$_disk_apm" = "0" ]; 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 # quit the loop after reaching the target [ -n "$target" ] && [ "$target" = "$_disk_dev" ] && break # 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 # $2: target disk device (optional) local pwrmode="$1" local target="$2" 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 if [ -z "$target" ]; then echo_debug "disk" "*** set_disk_spindown_timeout($pwrmode).all" else echo_debug "disk" "*** set_disk_spindown_timeout($pwrmode).target: $target" fi # pairwise iteration DISK_DEVICES[1,n], timeoutlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], timeoutlist[min(j,m)] # operation modes: # 1. work on all disks in $DISK_DEVICES # 2. work on $target only -- when called by udev event for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_SPINDOWN_TIMEOUT list handling} if get_disk_dev $dev $target; 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 # quit the loop after reaching the target [ -n "$target" ] && [ "$target" = "$_disk_dev" ] && break # 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 # $1: target disk device (optional) local target="$1" 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 if [ -z "$target" ]; then echo_debug "disk" "*** set_disk_iosched($pwrmode).all" else echo_debug "disk" "*** set_disk_iosched($pwrmode).target: $target" fi # 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 # operation modes: # 1. work on all disks in $DISK_DEVICES # 2. work on $target only -- when called by udev event for dev in $DISK_DEVICES; do local sched schedctrl if get_disk_dev $dev $target; 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 # quit the loop after reaching the target [ -n "$target" ] && [ "$target" = "$_disk_dev" ] && break # 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 "disk" "set_sata_link_power($pm).not_configured" return 0 fi # ALPM denylist host_bl=${SATA_LINKPWR_DENYLIST:-} # copy configured values to args set -- $pwrlist # iterate SATA hosts for host in $ALPMD ; do linkpol=$host/link_power_management_policy if [ -f $linkpol ]; then hostid=${host##*/} if wordinlist "$hostid" "$host_bl"; then # host denylisted --> skip echo_debug "disk" "set_sata_link_power($pm).deny: $host" ctrl_avail="1" else # host not denylisted --> iterate all configured values for pwr in "$@"; do write_sysf "$pwr" $linkpol; rc=$? echo_debug "disk" "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 "disk" "set_sata_link_power($pm).not_available" return 0 } set_ahci_disk_runtime_pm () { # set runtime power management for ahci disks # $1: 0=ac mode, 1=battery mode # $2: target disk device (optional) local target="$2" local control dev 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 "disk" "set_ahci_disk_runtime_pm($1).not_configured" return 0 fi # when timeout is unconfigured we're done here if [ -z "$timeout" ]; then echo_debug "disk" "set_ahci_disk_runtime_pm($1).timeout_not_configured" return 0 fi if [ -z "$target" ]; then echo_debug "disk" "*** set_ahci_disk_runtime_pm($pwrmode).all" else echo_debug "disk" "*** set_ahci_disk_runtime_pm($pwrmode).target: $target" fi # iterate DISK_DEVICES for dev in $DISK_DEVICES; do if get_disk_dev $dev $target; then case "$_disk_runpm" in 0) # runtime pm allowed for disk rc=0 # write timeout first to prevent lockups if ! write_sysf "$timeout" /sys/block/$_disk_dev/device/power/autosuspend_delay_ms; then # writing timeout failed rc=1 fi # proceed with activation if ! write_sysf "$control" /sys/block/$_disk_dev/device/power/control; then # activation failed rc=2 fi echo_debug "disk" "set_ahci_disk_runtime_pm($1).$control: disk=$_disk_dev timeout=$timeout; rc=$rc" ;; 1|2) # runtime pm denied for disk echo_debug "disk" "set_ahci_disk_runtime_pm($1).denied: disk=$_disk_dev; runpm=$_disk_runpm" ;; 3) # disk not runtime pm capable echo_debug "disk" "set_ahci_disk_runtime_pm($1).incapable: disk=$_disk_dev; runpm=$_disk_runpm" ;; esac fi # quit the loop after reaching the target [ -n "$target" ] && [ "$target" = "$_disk_dev" ] && break done return 0 } set_ahci_port_runtime_pm () { # set runtime power management for ahci ports # $1: 0=ac mode, 1=battery mode local control device if [ "$1" = "1" ]; then control=${AHCI_RUNTIME_PM_ON_BAT:-} else control=${AHCI_RUNTIME_PM_ON_AC:-} fi if [ -z "$control" ]; then # do nothing if unconfigured echo_debug "disk" "set_ahci_port_runtime_pm($1).not_configured" return 0 fi # iterate ahci ports for device in $AHCID; do if write_sysf "$control" ${device}/power/control; then echo_debug "disk" "set_ahci_port_runtime_pm($1).$control: port=$device; rc=0" else echo_debug "disk" "set_ahci_port_runtime_pm($1).no-port" fi done return 0 } # --- Filesystem Parameters set_laptopmode () { # set kernel laptop mode # $1: 0=ac mode, 1=battery 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 "disk" "set_laptopmode($1).not_configured" return 0 fi write_sysf "$isec" /proc/sys/vm/laptop_mode echo_debug "disk" "set_laptopmode($1): $isec; rc=$?" return 0 } set_dirty_parms () { # set filesystem buffer params # $1: 0=ac mode, 1=battery mode local age cage df ec 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 "disk" "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 "disk" "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 "disk" "set_dirty_parms($1).write_error: $df 3000; rc=$?" ec=$((ec+1)) fi done echo_debug "disk" "set_dirty_parms($1): $cage; ec=$ec" return 0 } TLP-1.5.0/func.d/20-tlp-func-usb000066400000000000000000000247341416575757700160540ustar00rootroot00000000000000#!/bin/sh # tlp-func-usb - USB Functions # # Copyright (c) 2022 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_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, scanners and denylisted # $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_ALLOWLIST"; then # device is in allowlist -- allowlist always wins control="auto" exc="_dev_allow" elif wordinlist "$usbid" "$USB_DENYLIST"; then # device is in denylist control="on" exc="_dev_deny" 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_deny" break fi done if [ -z "$exc" ]; then # check for bluetooth devices if [ "$USB_EXCLUDE_BTUSB" = "1" ] \ && [ "$dclass" = "e0" ] \ && [ "$(read_sysf $usbdev/bDeviceSubClass)" = "01" ] \ && [ "$(read_sysf $usbdev/bDeviceProtocol)" = "01" ]; then control="on" exc="_btusb_deny" fi fi # bluetooth if [ -z "$exc" ]; then # check for audio devices if [ "$USB_EXCLUDE_AUDIO" = "1" ]; then for subdev in "$usbdev"/*:*; do if [ "$(read_sysf $subdev/bInterfaceClass)" = "01" ]; then control="on" exc="_audio_deny" break fi done fi fi # audio 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="deny" exc="_libsane" fi fi if [ -z "$exc" ]; then # check for phone devices if [ "$USB_EXCLUDE_PHONE" = "1" ]; then if [ "$vendor" = "0fca" ]; then # RIM if [ "$dclass" = "ef" ]; then # RIM / BlackBerry control="on" exc="_phone_deny" 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_deny" break fi fi done fi elif [ "$vendor" = "045e" ] && [ "$dclass" = "ef" ]; then # Windows Phone control="on" exc="_phone_deny" elif [ "$vendor" = "05ac" ] && [ "$(read_sysf $usbdev/product)" = "iPhone" ]; then # iPhone control="on" exc="_phone_deny" 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_deny" break elif [ "$(read_sysf $subdev/bInterfaceClass)" = "ff" ] \ && [ "$(read_sysf $subdev/bInterfaceSubClass)" = "42" ] \ && [ "$(read_sysf $subdev/bInterfaceProtocol)" = "01" ]; then # ADB: Android control="on" exc="_phone_deny" 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_deny" break fi fi done fi # dclass 00 fi # exclude phone fi # phone if [ -z "$exc" ]; then # check for printers if [ "$USB_EXCLUDE_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_deny" break fi done fi fi fi # printer if [ -z "$exc" ]; then # check for wwan devices if [ "$USB_EXCLUDE_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_deny" break fi done fi if [ -z "$exc" ]; then # check for vendors if wordinlist "$vendor" "$USB_WWAN_VENDORS"; then control="on" exc="_wwan_deny" fi fi fi # exclude wwan fi # wwan fi # !device denylist 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 ;; deny) # do not touch denylisted 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 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.5.0/func.d/25-tlp-func-rf000066400000000000000000000114631416575757700156720ustar00rootroot00000000000000#!/bin/sh # tlp-func-rf - Radio Device Checks and PM Functions # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants 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 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 cmd_exists $IW; then $IW dev $iface set power_save $pwr > /dev/null 2>&1 echo_debug "pm" "set_wifi_power_mode($1, $iface).iw: $pwr; rc=$?" else # iw not iwconfig installed echo_debug "pm" "set_wifi_power_mode($1, $iface).no_iw" return 1 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.5.0/func.d/30-tlp-func-rf-sw000066400000000000000000000360161416575757700163160ustar00rootroot00000000000000#!/bin/sh # tlp-func-rf-sw - Radio Switch Functions # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, tlp-func-rf # shellcheck disable=SC2034,SC2086 # ---------------------------------------------------------------------------- # Constants readonly NMCLI=nmcli readonly RFKILL="rfkill" readonly RFKD="/dev/rfkill" readonly ALLDEV="bluetooth nfc 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/nfc/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 # preset retvals _devc="" _devs=254 _rfkdev="1" _devon="1" _devoff="0" case $1 in wwan|bluetooth|nfc) 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" ;; nfc) devstr="nfc " ;; 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 -r 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 nfc 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.5.0/func.d/35-tlp-func-batt000066400000000000000000000147011416575757700162140ustar00rootroot00000000000000#!/bin/sh # tlp-func-batt - Battery Feature Functions # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Needs: tlp-func-base, 34-tlp-func-platform # shellcheck disable=SC2086 # ---------------------------------------------------------------------------- # Constants # shellcheck disable=SC2034 readonly ACPIBATDIR=/sys/class/power_supply # ---------------------------------------------------------------------------- # Functions set_charge_thresholds () { # apply configured thresholds from configuration to all batteries # optional depending on active plugin when specified in $1 # - called from bg tasks tlp init [re]start/auto and tlp start # $1: plugin list (space separated) # rc: 0=ok/ # 1=battery not present/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=read error/ # 5=write error/ # 6=threshold write discarded by kernel or firmware/ # 255=no thresh api local rc # select battery feature driver select_batdrv # shellcheck disable=SC2154 if [ "$_bm_thresh" = "none" ]; then # thresholds not available --> quit echo_debug "bat" "set_charge_thresholds.no_method" return 255 fi # apply thresholds # shellcheck disable=SC2154 if [ -z "$1" ]; then batdrv_apply_configured_thresholds; rc=$? elif wordinlist $_batdrv_plugin "$1"; then batdrv_apply_configured_thresholds; rc=$? fi return $rc } setcharge_battery () { # write charge thresholds # - called from tlp setcharge/fullcharge/recalibrate # $1: start charge threshold, # $2: stop charge threshold # $3: battery # $4: error msg addenum # rc: 0=ok/ # 1=battery not present/ # 2=threshold(s) out of range or non-numeric/ # 3=minimum start stop diff violated/ # 4=read error/ # 5=write error/ # 6=threshold write discarded by kernel or firmware/ # 255=no thresh api local bat rc start_thresh stop_thresh local use_cfg=0 # select battery feature driver select_batdrv # shellcheck disable=SC2154 if [ "$_bm_thresh" = "none" ]; then # thresholds not available --> quit printf "Error: battery charge thresholds%s not available.\n" "${4:-}" 1>&2 echo_debug "bat" "setcharge_battery.no_method" return 255 fi # 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|4) # assume $1,$2 are thresholds, $3 is battery start_thresh=$1 stop_thresh=$2 bat=${3:-DEF} ;; esac # convert bat to uppercase bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") # check bat presence and/or get default(1st) battery if batdrv_select_battery $bat; then # battery present # get configured values if requested if [ $use_cfg -eq 1 ]; then # shellcheck disable=SC2154 eval start_thresh="\$START_CHARGE_THRESH_${_bt_cfg_bat:-$_bat_str}" # shellcheck disable=SC2154 eval stop_thresh="\$STOP_CHARGE_THRESH_${_bt_cfg_bat:-$_bat_str}" fi else # battery not present printf "Error: battery %s not present.\n" "$bat" 1>&2 echo_debug "bat" "setcharge_battery.not_present($bat)" return 1 fi # apply thresholds if [ $use_cfg -eq 1 ]; then # from configuration batdrv_write_thresholds "$start_thresh" "$stop_thresh" 2 "$_bat_str"; rc=$? else # from command line batdrv_write_thresholds "$start_thresh" "$stop_thresh" 2; rc=$? fi return $rc } chargeonce_battery () { # charge battery to upper threshold once # $1: battery # rc: 0=ok/1=battery no present/255=no api local bat rc # select battery feature driver select_batdrv if [ "$_bm_thresh" = "none" ]; then # thresholds not available --> quit printf "Error: battery charge thresholds not available.\n" 1>&2 echo_debug "bat" "chargeonce_battery.no_method" return 255 fi # check params if [ $# -gt 0 ]; then # parameter(s) given, check $1 bat=${1:-DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") else # no parameters given, use default(1st) battery bat=DEF fi # check bat presence and/or get default(1st) battery if ! batdrv_select_battery $bat; then # battery not present printf "Error: battery %s not present.\n" "$bat" 1>&2 echo_debug "bat" "chargeonce_battery.not_present($_bat_str)" return 1 fi # apply temporary start threshold batdrv_chargeonce; rc=$? if [ $rc -eq 255 ]; then printf "Error: chargeonce not available for your hardware.\n" 1>&2 echo_debug "bat" "chargeonce_battery.no_supported" return 255 fi return $rc } echo_discharge_locked () { # print "locked" message echo "Error: another discharge/recalibrate operation is pending." 1>&2 return 0 } discharge_battery () { # discharge battery # $1: battery # rc: 0=ok/1=battery no present/255=no api local bat rc # select battery feature driver select_batdrv # shellcheck disable=SC2154 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 255 fi # check params if [ $# -gt 0 ]; then # parameter(s) given, check $1 bat=${1:-DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") else # no parameters given, use default(1st) battery bat=DEF fi # check bat presence and/or get default(1st) battery if ! batdrv_select_battery $bat; then # battery not present echo "Error: battery $bat not present." 1>&2 echo_debug "bat" "discharge_battery.not_present($bat)" return 1 fi # execute discharge batdrv_discharge; rc=$? return $rc } TLP-1.5.0/func.d/40-tlp-func-bay000066400000000000000000000143031416575757700160270ustar00rootroot00000000000000#!/bin/sh # tlp-func-bay - Bay Functions # # Copyright (c) 2022 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 2> /dev/null)"; 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.5.0/func.d/45-tlp-func-gpu000066400000000000000000000230001416575757700160460ustar00rootroot00000000000000#!/bin/sh # tlp-func-gpu - Intel GPU Functions # # Copyright (c) 2022 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 # shellcheck disable=SC2034 readonly IGPU_RPN_FREQ=gt_RPn_freq_mhz # shellcheck disable=SC2034 readonly IGPU_RP0_FREQ=gt_RP0_freq_mhz # ---------------------------------------------------------------------------- # Functions # --- Intel GPU get_intel_gpu_sysdirs () { # determine Intel GPU sysdirs # $1: drm sysdir, $2: driver # retval: $_intel_gpu_parm: parameter sysdir; # $_intel_gpu_dbg: debug sysdir _intel_gpu_parm=${BASE_MODD}/$2/parameters _intel_gpu_dbg=${BASE_DEBUGD}/${1##${BASE_DRMD}/card} echo_debug "pm" "get_intel_gpu_sysdirs: gpu=$1 driver=$2; parm=$_intel_gpu_parm; dbg=$_intel_gpu_dbg" return 0 } set_intel_gpu_min_max_boost_freq () { # set gpu frequency limits # $1: 0=ac mode, 1=battery mode # rc: 0=ok/1=parameter error local new_min new_max new_boost local old_min old_max old_boost gpu_min gpu_max local driver suffix if [ "$1" = "1" ]; then new_min=${INTEL_GPU_MIN_FREQ_ON_BAT:-} new_max=${INTEL_GPU_MAX_FREQ_ON_BAT:-} new_boost=${INTEL_GPU_BOOST_FREQ_ON_BAT:-} suffix="BAT" else new_min=${INTEL_GPU_MIN_FREQ_ON_AC:-} new_max=${INTEL_GPU_MAX_FREQ_ON_AC:-} new_boost=${INTEL_GPU_BOOST_FREQ_ON_AC:-} suffix="AC" fi if [ -z "$new_min" ] && [ -z "$new_max" ] && [ -z "$new_boost" ]; then echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).not_configured" return 0 fi for gpu in "${BASE_DRMD}"/card?; do driver=$(readlink ${gpu}/device/driver) driver=${driver##*/} case "$driver" in i915*) # Intel GPU found get_intel_gpu_sysdirs "$gpu" "$driver" # shellcheck disable=SC2034 if old_min=$(read_sysf $gpu/$IGPU_MIN_FREQ) \ && old_max=$(read_sysf $gpu/$IGPU_MAX_FREQ) \ && old_boost=$(read_sysf $gpu/$IGPU_BOOST_FREQ) \ && gpu_min=$(read_sysf $gpu/$IGPU_RPN_FREQ) \ && gpu_max=$(read_sysf $gpu/$IGPU_RP0_FREQ); then # frequencies actually readable, check new ones against hardware limits and boundary conditions if ! is_uint "$new_min" 5 || [ $new_min -lt $gpu_min ] || [ $new_min -gt $gpu_max ]; then echo_message "Error in configuration at INTEL_GPU_MIN_FREQ_ON_${suffix}=\"${new_min}\": frequency invalid or out of range (see 'tlp-stat -g')." echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).invalid: gpu=$gpu min=$new_min gpu_min=$gpu_min hw_max=$gpu_max; rc=1" return 1 elif ! is_uint "$new_max" 5 || [ $new_max -lt $gpu_min ] || [ $new_max -gt $gpu_max ]; then echo_message "Error in configuration at INTEL_GPU_MAX_FREQ_ON_${suffix}=\"${new_max}\": frequency invalid or out of range (see 'tlp-stat -g')." echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).invalid: gpu=$gpu min=$new_min gpu_min=$gpu_min gpu_max=$gpu_max; rc=1" return 1 elif ! is_uint "$new_boost" 5 || [ $new_boost -lt $gpu_min ] || [ $new_boost -gt $gpu_max ]; then echo_message "Error in configuration at INTEL_GPU_BOOST_FREQ_ON_${suffix}=\"${new_boost}\": frequency invalid or out of range (see 'tlp-stat -g')." echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).invalid: gpu=$gpu boost=$new_boost gpu_min=$gpu_min gpu_max=$gpu_max; rc=1" return 1 elif [ $new_min -gt $new_max ]; then echo_message "Error in configuration: INTEL_GPU_MIN_FREQ_ON_${suffix} > INTEL_GPU_MAX_FREQ_ON_${suffix}." echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min_gt_max: gpu=$gpu min=$new_min max=$new_max; rc=1" return 1 elif [ $new_max -gt $new_boost ]; then echo_message "Error in configuration: INTEL_GPU_MAX_FREQ_ON_${suffix} > INTEL_GPU_BOOST_FREQ_ON_${suffix}." echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max_gt_boost: gpu=$gpu max=$new_max boost=$new_boost; rc=1" return 1 fi # all parameters valid --> write min, max in proper sequence if [ $new_min -gt $old_max ]; then write_sysf "$new_max" $gpu/$IGPU_MAX_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max: gpu=$gpu freq=$new_max; rc=$?" write_sysf "$new_min" $gpu/$IGPU_MIN_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min: gpu=$gpu freq=$new_min; rc=$?" else write_sysf "$new_min" $gpu/$IGPU_MIN_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min: gpu=$gpu freq=$new_min; rc=$?" write_sysf "$new_max" $gpu/$IGPU_MAX_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max: gpu=$gpu freq=$new_max; rc=$?" fi write_sysf "$new_boost" $gpu/$IGPU_BOOST_FREQ echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).boost: gpu=$gpu freq=$new_boost; rc=$?" else echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).not_available: gpu=$gpu" fi esac done return 0 } # --- AMD Radeon GPU set_amdgpu_profile () { # set amdgpu/radeon power profile # $1: 0=ac mode, 1=battery mode local driver gpu level pwr rc1 rc2 local sdone=0 # 1=gpu present for gpu in "${BASE_DRMD}"/card?; do driver=$(readlink ${gpu}/device/driver) driver=${driver##*/} case "$driver" in amdgpu) if [ -f $gpu/device/power_dpm_force_performance_level ]; then # Use amdgpu dynamic power management method (DPM) if [ "$1" = "1" ]; then level=${RADEON_DPM_PERF_LEVEL_ON_BAT:-} else level=${RADEON_DPM_PERF_LEVEL_ON_AC:-} fi if [ -z "$level" ]; then # do nothing if unconfigured echo_debug "pm" "set_amdgpu_profile($1).amdgpu.not_configured: gpu=$gpu" return 0 else write_sysf "$level" $gpu/device/power_dpm_force_performance_level; rc1=$? echo_debug "pm" "set_amdgpu_profile($1).amdgpu: gpu=$gpu level=${level}: rc=$rc1" fi sdone=1 fi ;; radeon) if [ -f $gpu/device/power_dpm_force_performance_level ] && [ -f $gpu/device/power_dpm_state ]; then # Use radeon dynamic power management method (DPM) if [ "$1" = "1" ]; then level=${RADEON_DPM_PERF_LEVEL_ON_BAT:-} pwr=${RADEON_DPM_STATE_ON_BAT:-} else level=${RADEON_DPM_PERF_LEVEL_ON_AC:-} pwr=${RADEON_DPM_STATE_ON_AC:-} fi if [ -z "$pwr" ] || [ -z "$level" ]; then # do nothing if (partially) unconfigured echo_debug "pm" "set_amdgpu_profile($1).radeon.not_configured: gpu=$gpu" return 0 else write_sysf "$level" $gpu/device/power_dpm_force_performance_level; rc1=$? write_sysf "$pwr" $gpu/device/power_dpm_state; rc2=$? echo_debug "pm" "set_amdgpu_profile($1).radeon: gpu=$gpu perf=${level}: rc=$rc1; state=${pwr}: rc=$rc2" fi sdone=1 elif [ -f $gpu/device/power_method ] && [ -f $gpu/device/power_profile ]; then # Use legacy 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_amdgpu_profile($1).radeon_legacy.not_configured: gpu=$gpu" return 0 else write_sysf "profile" $gpu/device/power_method; rc1=$? write_sysf "$pwr" $gpu/power_profile; rc2=$? echo_debug "pm" "set_amdgpu_profile($1).radeon_legacy: gpu=$gpu method=profile: rc=$rc1; profile=${pwr}: rc=$rc2" fi sdone=1 fi ;; esac done if [ $sdone -eq 0 ]; then echo_debug "pm" "set_amdgpu_profile($1).no_gpu" fi return 0 } TLP-1.5.0/func.d/tlp-func-stat000066400000000000000000000606361416575757700160200ustar00rootroot00000000000000#!/bin/sh # tlp-func-stat - tlp-stat Helper Functions # # Copyright (c) 2022 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,SC2086,SC2154 # ---------------------------------------------------------------------------- # Constants readonly INITCTL=initctl readonly SESTATUS=sestatus readonly SMARTCTL=smartctl readonly RE_AC_QUIRK='^UNDEFINED$' readonly RE_ATA_ERROR='ata[0-9]+: SError: {.*CommWake }' # ---------------------------------------------------------------------------- # Functions # --- Checks 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 # shellcheck disable=SC2059 [ -n "$sysf" ] && printf "$format\n" "$sysf" else # non empty value: strip delimiters from format str format=$(echo $format | sed -r "s/##(.*)##/\1/") # shellcheck disable=SC2059 printf "$format\n" "$sysf" "$val" 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 "(%s) [EPB]\n" "$strval" else printf " [EPB]\n" fi else # sysfile was not readable printf "%-54s = (not available) [EPB]\n" "$1" fi return 0 } printparm_ml () { # indented output of a multiline sysfile # $1: indent str, $2: sysfile, $3: n/a message local ind="$1" local sysf="$2" local namsg="$3" local sline if [ -f $sysf ]; then printf "%s:\n" $sysf # read and output sysfile line by line # shellcheck disable=SC2162 while read -r sline; do printf "%s%s\n" "$ind" "$sline" done < $sysf printf "\n" elif [ -n "$namsg" ]; then printf "%s (%s)\n\n" $sysf $namsg fi } print_sysf () { # formatted output of a sysfile # $1: format; $2: sysfile local val if val=$(read_sysf $2); then # sysfile readable # shellcheck disable=SC2059 printf "$1" "$val" else # sysfile not readable # shellcheck disable=SC2059 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 # shellcheck disable=SC2059 printf "$1" "$(printf "%s" "$val" | sed -r 's/^[[:blank:]]*//;s/[[:blank:]]*$//')" else # sysfile not readable # shellcheck disable=SC2059 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 } get_ahci_host () { # get host associated with a disk # $1: device # retval: $_ahci_host # /sys/block/$device is a softlink to # ../devices/pci0000:00/0000:00:XY.Z/ataN/.../$device # which reveals the associated ahci host: 0000:00:XY.Z/ataN/hostM _ahci_host="$(readlink /sys/block/$1 | sed -r 's/^\.\.\/devices\/pci[0-9:]+\/[0-9a-f:.]+\/ata[0-9]+\/(host[0-9]+).*$/\1/')" if [ -n "$_ahci_host" ]; then echo_debug "disk" "get_ahci_host($1): host=$_ahci_host" return 0 else echo_debug "disk" "get_ahci_host($1).none" return 1 fi } print_nvme_temp () { # print NVMe disk temperature from hwmon API # $1: device # # Reference: # - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=400b6a7b13a3fd71cff087139ce45dd1e5fff444 local sens ts # temp1_input is "Composite" for sens in $(glob_files '/hwmon*/temp1_input' /sys/block/$1/device); do if ts=$(read_sysval $sens); then perl -e 'printf (" Temp = %-2.0f °C\n", '$ts' / 1000.0);' break fi done 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 print_nvme_temp $_disk_dev ;; sata|ata|usb|ieee1394) # ATA/USB/IEEE1394 disk printf "\n%s:\n" /dev/$_disk_dev printf " Type = %s\n" "$(toupper $_disk_type)" [ -n "$_disk_id" ] && printf " Disk ID = %s\n" $_disk_id # 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 if [ "$_disk_type" = "sata" ] || [ "$_disk_type" = "ata" ]; then get_ahci_host $_disk_dev && printf " Host = %s\n" $_ahci_host fi # 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 [ "$_disk_mq" = "1" ]; 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 if [ "$_disk_runpm" != "3" ]; then # disk has runtime pm capability echo case "$_disk_runpm" in 0) printf " Runtime PM:\n";; 1) printf " Runtime PM: locked by kernel\n";; 2) printf " Runtime PM: locked by TLP\n" ;; esac print_sysf " /sys/block/$_disk_dev/device/power/control = %s, " /sys/block/$_disk_dev/device/power/control print_sysf "autosuspend_delay_ms = %s\n" /sys/block/$_disk_dev/device/power/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/^/ /' ;; sata|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, "[%]" }' ;; *) # unknown disk type ;; esac return 0 } get_ahci_disk () { # get disk associated with an alpm or ahci port runtime pm sysfile # $1: sysfile # retval: $_ahci_disk local aport # cut sysfile path down to the ahci port /sys/bus/pci/devices/0000:00:XY.Z/ataN aport="$(echo $1 | sed -r 's/^(\/sys\/bus\/pci\/devices\/[0-9a-f:.]+\/ata[0-9]+).*$/\1/')" # the directory /sys/bus/pci/devices/0000:00:XY.Z/ataN/host*/target*/*/block # lists the actual block device name pointing to /dev/sdX resp. /sys/block/sdX _ahci_disk="$(glob_dirs '/*' ${aport}/host*/target*/*/block 2> /dev/null | head -1)" _ahci_disk="${_ahci_disk##/*/}" if [ -n "$_ahci_disk" ]; then echo_debug "disk" "get_ahci_disk($1): port=$aport ahci_disk=$_ahci_disk" return 0 else echo_debug "disk" "get_ahci_disk($1).none" return 1 fi } printparm_ahci () { # print alpm or ahci port runtime pm sysfile # accompanied by the attached disk device # $1: sysfile local val if val=$(read_sysf $1); then # sysfile exists and is actually readable, output content printf "%-56s = %s " "$1" "$val" get_ahci_disk $1 if [ -n "$_ahci_disk" ]; then printf " -- %s\n" "$_ahci_disk" else printf "\n" fi fi return 0 } # --- Graphics printparm_i915 () { # formatted output of sysfile - i915 kernel module variant # $*: sysfile alternatives local sysf val for sysf in "$@"; do 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 printf "(use per-chip default)\n" else printf "(" if [ "${sysf##/*/}" = "enable_psr" ]; then # enable_psr case $val in 0) printf "disabled" ;; 1) printf "enabled" ;; 2) printf "force link-standby mode" ;; 3) printf "force link-off mode" ;; *) printf "unknown" ;; esac else # other parms if [ $((val & 1)) -ne 0 ]; then printf "enabled" else printf "disabled" fi [ $((val & 2)) -ne 0 ] && printf " + deep" [ $((val & 4)) -ne 0 ] && printf " + deepest" fi printf ")\n" fi # print first match only break fi done return 0 } show_gpu_data () { # show GPU data for all drivers local driver gpu sysout local hdr= for gpu in "${BASE_DRMD}"/card?; do [ -d "$gpu" ] || continue driver=$(readlink ${gpu}/device/driver) driver=${driver##*/} case "$driver" in i915*) # Intel GPU get_intel_gpu_sysdirs "$gpu" "$driver" # power managment data if [ "$hdr" != "i915" ]; then printf "+++ Intel Graphics\n" hdr="i915" fi printf "%-44s = %s\n\n" "$gpu/device/driver" "$driver" printparm_i915 $gpu/power/rc6_enable $_intel_gpu_parm/enable_rc6 $_intel_gpu_parm/i915_enable_rc6 if sysout=$(grep '^FBC ' $_intel_gpu_dbg/i915_fbc_status 2> /dev/null); then printf "%-44s = %s\n" "$_intel_gpu_dbg/i915_fbc_status" "$sysout" else printparm_i915 $_intel_gpu_parm/enable_fbc $_intel_gpu_parm/i915_enable_fbc fi if sysout=$(grep '^PSR mode:' $_intel_gpu_dbg/i915_edp_psr_status 2> /dev/null); then printf "%-44s = %s\n" "$_intel_gpu_dbg/i915_edp_psr_status" "$sysout" else printparm_i915 $_intel_gpu_parm/enable_psr fi printf "\n" # frequency parameters if readable_sysf $gpu/$IGPU_MIN_FREQ; then printparm "%-44s = ##%5d## [MHz]" $gpu/$IGPU_MIN_FREQ printparm "%-44s = ##%5d## [MHz]" $gpu/$IGPU_MAX_FREQ printparm "%-44s = ##%5d## [MHz]" $gpu/$IGPU_BOOST_FREQ printparm "%-44s = ##%5d## [MHz] (GPU min)" $gpu/$IGPU_RPN_FREQ printparm "%-44s = ##%5d## [MHz] (GPU max)" $gpu/$IGPU_RP0_FREQ printf "\n" fi ;; amdgpu) # AMD GPU if [ "$hdr" != "amdgpu" ]; then printf "+++ AMD Radeon Graphics\n" hdr="amdgpu" fi printf "%-65s = %s\n\n" "$gpu/device/driver" "$driver" if [ -f $gpu/device/power_dpm_force_performance_level ]; then printparm "%-65s = ##%s##" $gpu/device/power_dpm_force_performance_level printf "\n" fi ;; radeon) # AMD GPU if [ "$hdr" != "radeon" ]; then printf "+++ AMD Radeon Graphics\n" hdr="radeon" fi printf "%-65s = %s\n\n" "$gpu/device/driver" "$driver" if [ -f $gpu/device/power_dpm_force_performance_level ]; then # AMD hardware printparm "%-65s = ##%s##" $gpu/device/power_dpm_force_performance_level printparm "%-65s = ##%s##" $gpu/device/power_dpm_state printf "\n" elif [ -f $gpu/device/power_method ]; then # legacy ATI hardware printparm "%-65s = ##%s##" $gpu/device/power_method printparm "%-65s = ##%s##" $gpu/device/power_profile printf "\n" fi ;; nouveau|nvidia) # Nvidia GPU if [ "$hdr" != "$driver" ]; then printf "+++ Nvidia Graphics\n" hdr="$driver" fi printf "%-44s = %s\n\n" "$gpu/device/driver" "$driver" ;; *) # Other GPU if [ "$hdr" != "$driver" ]; then printf "+++ Other Graphics\n" hdr="$driver" fi printf "%-44s = %s\n\n" "$gpu/device/driver" "$driver" ;; esac done return 0 } # --- Battery print_methods_per_driver () { # show features provided by a Thinkpad battery plugin # $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 m="" case $bm in _bm_read) [ "$1" = "tpsmapi" ] && m="status" ;; _bm_thresh) m="charge thresholds" ;; _bm_dischg) m="recalibration" ;; esac if [ -n "$m" ]; then # concat method to output [ -n "$mlist" ] && mlist="${mlist}, " mlist="${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 state # map "Unknown" to "Idle" for clarity (and avoid user questions) [ "$val" = "Unknown" ] && val="Idle" printf "%-59s = %s\n" "$sysf" "$val" else # sysfile was not readable printf "%-59s = (not available)\n" "$sysf" fi return 0 } print_battery_cycle_count () { # print battery cycle count, explain special case of 0 # $1: sysfile # $2: cycle count case "$2" in 0) printf "%-59s = %6d (or not supported)\n" "$1" "$2" ;; "") printf "%-59s = (not supported)\n" "$1" ;; *) printf "%-59s = %6d\n" "$1" "$2" ;; esac return 0 } # -- udev diagnostic check_udev_rule_ps () { # check if udev rule for power source changes draws if [ -n "$_psdev" ]; then $UDEVADM test -a change $_psdev 2>&1 | grep -E '^Reading rules file: .*tlp.rules$' if $UDEVADM test -a change $_psdev 2> /dev/null | grep -E '^run: .*tlp auto'; then printf "OK.\n\n" else printf "Fatal Error: TLP's udev rule for power source changes (85-tlp.rules) is not active -- possible package bug.\n\n" fi fi } check_udev_rule_usb () { # check if udev rule for connecting USB device draws local ud ud="$(glob_dirs '/*' /sys/bus/usb/devices 2> /dev/null | grep -v ':' | head -1)" if [ -n "$ud" ]; then $UDEVADM test -a add $ud 2>&1 | grep -E '^Reading rules file: .*tlp.rules$' if $UDEVADM test -a add $ud 2> /dev/null | grep -E '^run: .*tlp-usb-udev usb'; then printf "OK.\n\n" else printf "Fatal Error: TLP's udev rule for connecting USB devices (85-tlp.rules) is not active -- possible package bug.\n\n" fi fi } TLP-1.5.0/man-rdw/000077500000000000000000000000001416575757700135515ustar00rootroot00000000000000TLP-1.5.0/man-rdw/tlp-rdw.8000066400000000000000000000005761416575757700152430ustar00rootroot00000000000000.TH tlp-rdw 8 2021-12-18 "TLP 1.5.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 SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/000077500000000000000000000000001416575757700127575ustar00rootroot00000000000000TLP-1.5.0/man/bluetooth.1000066400000000000000000000006721416575757700150530ustar00rootroot00000000000000.TH bluetooth 1 2021-12-18 "TLP 1.5.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 nfc (1), .BR wifi (1), .BR wwan (1), .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/nfc.1000066400000000000000000000006661416575757700136170ustar00rootroot00000000000000.TH nfc 1 2021-12-18 "TLP 1.5.0" "Power Management" . .SH NAME nfc \- enable/disable internal NFC device . .SH SYNOPSIS .B nfc \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 bluetooth (1), .BR wifi (1), .BR wwan (1), .BR tlp (8). .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/run-on-ac.1000066400000000000000000000004371416575757700146440ustar00rootroot00000000000000.TH run\-on\-ac 1 2020-01-31 "TLP 1.5.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) 2022 Thomas Koch TLP-1.5.0/man/run-on-bat.1000066400000000000000000000004461416575757700150270ustar00rootroot00000000000000.TH run\-on\-bat 1 2020-01-31 "TLP 1.5.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) 2022 Thomas Koch TLP-1.5.0/man/tlp-stat.8000066400000000000000000000051121416575757700146170ustar00rootroot00000000000000.TH tlp\-stat 8 2021-09-21 "TLP 1.5.0" "Power Management" . .SH NAME tlp\-stat \- view power saving status . .SH SYNOPSIS .B tlp\-stat \fB[\fIoptions\fR] [\fB--\fR \fICONFIG_PARAM\fR\fB=\fIvalue\fR "..."] . .SH DESCRIPTION View configuration, system information, kernel power saving settings and battery data. Invocation without options shows all information categories. . .SH OPTIONS . .TP .B \-b, \-\-battery View battery data. Add \fB-v\fR to see battery voltages (if available). . .TP .B \-c, \-\-config View active configuration. . .TP .B \-\-cdiff View the difference between defaults and user configuration. . .TP .B \-d, \-\-disk View disk device information. . .TP .B \-e, \-\-pcie View PCIe device information. Add \fB-v\fR to see device runtime status. . .TP .B \-g, \-\-graphics View graphics card information. . .TP .B \-p, \-\-processor View processor information. For clarity the standard output shows only cpu0. Add \fB-v\fR to see all. . .TP .B \-r, \-\-rfkill View radio device states. . .TP .B \-s, \-\-system View system information. . .TP .B \-t, \-\-temp View temperatures and fan speed. . .TP .B \-u, \-\-usb View USB device information. Add \fB-v\fR to see device runtime status. . .TP .B \-v, \-\-verbose Show more information in battery, PCIe, processor and USB categories. . .PP Diagnostics and debugging: . .TP .B \-P, \-\-pev Monitor power supply udev events. . .TP .B \-\-psup View power supply diagnostics. . .TP .B \-T, \-\-trace View trace output. . .TP .B \-\-udev Check if udev rules for power source changes and connecting USB devices are active. . .TP .B \-w, \-\-warn View warnings about SATA disks. . .TP .B \-- \fR\fICONFIG_PARAM\fR\fB=\fIvalue\fR "..." Add configuration 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 as fallback only when /etc/tlp.conf is non-existent. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/tlp.8000066400000000000000000000112131416575757700136450ustar00rootroot00000000000000.TH tlp 8 2021-12-18 "TLP 1.5.0" "Power Management" . .SH NAME tlp \- apply laptop power saving settings . .SH SYNOPSIS .B tlp \fIcommand\fR \fR[\fIparameters\fR] [\fB--\fR \fICONFIG_PARAM\fR\fB=\fIvalue\fR "..."] . .SH DESCRIPTION \fBtlp\fR applies power saving settings manually and controls battery care 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 or to leave manual mode. . .TP .B bat Apply the battery settings profile and enter manual mode. Manual mode means that changes to the power source will be ignored until the next reboot or \fBtlp start\fR is issued to resume automatic mode. . .TP .B true Same as \fBbat\fR. . .TP .B ac Apply the AC settings profile and enter manual mode. Manual mode means that changes to the power source will be ignored until the next reboot or \fBtlp start\fR is issued to resume automatic mode. . .TP .B false Same as \fBac\fR. . .TP .B usb Enable autosuspend for all USB devices except those excluded by default or in the configuration. . .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. . .PP Note: charge threshold and recalibration commands below work for specific laptop vendors and models only. Check the actual support status and the value ranges with \fBtlp-stat -b\fR. For details on charge thresholds also refer to the link at the bottom. .TP .B setcharge \fR[\fISTART_CHARGE_THRESH STOP_CHARGE_THRESH\fR] [\fBBAT0\fR|\fBBAT1\fR|\fBBAT\fR\fIx\fR|\fBCMB0\fR] Changes the charge thresholds for the battery temporarily. If your hardware supports only a stop threshold, set the start value to 0. Configured charge thresholds will be restored at the next boot or by using \fBtlp setcharge\fR again but without the threshold arguments. . .TP .B fullcharge \fR[\fBBAT0\fR|\fBBAT1\fR|\fBBAT\fR\fIx\fR|\fBCMB0\fR] Charge the battery to full capacity. This is done by applying vendor presets to the charge thresholds temporarily. Configured charge thresholds will be restored at the next boot or by using \fBtlp setcharge\fR without the threshold arguments. . .TP .B chargeonce \fR[\fBBAT0\fR|\fBBAT1\fR] \fR(ThinkPads only) Charge the battery to the stop charge threshold once. This is done by temporarily lifting the start charge threshold. The configured start charge threshold will be restored at the next boot or by using \fBtlp setcharge\fR without the threshold arguments. . .TP .B discharge \fR[\fBBAT0\fR|\fBBAT1\fR] \fR(ThinkPads only) Force complete discharge of the battery. . .TP .B recalibrate \fR[\fBBAT0\fR|\fBBAT1\fR] \fR(ThinkPads only) Battery recalibration: completely discharge the battery and recharge to 100%. The latter is done by temporarily applying vendor presets to the thresholds. Configured thresholds will be restored at the next boot or by using \fBtlp setcharge\fR. . .TP .B diskid Show disk ids for configuration. . .TP .B \-- \fR\fICONFIG_PARAM\fR\fB=\fIvalue\fR "..." Add configuration parameters to override the system configuration for this single program invocation only. They are not kept afterwards. . .SH NOTES For laptops with more than one battery the selection works as follows: .IP BAT0 \- Main or internal battery .IP BAT1 \- Secondary or replaceable battery . .SH EXAMPLES Change thresholds of the main battery to 70 / 90% temporarily: .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 nfc (1), .BR wifi (1), .BR wwan (1). . .PP .mso www.tmac .URL "https://linrunner.de/tlp" "Project homepage: " .PP .URL "https://linrunner.de/tlp/settings/battery.html" \ "Charge thresholds: " . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/tlp.service.8000066400000000000000000000013771416575757700153160ustar00rootroot00000000000000.TH tlp.service 8 2021-12-18 "TLP 1.5.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: save radio states 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 SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/wifi.1000066400000000000000000000006721416575757700140040ustar00rootroot00000000000000.TH wifi 1 2022-01-07 "TLP 1.5.0" "Power Management" . .SH NAME wifi \- enable/disable internal Wi-Fi 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 bluetooth (1), .BR ncf (1), .BR wifi (1), .BR wwan (1), .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/man/wwan.1000066400000000000000000000006661416575757700140250ustar00rootroot00000000000000.TH wwan 1 2022-01-07 "TLP 1.5.0" "Power Management" . .SH NAME wwan \- enable/disable internal WWAN (3G/4G/5G) 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 bluetooth (1), .BR ncf (1), .BR wifi (1), .BR tlp (8). . .SH AUTHOR (c) 2022 Thomas Koch TLP-1.5.0/rename.conf000066400000000000000000000011041416575757700143160ustar00rootroot00000000000000CPU_HWP_ON_AC CPU_ENERGY_PERF_POLICY_ON_AC CPU_HWP_ON_BAT CPU_ENERGY_PERF_POLICY_ON_BAT SATA_LINKPWR_BLACKLIST SATA_LINKPWR_DENYLIST RUNTIME_PM_BLACKLIST RUNTIME_PM_DENYLIST RUNTIME_PM_DRIVER_BLACKLIST RUNTIME_PM_DRIVER_DENYLIST USB_BLACKLIST USB_DENYLIST USB_BLACKLIST_BTUSB USB_EXCLUDE_BTUSB USB_BLACKLIST_PHONE USB_EXCLUDE_PHONE USB_BLACKLIST_PRINTER USB_EXCLUDE_PRINTER USB_BLACKLIST_WWAN USB_EXCLUDE_WWAN USB_WHITELIST USB_ALLOWLIST TLP-1.5.0/tlp-func-base.in000066400000000000000000000710141416575757700151770ustar00rootroot00000000000000#!/bin/sh # tlp - Base Functions # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # shellcheck disable=SC2034,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 SYSTEMCTL=systemctl 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 DMID=/sys/class/dmi/id/ readonly NETD=/sys/class/net # shellcheck disable=SC2034 readonly TPACPID=/sys/devices/platform/thinkpad_acpi 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" readonly TLP_SERVICES="tlp.service" readonly CONFLICTING_SERVICES="power-profiles-daemon.service" readonly RFKILL_SERVICES="systemd-rfkill.service systemd-rfkill.socket" # ---------------------------------------------------------------------------- # Control _nodebug=0 # ---------------------------------------------------------------------------- # Functions # -- Exit do_exit () { # cleanup and exit -- $1: rc # remove temporary runconf [ -z "$_conf_tmp" ] || rm -f -- "$_conf_tmp" exit $1 } # --- Messages 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 } echo_message () { # output message according to TLP_MSG_LEVEL # $1: message # $2: loglevel for syslog (default: warning) # shellcheck disable=SC2154 if [ "$_bgtask" = "1" ]; then # called from background task --> use syslog if [ -n "$1" ]; then case "$TLP_WARN_LEVEL" in 1|3) $LOGGER -p "${2:-warning}" -t "tlp" --id=$$ -- "$1" > /dev/null 2>&1 ;; esac fi else # called from command line task --> use stderr case "$TLP_WARN_LEVEL" in 2|3) # shellcheck disable=SC2059 printf "$1\n" 1>&2 ;; esac fi } # --- Strings tolower () { # print string in lowercase -- $1: string printf "%s" "$1" | tr "[:upper:]" "[:lower:]" } toupper () { # print string in uppercase -- $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 read_sysf () { # read and print contents of a sysfile # return 1 and print default if read fails # $1: sysfile # $2: default # rc: 0=ok/1=error if cat "$1" 2> /dev/null; then return 0 else printf "%s" "$2" return 1 fi } readable_sysf () { # check if sysfile is actually readable # $1: file # rc: 0=readable/1=read error cat "$1" > /dev/null 2>&1 } read_sysval () { # read and print contents of a sysfile # print '0' if file is non-existent, read fails or content 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 } # --- Globbing glob_files () { # @stdout glob_files ( glob_pattern, dir[, dir...] ) # # Nested loop that applies a glob expression to several directories # (or path prefixes) and prints matching file paths (including symlinks) # to stdout. # # NOTE: for x in $(glob_files 'a*' dirpath ); do ...; done # globs twice: # (a) once in the "for file_iter" loop in glob_files() # (b) another time when x gets word expanded in the "for x" loop # crafted filenames (e.g. a file named '*') will break this function, # as such it should be only be used with 'sort-of trustworthy' directories # (sysfs, proc). [ -n "${1-}" ] || return 64 local glob_pattern file_iter local rc=1 glob_pattern="${1}" while shift && [ $# -gt 0 ]; do for file_iter in ${1}${glob_pattern}; do if [ -f "${file_iter}" ] || [ -L "${file_iter}" ]; then printf '%s\n' "${file_iter}" rc=0 fi done done return $rc } glob_dirs () { # @stdout glob_dirs ( glob_pattern, dir[, dir...] ) # # Nested loop that applies a glob expression to several directories # (or path prefixes) and prints matching directory paths to stdout. # # NOTE: globs twice, see glob_files(). [ -n "${1-}" ] || return 64 local glob_pattern dir_iter local rc=1 glob_pattern="${1}" while shift && [ $# -gt 0 ]; do for dir_iter in ${1}${glob_pattern}; do if [ -d "${dir_iter}" ]; then printf '%s\n' "${dir_iter}" rc=0 fi done done return $rc } # --- Checks 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 } 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_services_activation_status () { # issue messages for # - TLP service(s) not enabled # - conflicting services enabled # rc: 0=no messages/messages issued local rc=0 if check_systemd; then cnt=0 for su in $TLP_SERVICES; do if ! $SYSTEMCTL is-enabled $su > /dev/null 2>&1 ; then echo_message "Error: $su is not enabled, power saving will not apply on boot." "err" echo_message ">>> Invoke 'systemctl enable $su' to correct this!" "err" echo_message "" rc=1 fi done for su in $CONFLICTING_SERVICES; do if $SYSTEMCTL is-enabled $su > /dev/null 2>&1 ; then echo_message "Error: conflicting $su is enabled, power saving will not apply on boot." "err" echo_message ">>> Invoke 'systemctl mask $su' to correct this!" "err" echo_message "" rc=1 fi done for su in $RFKILL_SERVICES; do if $SYSTEMCTL is-enabled $su 2> /dev/null | grep -q -v 'masked'; then echo_message "Warning: $su is not masked, radio device switching may not work as configured." echo_message ">>> Invoke 'systemctl mask $su' to correct this." echo_message "" rc=1 fi done fi return $rc } # --- 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 } # -- Battery Plugins select_batdrv () { # source battery feature drivers and # activate the one that matches the hardware # do not execute twice # shellcheck disable=SC2154 [ -z "$_batdrv_selected" ] || return 0 # iterate until a matching driver is found for batdrv in @TLP_BATD@/[0-9][0-9]*[a-z]; do # shellcheck disable=SC1090 . $batdrv || exit 70 # end iteration when a matching driver is found batdrv_init && break done return 0 } # --- 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 argd="" cfgd="" dflag=0 param value # 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 eval "$param='$value'" 2> /dev/null cfgd="$cfgd $param=""$value""" fi fi elif [ "$1" = "--" ]; then # delimiter reached --> begin interpretation dflag=1 else argd="$argd $1" fi shift # next argument done # while arguments echo_debug "arg" "parse_args4config: ${0##/*/}$argd --$cfgd" return 0 } 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 } # --- Power Source get_sys_power_supply () { # determine active power supply # $1: command # rc: 0=ac/1=battery/2=unknown # retval: $_syspwr == rc # $_psdev: 1st power supply found (for udev rule check) # # examine all power supply devices in lexical order, typically this is: # AC, ADPx (AC chargers) -> BATx, CMBx (batteries) -> ucsi* (USB). # names in $RE_PS_IGNORE are ignored. # # the ranking of power source classes for the determination of the active # power supply is as follows: # 1. AC chargers # 2. Batteries # 3. USB # $TLP_PS_IGNORE may be used to ignore one or more power source classes local bs ps_ignore psrc psrc_name local ac0seen= local wait= _psdev="" _syspwr="$X_SIMULATE_PS" if [ -n "$_syspwr" ]; then # simulate power supply echo_debug "ps" "get_sys_power_supply.simulate: syspwr=$_syspwr" return $_syspwr fi ps_ignore=$(toupper "$TLP_PS_IGNORE") 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) # AC detected _psdev="${_psdev:-$psrc}" # if configured, skip device to ignore incorrect AC status if wordinlist "AC" "$ps_ignore"; 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 offine --> end iteration _syspwr=1 echo_debug "ps" "get_sys_power_supply(${psrc_name}).ac_offline: syspwr=$_syspwr" break fi ;; USB) # USB PS detected _psdev="${_psdev:-$psrc}" # if configured, skip device to ignore incorrect AC status if wordinlist "USB" "$ps_ignore"; then echo_debug "ps" "get_sys_power_supply(${psrc_name}).usb_ignored: syspwr=$_syspwr" continue fi # check USB PS status if [ "$(read_sysf $psrc/online)" = "1" ]; then # USB online --> end iteration _syspwr=0 echo_debug "ps" "get_sys_power_supply(${psrc_name}).usb_online: syspwr=$_syspwr" break else # USB PS offline could mean battery, but multiple connectors may exist # --> remember and continue looking ac0seen=1 echo_debug "ps" "get_sys_power_supply(${psrc_name}).remember_usb_offline" fi ;; Battery) # battery detected _psdev="${_psdev:-$psrc}" # if configured, skip device to ignore incorrect battery status if wordinlist "BAT" "$ps_ignore"; then echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_ignored: syspwr=$_syspwr" continue fi # check battery status bs="$(read_sysf $psrc/status)" if [ "$bs" != "Discharging" ] && [ "$1" = "auto" ] && [ -z "$wait" ]; then # when command is 'tlp auto', not "Discharging" might be caused by lagging battery status updates # --> recheck every 0.1 secs for 1.5 secs (or user value in deciseconds) max # use delay loop only once wait="$X_PS_WAIT_DS" is_uint "$wait" 2 || wait=15 echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_not_discharging_recheck: bs=$bs; syspwr=$_syspwr; wait=$wait" while [ $wait -gt 0 ]; do sleep 0.1 wait=$((wait - 1)) bs="$(read_sysf $psrc/status)" [ "$bs" = "Discharging" ] && break done fi 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; wait=$wait" 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; wait=$wait" fi break # --> end iteration ;; *) # assume AC mode for everything else, e.g. "Charging", "Full", "Not charging", "Unknown" # --> continue looking because there may be multiple batteries _syspwr=0 echo_debug "ps" "get_sys_power_supply(${psrc_name}).bat_not_discharging: bs=$bs; syspwr=$_syspwr; wait=$wait" ;; 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_remembered: 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 $(toupper "$TLP_DEFAULT_MODE") in AC) _persist_mode=0; rc=0 ;; BAT) _persist_mode=1; rc=0 ;; esac fi return $rc } get_power_mode () { # get current operation mode # $1: command # 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 $1 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 $(toupper "$TLP_DEFAULT_MODE") in AC) rc=0 ;; 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.5.0/tlp-pcilist000066400000000000000000000051621416575757700143770ustar00rootroot00000000000000#!/usr/bin/perl # tlp-pcilist - list pci devices with runtime pm mode and device class # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # Cmdline options # --verbose: show Runtime PM device status package tlp_pcilist; use strict; use warnings; # --- Modules use Getopt::Long; # --- Global vars my $verbose = 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 } # --- MAIN # parse arguments GetOptions ('verbose' => \$verbose); # Output device list with Runtime PM mode, status 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"; # status file for Runtime PM my $devs = "$devp/power/runtime_status"; # get device class my $class = catsysf ("$devp/class"); # get device driver my $driver = getdriver ("$devp") || "no driver"; if (-f $devc) { # control file exists # get device mode my $pmode = catsysf ("$devc"); if ( $verbose ) { # get device status my $pstatus = catsysf ("$devs"); # output device mode, status and data printf "%s/power/control = %-4s, runtime_status = %-9s (%s, %s, %s)\n", $devp, $pmode, $pstatus, $class, $classdesc, $driver; } else { # output device mode and 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.5.0/tlp-rdw-nm.in000066400000000000000000000127671416575757700145520ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - network manager dispatcher hook: # enable/disable radios on ifup/ifdown # # Copyright (c) 2022 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 # shellcheck disable=SC2034 _bgtask=1 # 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.5.0/tlp-rdw-udev.in000066400000000000000000000056411416575757700150740ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - handle dock/undock events # # Copyright (c) 2022 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.5.0/tlp-rdw.bash_completion000066400000000000000000000004331416575757700166650ustar00rootroot00000000000000# 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.5.0/tlp-rdw.in000066400000000000000000000016471416575757700141350ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - enable/disable RDW # # Copyright (c) 2022 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 # shellcheck disable=SC2034 _bgtask=1 # 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.5.0/tlp-rdw.rules.in000066400000000000000000000036221416575757700152610ustar00rootroot00000000000000# tlp-rdw - udev rules # # Copyright (c) 2022 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 Pro Dock [P/N 40A1] ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1012/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Ultra Dock [P/N 40A2] ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1010/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Pro Dock (USB3 Gigabit LAN interface) [P/N 40X1E] 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 [P/N 40X1A9] 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 [P/N 40A4] 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" [P/N 40AH] ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/306f/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad USB-C Dock Gen 2 [P/N 40AS] ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/a396/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" TLP-1.5.0/tlp-readconfs.in000066400000000000000000000151061416575757700153000ustar00rootroot00000000000000#!/usr/bin/perl # tlp-readconfs - read all of TLP's config files # # Copyright (c) 2022 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 # --cdiff: only show differences to the default # # 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_REN => "@TLP_CONFREN@"; use constant CONF_OLD => "@TLP_CONF@"; # Exit codes use constant EXIT_TLPCONF => 5; use constant EXIT_DEFCONF => 6; # --- Global vars my @config_val = (); # 2-dim array: parameter name, value, source, default-value my %config_idx = (); # hash: parameter name => index into the name-value array my %rename = (); # hash: OLD_PARAMETER => NEW_PARAMETER my $renrex; # compiled regex for renaming parameters my $do_rename = 0; # enable renaming (when $renrex not empty) my $notrace = 0; my $debug = 0; my $cdiff = 0; my $outfile; my $defsrc = basename (CONF_DEF); # --- Subroutines # Format and write debug message # @_: printf arguments including format string sub printf_debug { if ( ! $notrace && $debug ) { open (my $logpipe, "|-", "logger -p debug -t \"tlp\" --id=\$\$ --") || 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]: 0=replace/1=append parameter value # $_[3]: parameter source e.g. filepath + line no. # $_[4]: 0=user config/1=default # return: 0=new name/1=known name sub store_name_value_source { my $name = $_[0]; my $value = $_[1]; my $append = $_[2]; my $source = $_[3]; my $is_def = $_[4]; $debug = 1 if ( $name eq "TLP_DEBUG" && $value =~ /\bcfg\b/ ); if ( defined $config_idx{$name} ) { # existing name if ( $append ) { # append value, source $config_val[$config_idx{$name}][1] .= " $value"; $config_val[$config_idx{$name}][2] .= " & $source"; } else { # replace value, source $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 if ( $is_def ) { #save value as default push(@config_val, [$name, $value, $source, $value]); } else { # save value as user config 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 # $_[1]: 0=no change/1=rename parameters # return: 0=ok/1=file non-existent sub parse_configfile { my $fname = $_[0]; my $do_ren = $_[1]; my $source; my $is_def; if ( $fname eq CONF_DEF ) { $source = $defsrc; $is_def = 1; } else { $source = $fname; $is_def = 0; } open (my $cf, "<", $fname) || return 1; my $ln = 0; while ( my $line = <$cf> ) { chomp $line; $line =~ s/\s+$//; $ln += 1; # select lines with format 'PARAMETER=value' or 'PARAMETER="value"' if ( $line =~ /^(?[A-Z_]+[0-9]*)(?(=|\+=))(?:(?[-0-9a-zA-Z _.:]*)|"(?[-0-9a-zA-Z _.:]*)")\s*$/ ) { my $name = $+{name}; if ( $do_ren ) { # rename PARAMETER $name =~ s/$renrex/$rename{$1}/; } my $value = $+{val_dquoted} // $+{val_bare}; my $append = $+{op} eq "+="; store_name_value_source ($name, $value, $append, $source . " L" . sprintf ("%04d", $ln), $is_def ); } } 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 ( ! $fname ) { $runconf = *STDOUT; } else { open ($runconf, ">", $fname) || return 1; } foreach ( @config_val ) { my ($name, $value, $source, $default) = @$_; if ( $runconf eq *STDOUT ) { # --cdiff: do not show user config lines matching the default if ( ! $cdiff || $value ne $default ) { printf {$runconf} "%s: %s=\"%s\"\n", $source, $name, $value; } } else { printf {$runconf} "%s=\"%s\"\n", $name, $value; } } close ($runconf); return 0 } # Parse parameter renaming rules from file # $_[0]: rules file # return: 0=ok/1=file non-existent sub parse_renfile { my $fname = $_[0]; open (my $rf, "<", $fname) || return 1; # accumulate renaming while ( my $line = <$rf> ) { chomp $line; # select lines with format 'OLD_PARAMETERNEW_PARAMETER' if ( $line =~ /^(?[A-Z_]+[0-9]*)\s+(?[A-Z_]+[0-9]*)\s*$/ ) { my $old_name = $+{old_name}; my $new_name = $+{new_name}; $rename{$old_name} = $new_name; } } close ($rf); if ( keys %rename > 0 ) { # renaming hash not empty --> compile OLD_PARAMETER keys to match regex $renrex = qr/^(@{[join '|', map { quotemeta($_) } keys %rename]})$/; # enable renaming $do_rename = 1; } return 0; } # --- MAIN # parse arguments GetOptions ('outfile=s' => \$outfile, 'notrace' => \$notrace, 'cdiff' => \$cdiff); # read parameter renaming rules parse_renfile (CONF_REN); # 1. read intrinsic defaults (no renaming) parse_configfile (CONF_DEF, 0) == 0 || exit EXIT_DEFCONF; # 2. read customization (with renaming) foreach my $conffile ( grep { -f } glob CONF_DIR . "/*.conf" ) { parse_configfile ($conffile, $do_rename); } # 3. read user settings (with renaming) parse_configfile (CONF_USR, $do_rename) == 0 || parse_configfile (CONF_OLD, $do_rename) == 0 || exit EXIT_TLPCONF; # save result write_runconf ($outfile); exit 0; TLP-1.5.0/tlp-rf.in000066400000000000000000000023041416575757700137370ustar00rootroot00000000000000#!/bin/sh # tlp - switch bluetooth/nfc/wifi/wwan on/off # # Copyright (c) 2022 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|nfc|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.5.0/tlp-run-on.in000066400000000000000000000014271416575757700145530ustar00rootroot00000000000000#!/bin/sh # tlp - run commands depending on power source # # Copyright (c) 2022 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.5.0/tlp-sleep000066400000000000000000000003571416575757700140410ustar00rootroot00000000000000#!/bin/sh # tlp - systemd suspend/resume hook # # Copyright (c) 2022 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.5.0/tlp-sleep.elogind000066400000000000000000000002141416575757700154510ustar00rootroot00000000000000#!/bin/sh case "${1-}" in 'pre') exec tlp suspend ;; 'post') exec tlp resume ;; *) exit 64 ;; esac TLP-1.5.0/tlp-stat.in000066400000000000000000000712641416575757700143160ustar00rootroot00000000000000#!/bin/sh # tlp-stat - display power saving details # # Copyright (c) 2022 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 JOURNALCTL=journalctl readonly ASPM=/sys/module/pcie_aspm/parameters/policy readonly EFID=/sys/firmware/efi readonly NMIWD=/proc/sys/kernel/nmi_watchdog readonly OSRELEASE=/etc/os-release readonly SWITCHEROO=/sys/kernel/debug/vgaswitcheroo/switch 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 # --- 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_udev=0 show_usb=0 show_verbose=0 show_warn=0 # --- Functions 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} ;; "--cdiff") show_all=0 show_cdiff=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 ;; "--udev") show_all=0 show_udev=1 : ${needs_root_priv:=0} ;; "--") # 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 " | --cdiff | | --pev |" echo " -P | --psup | -T | --trace |" echo " | --udev ]" 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 fi get_sys_power_supply echo "--- TLP $TLPVER --------------------------------------------" echo # --- show configuration if [ "$show_conf" = "1" ] || [ "$show_all" = "1" ]; then echo "+++ Configured Settings:" $READCONFS --notrace echo fi # show_conf if [ "$show_cdiff" = "1" ]; then echo "+++ Configured Settings (only differences to defaults):" $READCONFS --notrace --cdiff 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 printf "OS Release = " if ! sed -rn 's/PRETTY_NAME="(.*)"/\1/p' $OSRELEASE 2> /dev/null; then echo "unknown" fi 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/faq/operation.html#faq-ac-quirk" fi echo # -- check systemd services check_services_activation_status # -- 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 cnt=0 epp=0 cpu2nd="" cpulast="" for cn in $(glob_dirs '/cpu[0-9]*' $CPUD | sed 's/.*\/cpu//' | sort -n); do cpuf="${CPUD}/cpu${cn}/cpufreq" if [ -f $cpuf/scaling_driver ]; then cpu="cpu${cn}" # show only cpu0, unless verbose mode is enabled if [ $cnt -eq 0 ] || [ "$show_verbose" = "1" ] ; 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 if [ $cnt -eq 1 ]; then # remember 2nd cpu core cpu2nd=$cpu else # remember last cpu core cpulast=$cpu fi cnt=$((cnt + 1)) fi done if [ "$show_verbose" = "0" ] && [ $cnt -gt 1 ]; then printf "%s/%s..%s: omitted for clarity, use -v to show all\n\n" $CPUD $cpu2nd $cpulast fi if check_intel_pstate; then # show Intel P-state info printparm "%-54s = ##%s##" $INTEL_PSTATED/status printparm "%-54s = ##%3d## [%%]" $CPU_MIN_PERF_PCT printparm "%-54s = ##%3d## [%%]" $CPU_MAX_PERF_PCT printparm "%-54s = ##%3d##" $CPU_TURBO_PSTATE printparm "%-54s = ##%3d##" $CPU_HWP_DYN_BOOST 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="${CPUD}/sched_${pool}_power_savings" printparm "%-54s = ##%d##" $sdev _ done echo # --- show Intel energy and performance bias hint (EPB) if check_intel_pstate; then 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 cn in $(glob_dirs '/cpu[0-9]*' $CPUD | sed 's/.*\/cpu//' | sort -n); do cpuf="${CPUD}/cpu${cn}/power/energy_perf_bias" if [ -f $cpuf ]; then # show only cpu0, unless verbose mode is enabled if [ $cnt -eq 0 ] || [ "$show_verbose" = "1" ]; then printparm_epb $cpuf fi 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 fi # --- show workqueue power efficient status printparm "%-54s = ##%s##" $WQPE # --- show nmi watchdog printparm "%-54s = ##%d##" $NMIWD echo # --- show platform profile printf "+++ Platform Profile\n" printparm "%-54s = ##%s##" $FWACPID/platform_profile printparm "%-54s = ##%s##" $FWACPID/platform_profile_choices [ -d $TPACPID ] && printparm "%-54s = ##%s##" $TPACPID/dytc_lapmode printf "\n" fi # show_proc if [ "$show_temp" = "1" ] || [ "$show_all" = "1" ]; then # --- show temperatures echo "+++ Temperatures" if [ -f $IBMTHERMAL ] && [ "$X_IGNORE_IBM_TEMPFAN" != "1" ]; then # use ThinkPad-specific sysfile echo "$IBMTHERMAL = $(read_sysf $IBMTHERMAL | cut -f2 ) [°C]" else # use coretemp sensors cmax=0 # find max value of all packages (just in case there are several) # temp1_input is "Package" for sens in $(glob_files '/temp1_input' $CORETEMP_DIRS); do ctemp=$(read_sysval $sens) [ $ctemp -gt $cmax ] && cmax=$ctemp done if [ $cmax -gt 0 ]; then perl -e 'printf ("CPU temp = %5d [°C]\n", '$cmax' / 1000.0);' fi fi # --- show fan speed if [ -f $IBMFAN ] && [ "$X_IGNORE_IBM_TEMPFAN" != "1" ]; 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 disk info echo "+++ Disks" # 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 for dev in $disklist; do # iterate all devices show_disk_data $dev done fi echo # --- show sata alpm mode if stat -t ${ALPMD}/link_power_management_policy > /dev/null 2>&1; then echo "+++ AHCI Link Power Management (ALPM) :: SATA Links" for i in ${ALPMD} ; do printparm_ahci $i/link_power_management_policy done echo fi # --- show ahci runtime pm if stat -t ${AHCID}/power > /dev/null 2>&1; then echo "+++ AHCI Port Runtime Power Management :: SATA/ATA Ports" for dev in ${AHCID}/power ; do printparm_ahci $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 hybrid graphics switch (nouveau, radeon only) if [ -f $SWITCHEROO ]; then echo "+++ Hybrid Graphics Switch" printparm_ml " " $SWITCHEROO fi # --- show GPU date for all drivers show_gpu_data fi # show_graf if [ "$show_rfkill" = "1" ] || [ "$show_all" = "1" ]; then echo "+++ Wireless" # --- show rfkill state for i in bluetooth nfc 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 if cmd_exists $IW; then wifipm=$($IW dev $iface get power_save 2> /dev/null | \ grep 'Power save' | \ sed -r 's/.*Power save: (on|off).*/\1/') else wifipm="" 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 "+++ PCIe Runtime Power Management" echo "Enable devices = ${RUNTIME_PM_ENABLE:-(disabled)}" echo "Disable devices = ${RUNTIME_PM_DISABLE:-(disabled)}" echo "Device denylist = ${RUNTIME_PM_DENYLIST:=(disabled)}" echo "Driver denylist = ${RUNTIME_PM_DRIVER_DENYLIST:-(disabled)}" echo if cmd_exists $TLPPCI; then if [ $show_verbose -eq 1 ]; then $TLPPCI --verbose else $TLPPCI fi 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 allowlist = ${USB_ALLOWLIST:=(not configured)}" echo "Device denylist = ${USB_DENYLIST:=(not configured)}" if [ "${USB_EXCLUDE_AUDIO:-0}" = "1" ]; then echo "Exclude audio = enabled" else echo "Exclude audio = disabled" fi if [ "${USB_EXCLUDE_BTUSB:-0}" = "1" ]; then echo "Exclude bluetooth = enabled" else echo "Exclude bluetooth = disabled" fi if [ "${USB_EXCLUDE_PHONE:-0}" = "1" ]; then echo "Exclude phones = enabled" else echo "Exclude phones = disabled" fi if [ "${USB_EXCLUDE_PRINTER:-0}" = "1" ]; then echo "Exclude printers = enabled" else echo "Exclude printers = disabled" fi if [ "${USB_EXCLUDE_WWAN:-1}" = "1" ]; then echo "Exclude WWAN = enabled" else echo "Exclude WWAN = disabled" fi echo if cmd_exists $TLPUSB; then if [ $show_verbose -eq 1 ]; then $TLPUSB --verbose else $TLPUSB fi else echo "Error: missing subcommand $TLPUSB." 1>&2 fi echo fi # show_usb if [ "$show_bat" = "1" ] || [ "$show_all" = "1" ]; then select_batdrv batdrv_show_battery_data $show_verbose 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 # battery plugin specific recommendations reout="$(batdrv_recommendations)" if [ "$show_all" = "1" ]; then # add other recommendations cmd_exists ethtool || reout="${reout}Install ethtool to disable Wake-on-LAN\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 printf "+++ Power supply diagnostic\n" for ps in /sys/class/power_supply/*; do # shellcheck disable=SC2063 printparm "%s: ##%s##" $ps/type printparm "%s: ##%s##" $ps/usb_type printparm "%s: ##%s##" $ps/online printparm "%s: ##%s##" $ps/voltage_max printparm "%s: ##%s##" $ps/voltage_min printparm "%s: ##%s##" $ps/voltage_now printparm "%s: ##%s##" $ps/present printparm "%s: ##%s##" $ps/charge_control_start_threshold printparm "%s: ##%s##" $ps/charge_control_end_threshold printparm "%s: ##%s##" $ps/status printparm "%s: ##%s##" $ps/device/path done printf "\n+++ udev diagnostic\n" check_udev_rule_ps fi # show_psup if [ "$show_udev" = "1" ]; then # --- show udev diagnostic printf "+++ udev diagnostic\n" check_udev_rule_ps check_udev_rule_usb 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.5.0/tlp-usb-udev.in000066400000000000000000000036641416575757700150740ustar00rootroot00000000000000#!/bin/sh # tlp - handle added usb devices # # Copyright (c) 2022 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@/15-tlp-func-disk @TLP_FLIB@/20-tlp-func-usb; do # shellcheck disable=SC1090 . $lib || exit 70 done # --- MAIN # shellcheck disable=SC2034 _bgtask=1 # read configuration: quit on error, trace allowed read_config 1 0 # quit if TLP disabled # shellcheck disable=SC2015 check_tlp_enabled || do_exit 0 if [ "$X_USB_ENV_TRACE" = "1" ]; then echo_debug "usb" "tlp_usb_udev.env = $(printenv)" fi case "$1" in usb) # usb devices in general [ "$USB_AUTOSUSPEND" = "1" ] || do_exit 0 # quit if usb autosuspend disabled # 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 # handle device usb_suspend_device "/sys$2" "udev" ;; disk) # (s)ata disks attached via usb get_power_mode; pwrmode=$? dev="${2##*/block/}" set_ahci_disk_runtime_pm $pwrmode $dev set_disk_apm_level $pwrmode $dev set_disk_spindown_timeout $pwrmode $dev set_disk_iosched $dev ;; esac do_exit 0 TLP-1.5.0/tlp-usblist000066400000000000000000000066741416575757700144260ustar00rootroot00000000000000#!/usr/bin/perl # tlp-usblist - list usb device info with autosuspend attributes # # Copyright (c) 2022 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"; # --- Modules use Getopt::Long; # --- Global vars my %usbdevices; my $verbose = 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 # parse arguments GetOptions ('verbose' => \$verbose); # 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 device mode and timeout if ( length (my $ptimeout = catsysf ("$udev/power/autosuspend_delay_ms")) && length (my $pmode = catsysf ("$udev/power/control")) ) { if ( $verbose ) { # get device status my $pstatus = catsysf ("$udev/power/runtime_status"); # format: device mode, timeout, status $usbv = sprintf ("control = %-5s autosuspend_delay_ms = %4d, runtime_status = %-9s", $pmode . ",", $ptimeout, $pstatus); } else { # format: device mode, timeout $usbv = sprintf ("control = %-5s autosuspend_delay_ms = %4d", $pmode . ",", $ptimeout); } } # store 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 =~ s/\s+$//; $desc ||= ""; print "Bus $bus Device $dev ID $usbid $usbdevices{$usbk}[1] -- $desc (" . usbdriverlist ($usbdevices{$usbk}[0]) . ")\n"; } exit 0; TLP-1.5.0/tlp.bash_completion000066400000000000000000000036421416575757700161000ustar00rootroot00000000000000# 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 nfc complete -F _tlp_rf wwan complete -F _tlp_rf wifi _tlp_stat() { local cur prev words cword opts _init_completion || return opts="--battery --cdiff --config --disk --graphics --pcie --pev --processor --psup --rfkill --system --temp --trace --udev --usb --verbose --warn" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_stat tlp-stat TLP-1.5.0/tlp.conf000066400000000000000000000432341416575757700136600ustar00rootroot00000000000000# ------------------------------------------------------------------------------ # /etc/tlp.conf - TLP user configuration (version 1.4) # See full explanation: https://linrunner.de/tlp/settings # # 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 # - However you may append values to a parameter already defined as intrinsic # default or in a previously read file: use PARAMETER+="add values" # - IMPORTANT: all parameters here are disabled; remove the leading '#' if you # like to enable a feature without default or have a value different from the # default # - 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 # Control how warnings about invalid settings are issued: # 0=disabled, # 1=background tasks (boot, resume, change of power source) report to syslog, # 2=shell commands report to the terminal (stderr), # 3=combination of 1 and 2 # Default: 3 #TLP_WARN_LEVEL=3 # 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 classes to ignore when determining operation mode: AC, USB, BAT. # Separate multiple classes with spaces. # 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 # Select a CPU frequency scaling governor. # Intel processor with intel_pstate driver: # performance, powersave(*). # Intel processor with intel_cpufreq driver (aka intel_pstate passive mode): # conservative, ondemand, userspace, powersave, performance, schedutil(*). # Intel and other processor brands with acpi-cpufreq driver: # conservative, ondemand(*), userspace, powersave, performance, schedutil(*). # Use tlp-stat -p to show the active driver and available governors. # Important: # Governors marked (*) above are power efficient for *almost all* workloads # and therefore kernel and most distributions have chosen them as defaults. # You should have done your research about advantages/disadvantages *before* # changing the governor. # 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. # Notes: # - Min/max frequencies must always be specified for both AC *and* BAT # - Not recommended for use with the intel_pstate scaling driver, use # CPU_MIN/MAX_PERF_ON_AC/BAT below instead # 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: # - HWP.EPP: requires kernel 4.10, intel_pstate scaling driver and Intel Core i # 6th gen. or newer CPU # - EPB: requires kernel 5.2 or module msr and x86_energy_perf_policy from # linux-tools, intel_pstate or intel_cpufreq scaling driver and Intel Core i # 2nd gen. or newer CPU # - 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 intel_pstate or intel_cpufreq driver and Intel Core i 2nd gen. or # newer CPU. # 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" (Intel) or "turbo core" (AMD) feature: # 0=disable, 1=allow. # Note: a value of 1 does *not* activate boosting, it just allows it. # Default: #CPU_BOOST_ON_AC=1 #CPU_BOOST_ON_BAT=0 # Set the Intel CPU HWP dynamic boost feature: # 0=disable, 1=enable. # Requires intel_pstate scaling driver in 'active' mode and Intel Core i # 6th gen. or newer CPU. # Default: #CPU_HWP_DYN_BOOST_ON_AC=1 #CPU_HWP_DYN_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 # Select platform profile: # performance, balanced, low-power. # Controls system operating characteristics around power/performance levels, # thermal and fan speed. Values are given in order of increasing power saving. # Note: check the output of tlp-stat -p to determine availability on your # hardware and additional profiles such as: balanced-performance, quiet, cool. # Default: #PLATFORM_PROFILE_ON_AC=performance #PLATFORM_PROFILE_ON_BAT=low-power # Define disk devices on which the following DISK/AHCI_RUNTIME parameters act. # Separate multiple devices with spaces. # Devices can be specified by disk ID also (lookup with: tlp diskid). # 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" # Exclude disk classes from advanced power management (APM): # sata, ata, usb, ieee1394. # Separate multiple classes with spaces. # CAUTION: USB and IEEE1394 disks may fail to mount or data may get corrupted # with APM enabled. Be careful and make sure you have backups of all affected # media before removing 'usb' or 'ieee1394' from the denylist! # Default: "usb ieee1394" #DISK_APM_CLASS_DENYLIST="usb ieee1394" # 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 SATA disks: # min_power, med_power_with_dipm(*), medium_power, max_performance. # (*) Kernel 4.15 (or newer) 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 SATA links from AHCI link power management (ALPM). # SATA links are specified by their host. Refer to the output of # tlp-stat -d to determine the host; the format is "hostX". # Separate multiple hosts with spaces. # Default: #SATA_LINKPWR_DENYLIST="host1" # Runtime Power Management for NVMe, SATA, ATA and USB disks # as well as SATA ports: # on=disable, auto=enable. # Note: SATA controllers are PCIe bus devices and handled by RUNTIME_PM further # down. # Default: on (AC), auto (BAT) #AHCI_RUNTIME_PM_ON_AC=on #AHCI_RUNTIME_PM_ON_BAT=auto # 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 # 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" # 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 # AMD GPU power management. # Performance level (DPM): auto, low, high; auto is recommended. # Note: requires amdgpu or radeon driver. # Default: auto #RADEON_DPM_PERF_LEVEL_ON_AC=auto #RADEON_DPM_PERF_LEVEL_ON_BAT=auto # Dynamic power management method (DPM): balanced, battery, performance. # Note: radeon driver only. # Default: #RADEON_DPM_STATE_ON_AC=performance #RADEON_DPM_STATE_ON_BAT=battery # Graphics clock speed (profile method): low, mid, high, auto, default; # auto = mid on BAT, high on AC. # Note: radeon driver on legacy ATI hardware only (where DPM is not available). # Default: default #RADEON_POWER_PROFILE_ON_AC=default #RADEON_POWER_PROFILE_ON_BAT=default # Wi-Fi power saving mode: on=enable, off=disable. # 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. # Note: 1 is recommended for Linux desktop environments with PulseAudio, # systems without PulseAudio may require 10. # Default: 1 #SOUND_POWER_SAVE_ON_AC=1 #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 # PCIe Active State Power Management (ASPM): # default(*), performance, powersave, powersupersave. # (*) keeps BIOS ASPM defaults (recommended) # Default: #PCIE_ASPM_ON_AC=default #PCIE_ASPM_ON_BAT=default # Runtime Power Management for PCIe bus devices: on=disable, auto=enable. # Default: on (AC), auto (BAT) #RUNTIME_PM_ON_AC=on #RUNTIME_PM_ON_BAT=auto # Exclude listed PCIe device adresses from Runtime PM. # Note: this preserves the kernel driver default, to force a certain state # use RUNTIME_PM_ENABLE/DISABLE instead. # Separate multiple addresses with spaces. # Use lspci to get the adresses (1st column). # Default: #RUNTIME_PM_DENYLIST="11:22.3 44:55.6" # Exclude PCIe devices assigned to the listed drivers from Runtime PM. # Note: this preserves the kernel driver default, to force a certain state # use RUNTIME_PM_ENABLE/DISABLE instead. # Separate multiple drivers with spaces. # Default: "mei_me nouveau radeon", use "" to disable completely. #RUNTIME_PM_DRIVER_DENYLIST="mei_me nouveau radeon" # Permanently enable/disable Runtime PM for listed PCIe device addresses # (independent of the power source). This has priority over all preceding # Runtime PM settings. Separate multiple addresses with spaces. # Use lspci to get the adresses (1st column). # Default: #RUNTIME_PM_ENABLE="11:22.3" #RUNTIME_PM_DISABLE="44:55.6" # 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) and libsane-supported scanners are excluded # automatically. # Default: #USB_DENYLIST="1111:2222 3333:4444" # Exclude audio devices from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 1 #USB_EXCLUDE_AUDIO=1 # Exclude bluetooth devices from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 0 #USB_EXCLUDE_BTUSB=0 # Exclude phone devices from USB autosuspend: # 0=do not exclude, 1=exclude (enable charging). # Default: 0 #USB_EXCLUDE_PHONE=0 # Exclude printers from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 1 #USB_EXCLUDE_PRINTER=1 # Exclude WWAN devices from USB autosuspend: # 0=do not exclude, 1=exclude. # Default: 0 #USB_EXCLUDE_WWAN=0 # Allow USB autosuspend for listed devices even if already denylisted or # excluded above (separate with spaces). Use lsusb to get the ids. # Default: 0 #USB_ALLOWLIST="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, nfc, wifi, wwan. # Separate multiple devices with spaces. # Default: #DEVICES_TO_DISABLE_ON_STARTUP="bluetooth nfc wifi wwan" # Radio devices to enable on startup: bluetooth, nfc, wifi, wwan. # Separate multiple devices with spaces. # Default: #DEVICES_TO_ENABLE_ON_STARTUP="wifi" # Radio devices to disable on shutdown: bluetooth, nfc, wifi, wwan. # Note: use as a workaround for devices that are blocking shutdown. # Default: #DEVICES_TO_DISABLE_ON_SHUTDOWN="bluetooth nfc wifi wwan" # Radio devices to enable on shutdown: bluetooth, nfc, wifi, wwan. # (to prevent other operating systems from missing radios). # Default: #DEVICES_TO_ENABLE_ON_SHUTDOWN="wwan" # Radio devices to enable on AC: bluetooth, nfc, wifi, wwan. # Default: #DEVICES_TO_ENABLE_ON_AC="bluetooth nfc wifi wwan" # Radio devices to disable on battery: bluetooth, nfc, wifi, wwan. # Default: #DEVICES_TO_DISABLE_ON_BAT="bluetooth nfc wifi wwan" # Radio devices to disable on battery when not in use (not connected): # bluetooth, nfc, wifi, wwan. # Default: #DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth nfc wifi wwan" # Battery Care -- Charge thresholds # Charging starts when the charge level is below the START_CHARGE_THRESH value # when the charger is connected. It stops when the STOP_CHARGE_THRESH value is # reached. # Required hardware: Lenovo ThinkPads and select other laptop brands are driven # via specific plugins, the actual support status is shown by tlp-stat -b. # For more explanations and vendor specific details refer to # https://linrunner.de/tlp/settings/battery.html # Notes: # - ThinkPads may require external kernel module(s), refer to the output of # tlp-stat -b # - Vendor specific parameter value ranges are shown by tlp-stat -b # - If your hardware supports a start *and* a stop threshold, you must # specify both, otherwise TLP will refuse to apply the single threshold # - If your hardware supports only a stop threshold, set the start value to 0 # BAT0: Primary / Main / Internal battery (values in %) # Note: also use for batteries BATC, BATT and CMB0 # Default: #START_CHARGE_THRESH_BAT0=75 #STOP_CHARGE_THRESH_BAT0=80 # BAT1: Secondary / Ultrabay / Slice / Replaceable battery (values in %) # Note: primary on some laptops # 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 # Control battery care 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.5.0/tlp.in000066400000000000000000000345771416575757700133530ustar00rootroot00000000000000#!/bin/sh # tlp - adjust power settings # # Copyright (c) 2022 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_platform_profile $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_intel_cpu_hwp_dyn_boost $1 set_sched_powersave $1 set_nmi_watchdog set_ahci_port_runtime_pm $1 set_runtime_pm $1 set_ahci_disk_runtime_pm $1 set_sata_link_power $1 set_disk_apm_level $1 set_disk_spindown_timeout $1 set_disk_iosched set_pcie_aspm $1 set_intel_gpu_min_max_boost_freq $1 set_amdgpu_profile $1 set_wifi_power_mode $1 disable_wake_on_lan set_sound_power_mode $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 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 # get current power state get_power_mode "$_cmd"; pwrmode=$? get_manual_mode # 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 # process command 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 ... # shellcheck disable=SC2034 _bgtask=1 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 # DISABLED: apply ac settings if [ "$X_TLP_SHUTDOWN_ACMODE" = "1" ]; then echo -n "Applying power save settings..." apply_common_settings 0 poweroff_drivebay $pwrmode 0 echo "done." fi # 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 # shellcheck disable=SC2034 _bgtask=1 check_services_activation_status 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_services_activation_status 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_services_activation_status 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_services_activation_status 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 save_device_states "bluetooth wwan" suspend_drivebay $pwrmode # DISABLED: apply ac settings if [ "$X_TLP_SUSPEND_ACMODE" = "1" ]; then if lock_tlp; then apply_common_settings 0 save_runconf unlock_tlp fi 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 set_charge_thresholds "asus huawei" 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 DEF DEF $_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=$? 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 if setcharge_battery DEF DEF "$_carg1" "/discharge/recalibrate"; then 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." else echo "Battery recalibration aborted." fi fi else echo_discharge_locked fi else exitcode=2 fi ;; diskid) # show disk id's show_disk_ids ;; noop) # Debug: no operation check_root select_batdrv batdrv_select_battery "DEF" save_runconf echo "Debug: no operation performed." ;; esac do_exit $exitcode TLP-1.5.0/tlp.init000066400000000000000000000014651416575757700136760ustar00rootroot00000000000000#!/bin/sh # tlp - system startup/shutdown # # Copyright (c) 2022 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.5.0/tlp.rules.in000066400000000000000000000012611416575757700144640ustar00rootroot00000000000000# tlp - udev rules # # Copyright (c) 2022 Thomas Koch and others. # This software is licensed under the GPL v2 or later. # handle change of power source ac/bat, ignore input device batteries ACTION=="change", SUBSYSTEM=="power_supply", KERNEL!="hidpp_battery*", 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 usb %p" # handle added usb disk devices (exclude partitions via ENV{DEVTYPE}=="disk") ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", ENV{ID_BUS}=="usb", RUN+="@TLP_ULIB@/tlp-usb-udev disk %p" TLP-1.5.0/tlp.service.in000066400000000000000000000007731416575757700150010ustar00rootroot00000000000000# tlp - systemd startup/shutdown service # # Copyright (c) 2022 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 ExecReload=@TLP_SBIN@/tlp start ExecStop=@TLP_SBIN@/tlp init stop [Install] WantedBy=multi-user.target TLP-1.5.0/tlp.upstart.in000066400000000000000000000006601416575757700150360ustar00rootroot00000000000000# tlp - system startup/shutdown # # Copyright (c) 2022 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.5.0/tpacpi-bat000066400000000000000000000337351416575757700141660ustar00rootroot00000000000000#!/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' => '\_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); TLP-1.5.0/unit-tests/000077500000000000000000000000001416575757700143235ustar00rootroot00000000000000TLP-1.5.0/unit-tests/charge-thresholds_other000077500000000000000000000657341416575757700210770ustar00rootroot00000000000000#!/usr/bin/clitest # Test charge thresholds for non-ThinkPads (simulated) # Requirements: # * Hardware: non-legacy ThinkPad # $ # +++ ASUS +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- initialize $ sudo tlp start -- START_CHARGE_THRESH_BAT0="35" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="101" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="86" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if STOP_CHARGE_THRESH_BAT0="86" works as expected. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=asus NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="97" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="86" Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if STOP_CHARGE_THRESH_BAT0="86" works as expected. Setting temporary charge threshold for BAT0: stop = 86 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="80" Setting temporary charge threshold for BAT0: stop = 80 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="80" Setting temporary charge threshold for BAT0: stop = 80 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=asus NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 60 100 -- X_BAT_PLUGIN_SIMULATE=asus Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge 0 0 -- X_BAT_PLUGIN_SIMULATE=asus Error: charge stop threshold (0) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 0 101 -- X_BAT_PLUGIN_SIMULATE=asus Error: charge stop threshold (101) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge XYZZY 0 -- X_BAT_PLUGIN_SIMULATE=asus Error: charge stop threshold (0) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 0 XYZZY -- X_BAT_PLUGIN_SIMULATE=asus Error: charge stop threshold (XYZZY) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 97 100 -- X_BAT_PLUGIN_SIMULATE=asus Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge 95 66 -- X_BAT_PLUGIN_SIMULATE=asus Notice: some ASUS laptops silently ignore charge thresholds other than 40, 60 or 80. Please check if 66 works as expected. Setting temporary charge threshold for BAT0: stop = 66 $ sudo tlp setcharge 95 60 -- X_BAT_PLUGIN_SIMULATE=asus Setting temporary charge threshold for BAT0: stop = 60 $ sudo tlp setcharge 95 60 -- X_BAT_PLUGIN_SIMULATE=asus X_THRESH_SIMULATE_START="35" X_THRESH_SIMULATE_STOP="100" Setting temporary charge threshold for BAT0: stop = 60 $ sudo tlp setcharge 95 60 -- X_BAT_PLUGIN_SIMULATE=asus Setting temporary charge threshold for BAT0: stop = 60 $ sudo tlp setcharge DEF DEF -- X_BAT_PLUGIN_SIMULATE=asus Setting temporary charge threshold for BAT0: stop = 100 $ sudo tlp setcharge BAT1 -- X_BAT_PLUGIN_SIMULATE=asus Error: battery BAT1 not present. $ sudo tlp setcharge 0 3 BAT1 -- X_BAT_PLUGIN_SIMULATE=asus Error: battery BAT1 not present. $ sudo tlp setcharge XYZZY ABCDE BAT1 -- X_BAT_PLUGIN_SIMULATE=asus Error: battery BAT1 not present. $ # $ # --- Reset $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=asus | grep "charge_control_end_threshold" /sys/class/power_supply/BAT0/charge_control_end_threshold = 100 [%] $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=asus X_THRESH_SIMULATE_READERR=1 | grep "charge_control_end_threshold" /sys/class/power_supply/BAT0/charge_control_end_threshold = (not available) [%] $ # $ # +++ Huawei ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="59" X_THRESH_SIMULATE_STOP="99" Error: writing charge thresholds failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Error: writing charge thresholds failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Error: writing charge thresholds failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="100" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Error in configuration at START_CHARGE_THRESH_BAT0="100": not specified, invalid or out of range (0..99). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="1" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="99" STOP_CHARGE_THRESH_BAT0="98" Error in configuration: START_CHARGE_THRESH_BAT0 > STOP_CHARGE_THRESH_BAT0. Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="95" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Error: writing charge thresholds failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_START="0" X_THRESH_SIMULATE_STOP="100" Error: writing charge thresholds failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=huawei NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 60, stop = 100 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="99" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 99, stop = 100 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="102" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at START_CHARGE_THRESH_BAT0="102": not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="1" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="99" STOP_CHARGE_THRESH_BAT0="98" Error in configuration: START_CHARGE_THRESH_BAT0 > STOP_CHARGE_THRESH_BAT0. Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="95" X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 95, stop = 95 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="95" X_THRESH_SIMULATE_START="95" X_THRESH_SIMULATE_STOP="95" Setting temporary charge thresholds: start = 95, stop = 95 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_START="0" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 0, stop = 100 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=huawei NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 60 100 -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 60, stop = 100 (Error: write failed) $ sudo tlp setcharge 99 100 -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 99, stop = 100 (Error: write failed) $ sudo tlp setcharge 0 0 -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Error: charge stop threshold (0) is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 102 101 -- X_BAT_PLUGIN_SIMULATE=huawei Error: charge start threshold (102) is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 1 101 -- X_BAT_PLUGIN_SIMULATE=huawei Error: charge stop threshold (101) is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge XYZZY 0 -- X_BAT_PLUGIN_SIMULATE=huawei Error: charge start threshold (XYZZY) is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 1 XYZZY -- X_BAT_PLUGIN_SIMULATE=huawei Error: charge stop threshold (XYZZY) is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 99 98 -- X_BAT_PLUGIN_SIMULATE=huawei Error: start threshold > stop threshold. Aborted. $ sudo tlp setcharge 95 95 -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds: start = 95, stop = 95 (Error: write failed) $ sudo tlp setcharge 95 95 -- X_BAT_PLUGIN_SIMULATE=huawei Setting temporary charge thresholds: start = 95, stop = 95 (Error: write failed) $ sudo tlp setcharge 95 95 -- -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="95" X_THRESH_SIMULATE_STOP="95" Setting temporary charge thresholds: start = 95, stop = 95 (Error: write failed) $ sudo tlp setcharge BAT1 -- X_BAT_PLUGIN_SIMULATE=huawei Error: battery BAT1 not present. $ sudo tlp setcharge 0 3 BAT1 -- X_BAT_PLUGIN_SIMULATE=huawei Error: battery BAT1 not present. $ sudo tlp setcharge XYZZY ABCDE BAT1 -- X_BAT_PLUGIN_SIMULATE=huawei Error: battery BAT1 not present. $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="0" X_THRESH_SIMULATE_STOP="0" | grep "charge_control_thresholds" /sys/devices/platform/huawei-wmi/charge_control_thresholds = 0 0 [%] (disabled) $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="0" X_THRESH_SIMULATE_STOP="100" | grep "charge_control_thresholds" /sys/devices/platform/huawei-wmi/charge_control_thresholds = 0 100 [%] (disabled) $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=huawei X_THRESH_SIMULATE_START="75" X_THRESH_SIMULATE_STOP="80" | grep "charge_control_thresholds" /sys/devices/platform/huawei-wmi/charge_control_thresholds = 75 80 [%] $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=huawei | grep "charge_control_thresholds" /sys/devices/platform/huawei-wmi/charge_control_thresholds = (not available) $ # $ # +++ Lenovo ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="24": conservation mode not specified or invalid (must be 0 or 1). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="1" X_THRESH_SIMULATE_STOP="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing conservation mode failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_STOP="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lenovo NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" Error in configuration at STOP_CHARGE_THRESH_BAT0="24": conservation mode not specified or invalid (must be 0 or 1). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=lenovo START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="1" X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: conservation mode = 1 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=lenovo NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 42 24 -- X_BAT_PLUGIN_SIMULATE=lenovo Error: conservation mode (24) not specified or invalid (must be 0 or 1). Aborted. $ sudo tlp setcharge 42 1 -- X_BAT_PLUGIN_SIMULATE=lenovo X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: conservation mode = 1 (Error: write failed) $ sudo tlp setcharge 42 1 -- X_BAT_PLUGIN_SIMULATE=lenovo X_THRESH_SIMULATE_STOP="1" Setting temporary charge threshold for all batteries: conservation mode = 1 (no change) $ sudo tlp setcharge DEF DEF -- X_BAT_PLUGIN_SIMULATE=lenovo X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: conservation mode = 0 (no change) $ sudo tlp setcharge 42 24 BAT1 -- X_BAT_PLUGIN_SIMULATE=lenovo Error: battery BAT1 not present. $ sudo tlp setcharge 2 -- X_BAT_PLUGIN_SIMULATE=lenovo Error: battery 2 not present. $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=lenovo | grep "conservation_mode" /sys/bus/platform/drivers/ideapad_acpi/VPC2004:00/conservation_mode = (not available) $ # $ # +++ LG ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="24": care limit not specified or invalid (must be 80 or 100). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="80" X_THRESH_SIMULATE_STOP="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing care limit failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_STOP="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=lg NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" Error in configuration at STOP_CHARGE_THRESH_BAT0="24": care limit not specified or invalid (must be 80 or 100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=lg START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: could not read current care limit. Aborted. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 42 24 -- X_BAT_PLUGIN_SIMULATE=lg Error: care limit (24) not specified or invalid (must be 80 or 100). Aborted. $ sudo tlp setcharge 42 100 -- X_BAT_PLUGIN_SIMULATE=lg X_THRESH_SIMULATE_STOP="80" Setting temporary charge threshold for all batteries: care limit = 100 (Error: write failed) $ sudo tlp setcharge 42 80 -- X_BAT_PLUGIN_SIMULATE=lg X_THRESH_SIMULATE_STOP="80" Setting temporary charge threshold for all batteries: care limit = 80 (no change) $ sudo tlp setcharge DEF DEF -- X_BAT_PLUGIN_SIMULATE=lg X_THRESH_SIMULATE_STOP="100" Setting temporary charge threshold for all batteries: care limit = 100 (no change) $ sudo tlp setcharge 42 80 BAT1 -- X_BAT_PLUGIN_SIMULATE=lg Error: battery BAT1 not present. $ sudo tlp setcharge 2 -- X_BAT_PLUGIN_SIMULATE=lg Error: battery 2 not present. $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=lg | grep "battery_care_limit" /sys/devices/platform/lg-laptop/battery_care_limit = (not available) $ # $ # +++ Samsung ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="24": life extender not specified or invalid (must be 0 or 1). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="1" X_THRESH_SIMULATE_STOP="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing life extender failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_STOP="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=samsung NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" Error in configuration at STOP_CHARGE_THRESH_BAT0="24": life extender not specified or invalid (must be 0 or 1). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=samsung START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="1" X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: life extender = 1 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=samsung NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 42 24 -- X_BAT_PLUGIN_SIMULATE=samsung Error: life extender (24) not specified or invalid (must be 0 or 1). Aborted. $ sudo tlp setcharge 42 1 -- X_BAT_PLUGIN_SIMULATE=samsung X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: life extender = 1 (Error: write failed) $ sudo tlp setcharge 42 1 -- X_BAT_PLUGIN_SIMULATE=samsung X_THRESH_SIMULATE_STOP="1" Setting temporary charge threshold for all batteries: life extender = 1 (no change) $ sudo tlp setcharge DEF DEF -- X_BAT_PLUGIN_SIMULATE=samsung X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: life extender = 0 (no change) $ sudo tlp setcharge 42 24 BAT1 -- X_BAT_PLUGIN_SIMULATE=samsung Error: battery BAT1 not present. $ sudo tlp setcharge 2 -- X_BAT_PLUGIN_SIMULATE=samsung Error: battery 2 not present. $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=samsung | grep "battery_life_extender" /sys/devices/platform/samsung/battery_life_extender = (not available) $ # $ # +++ Sony ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="24": care limiter not specified or invalid (must be 50, 80 or 100). Skipped. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="50" X_THRESH_SIMULATE_STOP="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing care limiter failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="80" X_THRESH_SIMULATE_STOP="50" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing care limiter failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_STOP="80" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing care limiter failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" X_THRESH_SIMULATE_STOP="50" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error: writing care limiter failed. TLP started in AC mode (auto). $ sudo tlp start -- X_BAT_PLUGIN_SIMULATE=sony NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="24" Error in configuration at STOP_CHARGE_THRESH_BAT0="24": care limiter not specified or invalid (must be 50, 80 or 100). Aborted. $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="50" X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: care limiter = 50 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="80" X_THRESH_SIMULATE_STOP="50" Setting temporary charge threshold for all batteries: care limiter = 80 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=sony START_CHARGE_THRESH_BAT0="42" STOP_CHARGE_THRESH_BAT0="100" X_THRESH_SIMULATE_STOP="80" Setting temporary charge threshold for all batteries: care limiter = 100 (Error: write failed) $ sudo tlp setcharge -- X_BAT_PLUGIN_SIMULATE=sony NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 42 24 -- X_BAT_PLUGIN_SIMULATE=sony Error: care limiter (24) not specified or invalid (must be 50, 80 or 100). Aborted. $ sudo tlp setcharge 42 50 -- X_BAT_PLUGIN_SIMULATE=sony X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: care limiter = 50 (Error: write failed) $ sudo tlp setcharge 42 80 -- X_BAT_PLUGIN_SIMULATE=sony X_THRESH_SIMULATE_STOP="50" Setting temporary charge threshold for all batteries: care limiter = 80 (Error: write failed) $ sudo tlp setcharge 42 100 -- X_BAT_PLUGIN_SIMULATE=sony X_THRESH_SIMULATE_STOP="80" Setting temporary charge threshold for all batteries: care limiter = 100 (Error: write failed) $ sudo tlp setcharge 42 80 -- X_BAT_PLUGIN_SIMULATE=sony X_THRESH_SIMULATE_STOP="80" Setting temporary charge threshold for all batteries: care limiter = 80 (no change) $ sudo tlp setcharge DEF DEF -- X_BAT_PLUGIN_SIMULATE=sony X_THRESH_SIMULATE_STOP="0" Setting temporary charge threshold for all batteries: care limiter = 100 (no change) $ sudo tlp setcharge 42 24 BAT1 -- X_BAT_PLUGIN_SIMULATE=sony Error: battery BAT1 not present. $ sudo tlp setcharge 2 -- X_BAT_PLUGIN_SIMULATE=sony Error: battery 2 not present. $ # $ # --- tlp-stat $ sudo tlp-stat -b -- X_BAT_PLUGIN_SIMULATE=sony | grep "battery_care_limiter" /sys/devices/platform/sony-laptop/battery_care_limiter = (not available) [%] TLP-1.5.0/unit-tests/charge-thresholds_thinkpad000077500000000000000000000175141416575757700215510ustar00rootroot00000000000000#!/usr/bin/clitest # Test charge thresholds for ThinkPads # Requirements: # * Hardware: non-legacy ThinkPad # * Batteries: BAT0 only # * Power source AC # $ # +++ ThinkPad +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="100" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at START_CHARGE_THRESH_BAT0="100": not specified, invalid or out of range (0..99). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="101" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="97" STOP_CHARGE_THRESH_BAT0="97" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration: START_CHARGE_THRESH_BAT0 >= STOP_CHARGE_THRESH_BAT0. Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="96" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- NATACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="100" STOP_CHARGE_THRESH_BAT0="100" Error in configuration at START_CHARGE_THRESH_BAT0="100": not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="97" STOP_CHARGE_THRESH_BAT0="97" Error in configuration: START_CHARGE_THRESH_BAT0 >= STOP_CHARGE_THRESH_BAT0. Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="96" Setting temporary charge thresholds for BAT0: start = 95 stop = 96 $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="96" Setting temporary charge thresholds for BAT0: start = 95 (no change) stop = 96 (no change) $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Setting temporary charge thresholds for BAT0: start = 96 stop = 100 $ sudo tlp setcharge -- NATACPI_ENABLE=0 TPACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 60 100 Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ sudo tlp setcharge 100 100 Error: charge start threshold (100) for BAT0 is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 0 0 Error: charge stop threshold (0) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 0 101 Error: charge stop threshold (101) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge XYZZY 0 Error: charge start threshold (XYZZY) for BAT0 is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 0 XYZZY Error: charge stop threshold (XYZZY) for BAT0 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 97 97 Error: start threshold >= stop threshold for BAT0. Aborted. $ sudo tlp setcharge 95 96 Setting temporary charge thresholds for BAT0: start = 95 stop = 96 $ sudo tlp setcharge 95 96 -- X_THRESH_SIMULATE_READERR="1" Error: could not read current charge threshold(s) for BAT0. Aborted. $ sudo tlp setcharge 95 96 -- X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds for BAT0: start = 95 stop = 96 $ sudo tlp setcharge 95 96 Setting temporary charge thresholds for BAT0: start = 95 (no change) stop = 96 (no change) $ sudo tlp setcharge DEF DEF Setting temporary charge thresholds for BAT0: start = 96 stop = 100 $ sudo tlp setcharge BAT1 Error: battery BAT1 not present. $ sudo tlp setcharge 0 3 BAT1 Error: battery BAT1 not present. $ sudo tlp setcharge XYZZY ABCDE BAT1 Error: battery BAT1 not present. $ # $ # --- Reset $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ # $ # --- tlp-stat $ # steps require a kernel >= 5.17 -- with 'charge_behaviour' $ sudo tlp-stat -b | grep -E 'charge_(control|behaviour)' /sys/class/power_supply/BAT0/charge_control_start_threshold = 60 [%] /sys/class/power_supply/BAT0/charge_control_end_threshold = 100 [%] /sys/class/power_supply/BAT0/charge_behaviour = [auto] inhibit-charge force-discharge $ sudo tlp-stat -b -- X_THRESH_SIMULATE_READERR=1 | grep -E 'charge_(control|behaviour)' /sys/class/power_supply/BAT0/charge_control_start_threshold = (not available) [%] /sys/class/power_supply/BAT0/charge_control_end_threshold = (not available) [%] /sys/class/power_supply/BAT0/charge_behaviour = [auto] inhibit-charge force-discharge $ # $ # --- Feature Detection Edge Cases and Kernel Module Recommendations $ sudo ./kmod-helper acpi_call restore $ sudo ./kmod-helper acpi_call enable $ sudo tlp-stat -b -- NATACPI_ENABLE=0 | head -8 | tail -5 Plugin: thinkpad Supported features: charge thresholds, recalibration Driver usage: * natacpi (thinkpad_acpi) = inactive (disabled by configuration) * tpacpi-bat (acpi_call) = active (charge thresholds, recalibration) $ sudo ./kmod-helper acpi_call disable $ sudo tlp-stat -b -- NATACPI_ENABLE=0 | head -8 | tail -5 Plugin: thinkpad Supported features: none available Driver usage: * natacpi (thinkpad_acpi) = inactive (disabled by configuration) * tpacpi-bat (acpi_call) = inactive (kernel module 'acpi_call' load error) $ sudo ./kmod-helper acpi_call enable $ sudo ./kmod-helper acpi_call remove $ sudo tlp-stat -b -- NATACPI_ENABLE=0 | head -8 | tail -5 Plugin: thinkpad Supported features: none available Driver usage: * natacpi (thinkpad_acpi) = inactive (disabled by configuration) * tpacpi-bat (acpi_call) = inactive (kernel module 'acpi_call' not installed) $ # next step requires a kernel < 5.17 -- without 'charge_behaviour' $ sudo tlp-stat -b | grep -A1 '+++ Recommendations' +++ Recommendations * Install acpi_call kernel module for ThinkPad battery recalibration $ sudo ./kmod-helper acpi_call restore $ sudo tlp-stat -b -- NATACPI_ENABLE=0 TPACPI_ENABLE=0 | head -8 | tail -5 Plugin: thinkpad Supported features: none available Driver usage: * natacpi (thinkpad_acpi) = inactive (disabled by configuration) * tpacpi-bat (acpi_call) = inactive (disabled by configuration) TLP-1.5.0/unit-tests/charge-thresholds_thinkpad-BAT1000077500000000000000000000163571416575757700222420ustar00rootroot00000000000000#!/usr/bin/clitest # Test charge thresholds for ThinkPads # Requirements: # * Hardware: non-legacy ThinkPad # * Batteries: BAT1 + BAT1 # * Power source AC # $ # +++ ThinkPad +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1="61" STOP_CHARGE_THRESH_BAT1="100" TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="102" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1="100" STOP_CHARGE_THRESH_BAT1="100" Error in configuration at START_CHARGE_THRESH_BAT0="102": not specified, invalid or out of range (0..99). Battery skipped. Error in configuration at START_CHARGE_THRESH_BAT1="100": not specified, invalid or out of range (0..99). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="0" START_CHARGE_THRESH_BAT1="0" STOP_CHARGE_THRESH_BAT1="0" Error in configuration at STOP_CHARGE_THRESH_BAT0="0": not specified, invalid or out of range (1..100). Battery skipped. Error in configuration at STOP_CHARGE_THRESH_BAT1="0": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="0" STOP_CHARGE_THRESH_BAT0="103" START_CHARGE_THRESH_BAT1="0" STOP_CHARGE_THRESH_BAT1="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="103": not specified, invalid or out of range (1..100). Battery skipped. Error in configuration at STOP_CHARGE_THRESH_BAT1="101": not specified, invalid or out of range (1..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="88" STOP_CHARGE_THRESH_BAT0="88" START_CHARGE_THRESH_BAT1="97" STOP_CHARGE_THRESH_BAT1="97" Error in configuration: START_CHARGE_THRESH_BAT0 >= STOP_CHARGE_THRESH_BAT0. Battery skipped. Error in configuration: START_CHARGE_THRESH_BAT1 >= STOP_CHARGE_THRESH_BAT1. Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="85" STOP_CHARGE_THRESH_BAT0="86" START_CHARGE_THRESH_BAT1="95" STOP_CHARGE_THRESH_BAT1="96" TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" START_CHARGE_THRESH_BAT1="DEF" STOP_CHARGE_THRESH_BAT1="DEF" TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="60" STOP_CHARGE_THRESH_BAT1="100" Setting temporary charge thresholds for BAT1: start = 60 stop = 100 (no change) $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="100" STOP_CHARGE_THRESH_BAT1="100" Error in configuration at START_CHARGE_THRESH_BAT1="100": not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="0" STOP_CHARGE_THRESH_BAT1="0" Error in configuration at STOP_CHARGE_THRESH_BAT1="0": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="0" STOP_CHARGE_THRESH_BAT1="101" Error in configuration at STOP_CHARGE_THRESH_BAT1="101": not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="97" STOP_CHARGE_THRESH_BAT1="97" Error in configuration: START_CHARGE_THRESH_BAT1 >= STOP_CHARGE_THRESH_BAT1. Aborted. $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="95" STOP_CHARGE_THRESH_BAT1="96" Setting temporary charge thresholds for BAT1: start = 95 stop = 96 $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="95" STOP_CHARGE_THRESH_BAT1="96" Setting temporary charge thresholds for BAT1: start = 95 (no change) stop = 96 (no change) $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="DEF" STOP_CHARGE_THRESH_BAT1="DEF" Setting temporary charge thresholds for BAT1: start = 96 stop = 100 $ sudo tlp setcharge BAT1 -- NATACPI_ENABLE=0 TPACPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 60 100 BAT1 Setting temporary charge thresholds for BAT1: start = 60 stop = 100 (no change) $ sudo tlp setcharge 100 100 BAT1 Error: charge start threshold (100) for BAT1 is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 0 0 BAT1 Error: charge stop threshold (0) for BAT1 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 0 101 BAT1 Error: charge stop threshold (101) for BAT1 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge XYZZY 0 BAT1 Error: charge start threshold (XYZZY) for BAT1 is not specified, invalid or out of range (0..99). Aborted. $ sudo tlp setcharge 0 XYZZY BAT1 Error: charge stop threshold (XYZZY) for BAT1 is not specified, invalid or out of range (1..100). Aborted. $ sudo tlp setcharge 97 97 BAT1 Error: start threshold >= stop threshold for BAT1. Aborted. $ sudo tlp setcharge 95 96 BAT1 Setting temporary charge thresholds for BAT1: start = 95 stop = 96 $ sudo tlp setcharge 95 96 BAT1 -- X_THRESH_SIMULATE_READERR="1" Error: could not read current charge threshold(s) for BAT1. Aborted. $ sudo tlp setcharge 95 96 BAT1 -- X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds for BAT1: start = 95 stop = 96 $ sudo tlp setcharge 95 96 BAT1 Setting temporary charge thresholds for BAT1: start = 95 (no change) stop = 96 (no change) $ sudo tlp setcharge DEF DEF BAT1 Setting temporary charge thresholds for BAT1: start = 96 stop = 100 $ # $ # --- Reset $ sudo tlp setcharge BAT0 -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ sudo tlp setcharge BAT1 -- START_CHARGE_THRESH_BAT1="61" STOP_CHARGE_THRESH_BAT1="100" Setting temporary charge thresholds for BAT1: start = 61 stop = 100 (no change) $ # $ # --- tlp-stat $ # steps require a kernel >= 5.17 -- with 'charge_behaviour' $ sudo tlp-stat -b | grep -E 'charge_(control|behaviour)' /sys/class/power_supply/BAT0/charge_control_start_threshold = 60 [%] /sys/class/power_supply/BAT0/charge_control_end_threshold = 100 [%] /sys/class/power_supply/BAT0/charge_behaviour = [auto] inhibit-charge force-discharge /sys/class/power_supply/BAT1/charge_control_start_threshold = 61 [%] /sys/class/power_supply/BAT1/charge_control_end_threshold = 100 [%] /sys/class/power_supply/BAT1/charge_behaviour = [auto] inhibit-charge force-discharge $ sudo tlp-stat -b -- X_THRESH_SIMULATE_READERR=1 | grep -E 'charge_(control|behaviour)' /sys/class/power_supply/BAT0/charge_control_start_threshold = (not available) [%] /sys/class/power_supply/BAT0/charge_control_end_threshold = (not available) [%] /sys/class/power_supply/BAT0/charge_behaviour = [auto] inhibit-charge force-discharge /sys/class/power_supply/BAT1/charge_control_start_threshold = (not available) [%] /sys/class/power_supply/BAT1/charge_control_end_threshold = (not available) [%] /sys/class/power_supply/BAT1/charge_behaviour = [auto] inhibit-charge force-discharge TLP-1.5.0/unit-tests/charge-thresholds_thinkpad-legacy000077500000000000000000000167031416575757700230120ustar00rootroot00000000000000#!/usr/bin/clitest # Test charge thresholds for Legacy ThinkPads # Requirements: # * Hardware: Legacy ThinkPad (<= X201/T410) # $ # +++ Legacy ThinkPad +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ $ # $ # --- tlp start $ sudo tlp start -- START_CHARGE_THRESH_BAT0= STOP_CHARGE_THRESH_BAT0= START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="100" STOP_CHARGE_THRESH_BAT0="100" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at START_CHARGE_THRESH_BAT0="100": not specified, invalid or out of range (2..96). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="2" STOP_CHARGE_THRESH_BAT0="2" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="2": not specified, invalid or out of range (6..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="2" STOP_CHARGE_THRESH_BAT0="101" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (6..100). Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="96" STOP_CHARGE_THRESH_BAT0="99" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= Error in configuration: START_CHARGE_THRESH_BAT0 > STOP_CHARGE_THRESH_BAT0 - 4. Battery skipped. TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="99" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ sudo tlp start -- START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" START_CHARGE_THRESH_BAT1= STOP_CHARGE_THRESH_BAT1= TLP started in AC mode (auto). $ # $ # --- tlp setcharge w/o arguments $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="100" STOP_CHARGE_THRESH_BAT0="100" Error in configuration at START_CHARGE_THRESH_BAT0="100": not specified, invalid or out of range (2..96). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="2" STOP_CHARGE_THRESH_BAT0="2" Error in configuration at STOP_CHARGE_THRESH_BAT0="2": not specified, invalid or out of range (6..100). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="2" STOP_CHARGE_THRESH_BAT0="101" Error in configuration at STOP_CHARGE_THRESH_BAT0="101": not specified, invalid or out of range (6..100). Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="96" STOP_CHARGE_THRESH_BAT0="99" Error in configuration: START_CHARGE_THRESH_BAT0 > STOP_CHARGE_THRESH_BAT0 - 4. Aborted. $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="99" Setting temporary charge thresholds for BAT0: start = 95 stop = 99 $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="95" STOP_CHARGE_THRESH_BAT0="99" Setting temporary charge thresholds for BAT0: start = 95 (no change) stop = 99 (no change) $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Setting temporary charge thresholds for BAT0: start = 96 stop = 100 $ sudo tlp setcharge -- TPSMAPI_ENABLE=0 START_CHARGE_THRESH_BAT0="DEF" STOP_CHARGE_THRESH_BAT0="DEF" Error: battery charge thresholds not available. $ # $ # --- tlp setcharge w/ arguments $ sudo tlp setcharge 60 100 Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ sudo tlp setcharge 100 100 Error: charge start threshold (100) for BAT0 is not specified, invalid or out of range (2..96). Aborted. $ sudo tlp setcharge 2 2 Error: charge stop threshold (2) for BAT0 is not specified, invalid or out of range (6..100). Aborted. $ sudo tlp setcharge 2 101 Error: charge stop threshold (101) for BAT0 is not specified, invalid or out of range (6..100). Aborted. $ sudo tlp setcharge XYZZY 0 Error: charge start threshold (XYZZY) for BAT0 is not specified, invalid or out of range (2..96). Aborted. $ sudo tlp setcharge 2 XYZZY Error: charge stop threshold (XYZZY) for BAT0 is not specified, invalid or out of range (6..100). Aborted. $ sudo tlp setcharge 96 99 Error: start threshold > stop threshold - 4 for BAT0. Aborted. $ sudo tlp setcharge 95 99 Setting temporary charge thresholds for BAT0: start = 95 stop = 99 $ sudo tlp setcharge 95 99 -- X_THRESH_SIMULATE_READERR="1" Error: could not read current charge threshold(s) for BAT0. Aborted. $ sudo tlp setcharge 95 99 -- X_THRESH_SIMULATE_START="60" X_THRESH_SIMULATE_STOP="100" Setting temporary charge thresholds for BAT0: start = 95 stop = 99 $ sudo tlp setcharge 95 99 Setting temporary charge thresholds for BAT0: start = 95 (no change) stop = 99 (no change) $ sudo tlp setcharge DEF DEF Setting temporary charge thresholds for BAT0: start = 96 stop = 100 $ sudo tlp setcharge BAT1 Error: battery BAT1 not present. $ sudo tlp setcharge 0 3 BAT1 Error: battery BAT1 not present. $ sudo tlp setcharge XYZZY ABCDE BAT1 Error: battery BAT1 not present. $ # $ # --- Reset $ sudo tlp setcharge -- START_CHARGE_THRESH_BAT0="60" STOP_CHARGE_THRESH_BAT0="100" Setting temporary charge thresholds for BAT0: start = 60 stop = 100 (no change) $ # $ # --- tlp-stat $ sudo tlp-stat -b | grep -E 'charge_thresh|discharge' /sys/devices/platform/smapi/BAT0/start_charge_thresh = 60 [%] /sys/devices/platform/smapi/BAT0/stop_charge_thresh = 100 [%] /sys/devices/platform/smapi/BAT0/force_discharge = 0 $ sudo tlp-stat -b -- X_THRESH_SIMULATE_READERR=1 | grep -E 'charge_thresh|discharge' /sys/devices/platform/smapi/BAT0/start_charge_thresh = (not available) [%] /sys/devices/platform/smapi/BAT0/stop_charge_thresh = (not available) [%] /sys/devices/platform/smapi/BAT0/force_discharge = 0 $ # $ # --- Feature Detection Edge Cases and Kernel Module Recommendations $ sudo ./kmod-helper tp_smapi restore $ sudo ./kmod-helper tp_smapi enable $ sudo tlp-stat -b | head -7 | tail -4 Plugin: thinkpad-legacy Supported features: charge thresholds, recalibration Driver usage: * tp-smapi (tp_smapi) = active (status, charge thresholds, recalibration) $ sudo tlp-stat -b -- NATACPI_ENABLE=0 | head -7 | tail -4 Plugin: thinkpad-legacy Supported features: charge thresholds, recalibration Driver usage: * tp-smapi (tp_smapi) = active (status, charge thresholds, recalibration) $ sudo tlp-stat -b -- NATACPI_ENABLE=0 TPACPI_ENABLE=0 | head -7 | tail -4 Plugin: thinkpad-legacy Supported features: charge thresholds, recalibration Driver usage: * tp-smapi (tp_smapi) = active (status, charge thresholds, recalibration) $ sudo ./kmod-helper tp_smapi disable $ sudo tlp-stat -b | head -7 | tail -4 Plugin: thinkpad-legacy Supported features: none available Driver usage: * tp-smapi (tp_smapi) = inactive (kernel module 'tp_smapi' load error) $ sudo ./kmod-helper tp_smapi enable $ sudo ./kmod-helper tp_smapi remove $ sudo tlp-stat -b | head -7 | tail -4 Plugin: thinkpad-legacy Supported features: none available Driver usage: * tp-smapi (tp_smapi) = inactive (kernel module 'tp_smapi' not installed) $ sudo tlp-stat -b | grep -A2 '+++ Recommendations' +++ Recommendations * Install tp-smapi kernel modules for ThinkPad battery thresholds and recalibration $ sudo ./kmod-helper tp_smapi restore TLP-1.5.0/unit-tests/intel-gpu-freqs000077500000000000000000000203321416575757700172730ustar00rootroot00000000000000#!/usr/bin/clitest # Test Intel GPU frequency limits # Requirements: # * Intel GPU w/ i915 driver # * Hardware freq range 700..1100 (or wider) # $ # --- valid freqs $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT=700 INTEL_GPU_MAX_FREQ_ON_BAT=850 INTEL_GPU_BOOST_FREQ_ON_BAT=900 TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 850 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 900 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=900 INTEL_GPU_MAX_FREQ_ON_AC=1050 INTEL_GPU_BOOST_FREQ_ON_AC=1100 TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 900 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1050 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT=700 INTEL_GPU_MAX_FREQ_ON_BAT=850 INTEL_GPU_BOOST_FREQ_ON_BAT=900 TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 850 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 900 [MHz] $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT=700 INTEL_GPU_MAX_FREQ_ON_BAT=950 INTEL_GPU_BOOST_FREQ_ON_BAT=950 TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 950 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 950 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=1100 TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ # --- unconfigured $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT= INTEL_GPU_MAX_FREQ_ON_BAT= INTEL_GPU_BOOST_FREQ_ON_BAT= TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ # --- freqs invalid or out of range $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC= INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MIN_FREQ_ON_AC="": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=XYZZY INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MIN_FREQ_ON_AC="XYZZY": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=100 INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MIN_FREQ_ON_AC="100": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC= INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MAX_FREQ_ON_AC="": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=XYZZY INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MAX_FREQ_ON_AC="XYZZY": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=100 INTEL_GPU_BOOST_FREQ_ON_AC=1100 Error in configuration at INTEL_GPU_MAX_FREQ_ON_AC="100": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC= Error in configuration at INTEL_GPU_BOOST_FREQ_ON_AC="": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=XYZZY Error in configuration at INTEL_GPU_BOOST_FREQ_ON_AC="XYZZY": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=700 INTEL_GPU_MAX_FREQ_ON_AC=1100 INTEL_GPU_BOOST_FREQ_ON_AC=100 Error in configuration at INTEL_GPU_BOOST_FREQ_ON_AC="100": frequency invalid or out of range (see 'tlp-stat -g'). TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ # --- boundary conditions not met $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT=750 INTEL_GPU_MAX_FREQ_ON_BAT=700 INTEL_GPU_BOOST_FREQ_ON_BAT=1100 Error in configuration: INTEL_GPU_MIN_FREQ_ON_BAT > INTEL_GPU_MAX_FREQ_ON_BAT. TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=1 INTEL_GPU_MIN_FREQ_ON_BAT=700 INTEL_GPU_MAX_FREQ_ON_BAT=750 INTEL_GPU_BOOST_FREQ_ON_BAT=700 Error in configuration: INTEL_GPU_MAX_FREQ_ON_BAT > INTEL_GPU_BOOST_FREQ_ON_BAT. TLP started in battery mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] $ sudo tlp start -- X_SIMULATE_PS=0 INTEL_GPU_MIN_FREQ_ON_AC=800 INTEL_GPU_MAX_FREQ_ON_AC=750 INTEL_GPU_BOOST_FREQ_ON_AC=700 Error in configuration: INTEL_GPU_MIN_FREQ_ON_AC > INTEL_GPU_MAX_FREQ_ON_AC. TLP started in AC mode (auto). $ sudo tlp-stat | grep -E "gt_(min|max|boost)" /sys/class/drm/card0/gt_min_freq_mhz = 700 [MHz] /sys/class/drm/card0/gt_max_freq_mhz = 1100 [MHz] /sys/class/drm/card0/gt_boost_freq_mhz = 1100 [MHz] TLP-1.5.0/unit-tests/kmod-helper000077500000000000000000000021071416575757700164600ustar00rootroot00000000000000#!/bin/sh # Kernel module helper for battery care testing: disable/remove DKMS modules # $1: module name # $2: disable/enable/remove/restore # Constants KILLFILE=/etc/modprobe.d/kmod-helper.conf # Functions unload_kmod () { # $1: module name modprobe -r "$1" } # MAIN if [ "$1" != "acpi_call" ] && [ "$1" != "tp_smapi" ]; then echo "Error: unknown kernel module \"$1\"." 1>&2 exit 1 fi kernel=$(uname -r) module=$1 modfile="/lib/modules/${kernel}/updates/dkms/${module}.ko" modsave="${modfile}-save" case "$2" in disable) unload_kmod "$module" echo "install ${module} killmod" > $KILLFILE ;; enable) rm -f "$KILLFILE" ;; remove) unload_kmod "$module" if [ -f "$modfile" ]; then mv "$modfile" "$modsave" else exit 2 fi ;; restore) if [ -f "$modsave" ]; then mv "$modsave" "$modfile" else exit 2 fi ;; *) echo "Error: unknown action \"$2\"." 1>&2 exit 1 ;; esac exit 0 TLP-1.5.0/unit-tests/processor000077500000000000000000000012051416575757700162660ustar00rootroot00000000000000#!/usr/bin/clitest # Test Processor features # Requirements: # * Intel Core i processor gen. 6 or newer # * HWP in active mode $ # --- valid freqs $ sudo tlp start -- X_SIMULATE_PS=1 CPU_HWP_DYN_BOOST_ON_AC=1 CPU_HWP_DYN_BOOST_ON_BAT=0 TLP started in battery mode (auto). $ sudo tlp-stat -p | grep hwp_dynamic_boost /sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost = 0 $ sudo tlp start -- X_SIMULATE_PS=0 CPU_HWP_DYN_BOOST_ON_AC=1 CPU_HWP_DYN_BOOST_ON_BAT=0 TLP started in AC mode (auto). $ sudo tlp-stat -p | grep hwp_dynamic_boost /sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost = 1 $ # --- Reset $ sudo tlp start > /dev/null TLP-1.5.0/unit-tests/unit-tests.rst000066400000000000000000000006271416575757700172010ustar00rootroot00000000000000Unit Tests for TLP ================== Tests cover only part of the functionality of TLP. For objectives see the individual files. Prequisites ----------- Software ^^^^^^^^ Tests are executed with the tool `clitest `_. Hardware ^^^^^^^^ Unit tests must be run on real hardware, not in a virtual machine or container. See the individual files for hardware details.