pax_global_header00006660000000000000000000000064132320172630014511gustar00rootroot0000000000000052 comment=ede82efcf5e0d017895396f732322bf274056895 TLP-1.1/000077500000000000000000000000001323201726300120115ustar00rootroot00000000000000TLP-1.1/.github/000077500000000000000000000000001323201726300133515ustar00rootroot00000000000000TLP-1.1/.github/CODE_OF_CONDUCT.md000066400000000000000000000062331323201726300161540ustar00rootroot00000000000000# 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.1/.github/CONTRIBUTING.md000066400000000000000000000011501323201726300155770ustar00rootroot00000000000000### Issues Disclaimer: the issue tracker is intended for TLP bug reports only, *not* as a general support forum. For questions on how to install, configure or use TLP and ThinkPad battery features, visit adequate Linux forums. *Before* opening an issue read the [FAQ](http://linrunner.de/en/tlp/docs/tlp-faq.html) and [Troubleshooting](http://linrunner.de/en/tlp/docs/tlp-troubleshooting.html). **When opening an issue, provide all the information requested by the template.** ### Code Please read the [developer docs](http://linrunner.de/en/tlp/docs/tlp-developer-documentation.html) before contributing code. TLP-1.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000003631323201726300160600ustar00rootroot00000000000000### Symptom data 1. Does the problem occur on battery or AC or both? 2. Attach the *full* output of `tlp-stat` via https://gist.github.com/ for *all* cases of 1. ### Expected behavior ### Actual behavior ### Steps to reproduce the problem TLP-1.1/49tlp000066400000000000000000000005311323201726300127070ustar00rootroot00000000000000#!/bin/sh # tlp - handle suspend/hibernate/resume tasks # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. . "${PM_FUNCTIONS}" case $1 in hibernate|suspend) tlp suspend ;; thaw|resume) tlp resume ;; *) exit $NA ;; esac exit 0 TLP-1.1/AUTHORS000066400000000000000000000002611323201726300130600ustar00rootroot00000000000000Main author: Thomas Koch - Contributors (refer to Git commits for details): André Erdmann Pali Rohár TLP-1.1/COPYING000066400000000000000000000013331323201726300130440ustar00rootroot00000000000000Author: Thomas Koch Copyright: Copyright (c) 2018 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 - http://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.1/LICENSE000066400000000000000000000431031323201726300130170ustar00rootroot00000000000000 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.1/Makefile000066400000000000000000000106561323201726300134610ustar00rootroot00000000000000# Makefile for TLP # Evaluate parameters TLP_SBIN ?= /usr/sbin TLP_BIN ?= /usr/bin TLP_TLIB ?= /usr/share/tlp TLP_PLIB ?= /usr/lib/pm-utils TLP_ULIB ?= /lib/udev TLP_NMDSP ?= /etc/NetworkManager/dispatcher.d TLP_CONF ?= /etc/default/tlp TLP_SYSD ?= /lib/systemd/system TLP_SYSV ?= /etc/init.d TLP_SHCPL ?= /usr/share/bash-completion/completions TLP_MAN ?= /usr/share/man TLP_META ?= /usr/share/metainfo TLP_RUN ?= /run/tlp # Catenate DESTDIR to paths _SBIN = $(DESTDIR)$(TLP_SBIN) _BIN = $(DESTDIR)$(TLP_BIN) _TLIB = $(DESTDIR)$(TLP_TLIB) _PLIB = $(DESTDIR)$(TLP_PLIB) _ULIB = $(DESTDIR)$(TLP_ULIB) _NMDSP = $(DESTDIR)$(TLP_NMDSP) _CONF = $(DESTDIR)$(TLP_CONF) _SYSD = $(DESTDIR)$(TLP_SYSD) _SYSV = $(DESTDIR)$(TLP_SYSV) _SHCPL = $(DESTDIR)$(TLP_SHCPL) _MAN = $(DESTDIR)$(TLP_MAN) _META = $(DESTDIR)$(TLP_META) _RUN = $(DESTDIR)$(TLP_RUN) SED = sed \ -e "s|@TLP_SBIN@|$(TLP_SBIN)|g" \ -e "s|@TLP_TLIB@|$(TLP_TLIB)|g" \ -e "s|@TLP_PLIB@|$(TLP_PLIB)|g" \ -e "s|@TLP_ULIB@|$(TLP_ULIB)|g" \ -e "s|@TLP_CONF@|$(TLP_CONF)|g" \ -e "s|@TLP_RUN@|$(TLP_RUN)|g" INFILES = \ tlp \ tlp-functions \ tlp-nop \ tlp-rdw-nm \ tlp-rdw.rules \ tlp-rdw-udev \ tlp-rf \ tlp.rules \ tlp-run-on \ tlp.service \ tlp-sleep.service \ tlp-stat \ tlp.upstart \ tlp-usb-udev MANFILES1 = \ bluetooth.1 \ run-on-ac.1 \ run-on-bat.1 \ tlp-pcilist.1 \ tlp-usblist.1 \ wifi.1 \ wwan.1 MANFILES8 = \ tlp.8 \ tlp-stat.8 \ tlp.service.8 \ tlp-sleep.service.8 SHFILES = \ tlp.in \ tlp-functions.in \ tlp-nop.in \ tlp-rdw-nm.in \ tlp-rdw-udev.in \ tlp-rf-func \ tlp-rf.in \ tlp-run-on.in \ tlp-stat.in \ tlp-usb-udev.in # Make targets all: $(INFILES) $(INFILES): %: %.in $(SED) $< > $@ clean: rm -f $(INFILES) install-tlp: all # Package tlp install -D -m 755 tlp $(_SBIN)/tlp install -D -m 755 tlp-rf $(_BIN)/bluetooth ln -sf bluetooth $(_BIN)/wifi ln -sf bluetooth $(_BIN)/wwan install -m 755 tlp-run-on $(_BIN)/run-on-ac ln -sf run-on-ac $(_BIN)/run-on-bat install -m 755 tlp-stat $(_BIN)/ install -m 755 tlp-usblist $(_BIN)/ install -m 755 tlp-pcilist $(_BIN)/ ifneq ($(TLP_NO_TPACPI),1) install -D -m 755 tpacpi-bat $(_TLIB)/tpacpi-bat endif install -D -m 755 tlp-functions $(_TLIB)/tlp-functions install -m 755 tlp-rf-func $(_TLIB)/ install -D -m 755 tlp-usb-udev $(_ULIB)/tlp-usb-udev install -D -m 644 tlp.rules $(_ULIB)/rules.d/85-tlp.rules [ -f $(_CONF) ] || install -D -m 644 default $(_CONF) ifneq ($(TLP_NO_INIT),1) install -D -m 755 tlp.init $(_SYSV)/tlp endif ifeq ($(TLP_WITH_SYSTEMD),1) install -D -m 644 tlp.service $(_SYSD)/tlp.service install -m 644 tlp-sleep.service $(_SYSD)/ endif ifneq ($(TLP_NO_PMUTILS),1) install -m 755 tlp-nop $(_TLIB)/ install -D -m 755 49tlp $(_PLIB)/sleep.d/49tlp endif ifneq ($(TLP_NO_BASHCOMP),1) install -D -m 644 tlp.bash_completion $(_SHCPL)/tlp ln -sf tlp $(_SHCPL)/tlp-stat ln -sf tlp $(_SHCPL)/bluetooth ln -sf tlp $(_SHCPL)/wifi ln -sf tlp $(_SHCPL)/wwan endif install -D -m 644 de.linrunner.tlp.metainfo.xml $(_META)/de.linrunner.tlp.metainfo.xml install-rdw: all # Package 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 install-man: # manpages install -d 755 $(_MAN)/man1 cd man && install -m 644 $(MANFILES1) $(_MAN)/man1/ install -d 755 $(_MAN)/man8 cd man && install -m 644 $(MANFILES8) $(_MAN)/man8/ install: install-tlp install-rdw uninstall-tlp: # Package tlp rm $(_SBIN)/tlp rm $(_BIN)/bluetooth rm $(_BIN)/wifi rm $(_BIN)/wwan rm $(_BIN)/run-on-ac rm $(_BIN)/run-on-bat rm $(_BIN)/tlp-stat rm $(_BIN)/tlp-usblist rm $(_BIN)/tlp-pcilist rm -f $(_TLIB)/tpacpi-bat rm $(_TLIB)/tlp-functions rm $(_TLIB)/tlp-rf-func rm $(_TLIB)/tlp-nop rmdir $(_TLIB) rm $(_ULIB)/tlp-usb-udev rm $(_ULIB)/rules.d/85-tlp.rules rm -f $(DESTDIR)/etc/init.d/tlp rm -f $(_SYSD)/tlp.service rm -f $(_SYSD)/tlp-sleep.service rm -f $(_PLIB)/sleep.d/49tlp rm -f $(_SHCPL)/tlp-stat rm -f $(_SHCPL)/bluetooth rm -f $(_SHCPL)/wifi rm -f $(_SHCPL)/wwan rm -f $(_SHCPL)/tlp rm -f $(_META)/de.linrunner.tlp.metainfo.xml uninstall-rdw: # Package tlp-rdw rm $(_ULIB)/rules.d/85-tlp-rdw.rules rm $(_ULIB)/tlp-rdw-udev rm $(_NMDSP)/99tlp-rdw-nm uninstall-man: # manpages cd $(_MAN)/man1 && rm -f $(MANFILES1) cd $(_MAN)/man8 && rm -f $(MANFILES8) uninstall: uninstall-tlp uninstall-rdw checkbashisms: checkbashisms $(SHFILES) || true TLP-1.1/README000066400000000000000000000047751323201726300127060ustar00rootroot00000000000000TLP README - 09.01.2018 TLP brings you the benefits of advanced power management for Linux without the need to understand every technical detail. TLP comes with a default configuration already optimized for battery life, so you may just install and forget it. Nevertheless TLP is highly customizable to fulfil your specific requirements. TLP is a pure command line tool with automated background tasks. It does not contain a GUI. Separate settings profiles depending on the power source AC/battery: - Kernel laptop mode and dirty buffer params - Processor frequency scaling including "turbo boost" / "turbo core" - Limit max/min P-state to control power dissipation of the CPU - intel_pstate only - HWP energy performance hints - Kernel 4.10 and Intel Skylake CPU or newer - Power aware process scheduler for multi-core/hyper-threading - Processor performance versus energy savings policy (x86_energy_perf_policy) - Hard disk advanced power magement level and spin down timeout (per disk) - AHCI link power management (ALPM) with device blacklist - AHCI runtime power management for host controllers and disks *EXPERIMENTAL* - PCIe active state power management (PCIe ASPM) - Runtime power management for PCI(e) bus devices - Radeon graphics KMS power management - not fglrx - Radeon graphics dynamic power management - not fglrx - Wifi power saving mode - depending on kernel/driver - Enable/disable integrated radio devices (excluding connected devices) - Power off optical drive in UltraBay/MediaBay - Audio power saving mode - hda_intel, ac97 Additional functions: - I/O scheduler (per disk) - X86 energy perf policy - USB autosuspend with device blacklist/whitelist (input devices excluded automatically) - Enable or disable integrated radio devices upon system startup and shutdown - Restore radio device state on system startup (from previous shutdown) - Radio device wizard: switch radios upon network connect/disconnect and dock/undock - Disable Wake On LAN - Integrated WWAN and bluetooth state is restored after suspend/hibernate - Untervolting of Intel processors - requires kernel with PHC-Patch - Battery charge thresholds - ThinkPads only - Recalibrate battery - ThinkPads only All TLP settings are stored in /etc/default/tlp. The default configuration provides optimized power saving out of the box. Read the the full documentation at: - http://linrunner.de/tlp Or take a look at the manpages: - tlp - tlp-stat (display tlp status and active settings) - wifi, bluetooth, wwan (switch wireless devices on/off) - run-on-ac, run-on-bat TLP-1.1/changelog000066400000000000000000000624171323201726300136750ustar00rootroot00000000000000+++ 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.1/de.linrunner.tlp.metainfo.xml000066400000000000000000000032121323201726300175330ustar00rootroot00000000000000 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 http://linrunner.de/tlp dmi:*:ct8:* dmi:*:ct9:* dmi:*:ct10:* acpi:PNP0C0A:*
TLP-1.1/default000066400000000000000000000270201323201726300133610ustar00rootroot00000000000000# ------------------------------------------------------------------------------ # tlp - Parameters for power saving # See full explanation: http://linrunner.de/en/tlp/docs/tlp-configuration.html # Hint: some features are disabled by default, remove the leading # to enable # them. # Set to 0 to disable, 1 to enable TLP. TLP_ENABLE=1 # Operation mode when no power supply can be detected: AC, BAT. # Concerns some desktop and embedded hardware only. TLP_DEFAULT_MODE=AC # Operation mode select: 0=depend on power source, 1=always use TLP_DEFAULT_MODE # Hint: use in conjunction with TLP_DEFAULT_MODE=BAT for BAT settings on AC. TLP_PERSISTENT_DEFAULT=0 # Seconds laptop mode has to wait after the disk goes idle before doing a sync. # Non-zero value enables, zero disables laptop mode. DISK_IDLE_SECS_ON_AC=0 DISK_IDLE_SECS_ON_BAT=2 # Dirty page values (timeouts in secs). MAX_LOST_WORK_SECS_ON_AC=15 MAX_LOST_WORK_SECS_ON_BAT=60 # Hint: CPU parameters below are disabled by default, remove the leading # # to enable them, otherwise kernel default values are used. # Select a CPU frequency scaling governor. # Intel Core i processor with intel_pstate driver: # powersave(*), performance. # Older hardware with acpi-cpufreq driver: # ondemand(*), powersave, performance, conservative, schedutil. # (*) is recommended. # Hint: use tlp-stat -p to show the active driver and available governors. # Important: # powersave for intel_pstate and ondemand for acpi-cpufreq are power # efficient for *almost all* workloads and therefore kernel and most # distributions have chosen them as defaults. If you still want to change, # you should know what you're doing! You *must* disable your distribution's # governor settings or conflicts will occur. #CPU_SCALING_GOVERNOR_ON_AC=powersave #CPU_SCALING_GOVERNOR_ON_BAT=powersave # Set the min/max frequency available for the scaling governor. # Possible values strongly depend on your CPU. For available frequencies see # the output of tlp-stat -p. #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 energy performance hints (HWP) for Intel P-state governor: # performance, balance_performance, default, balance_power, power # Values are given in order of increasing power saving. # Note: Intel Skylake or newer CPU and Kernel >= 4.10 required. CPU_HWP_ON_AC=balance_performance CPU_HWP_ON_BAT=balance_power # Set Intel P-state performance: 0..100 (%). # Limit the max/min P-state to control the power dissipation of the CPU. # Values are stated as a percentage of the available performance. # Requires an Intel Core i processor with intel_pstate driver. #CPU_MIN_PERF_ON_AC=0 #CPU_MAX_PERF_ON_AC=100 #CPU_MIN_PERF_ON_BAT=0 #CPU_MAX_PERF_ON_BAT=30 # Set the CPU "turbo boost" feature: 0=disable, 1=allow # Requires an Intel Core i processor. # Important: # - This may conflict with your distribution's governor settings # - A value of 1 does *not* activate boosting, it just allows it #CPU_BOOST_ON_AC=1 #CPU_BOOST_ON_BAT=0 # Minimize number of used CPU cores/hyper-threads under light load conditions: # 0=disable, 1=enable. SCHED_POWERSAVE_ON_AC=0 SCHED_POWERSAVE_ON_BAT=1 # Kernel NMI Watchdog: # 0=disable (default, saves power), 1=enable (for kernel debugging only). NMI_WATCHDOG=0 # Change CPU voltages aka "undervolting" - Kernel with PHC patch required. # Frequency voltage pairs are written to: # /sys/devices/system/cpu/cpu0/cpufreq/phc_controls # CAUTION: only use this, if you thoroughly understand what you are doing! #PHC_CONTROLS="F:V F:V F:V F:V" # Set CPU performance versus energy savings policy: # performance, balance-performance, default, balance-power, power. # Values are given in order of increasing power saving. # Requires kernel module msr and x86_energy_perf_policy from linux-tools. ENERGY_PERF_POLICY_ON_AC=performance ENERGY_PERF_POLICY_ON_BAT=power # Disk devices; separate multiple devices with spaces (default: sda). # Devices can be specified by disk ID also (lookup with: tlp diskid). DISK_DEVICES="sda sdb" # 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. DISK_APM_LEVEL_ON_AC="254 254" DISK_APM_LEVEL_ON_BAT="128 128" # Hard disk spin down timeout: # 0: spin down disabled # 1..240: timeouts from 5s to 20min (in units of 5s) # 241..251: timeouts from 30min to 5.5 hours (in units of 30min) # See 'man hdparm' for details. # Separate values for multiple disks with spaces. Use the special value 'keep' # to keep the hardware default for the particular disk. #DISK_SPINDOWN_TIMEOUT_ON_AC="0 0" #DISK_SPINDOWN_TIMEOUT_ON_BAT="0 0" # Select IO scheduler for the disk devices: cfq, deadline, noop (Default: cfq). # Separate values for multiple disks with spaces. Use the special value 'keep' # to keep the kernel default scheduler for the particular disk. #DISK_IOSCHED="cfq cfq" # AHCI link power management (ALPM) for disk devices: # min_power, med_power_with_dipm(*), medium_power, max_performance. # (*) Kernel >= 4.15 required, then recommended. # Multiple values separated with spaces are tried sequentially until success. SATA_LINKPWR_ON_AC="med_power_with_dipm max_performance" SATA_LINKPWR_ON_BAT="med_power_with_dipm min_power" # Exclude host devices from AHCI link power management. # Separate multiple hosts with spaces. #SATA_LINKPWR_BLACKLIST="host1" # Runtime Power Management for AHCI host and disks devices: # on=disable, auto=enable. # EXPERIMENTAL ** WARNING: auto will most likely cause system lockups/data loss. #AHCI_RUNTIME_PM_ON_AC=on #AHCI_RUNTIME_PM_ON_BAT=on # Seconds of inactivity before disk is suspended. AHCI_RUNTIME_PM_TIMEOUT=15 # PCI Express Active State Power Management (PCIe ASPM): # default, performance, powersave. PCIE_ASPM_ON_AC=performance PCIE_ASPM_ON_BAT=powersave # Radeon graphics clock speed (profile method): low, mid, high, auto, default; # auto = mid on BAT, high on AC; default = use hardware defaults. RADEON_POWER_PROFILE_ON_AC=high RADEON_POWER_PROFILE_ON_BAT=low # Radeon dynamic power management method (DPM): battery, performance. RADEON_DPM_STATE_ON_AC=performance RADEON_DPM_STATE_ON_BAT=battery # Radeon DPM performance level: auto, low, high; auto is recommended. RADEON_DPM_PERF_LEVEL_ON_AC=auto RADEON_DPM_PERF_LEVEL_ON_BAT=auto # WiFi power saving mode: on=enable, off=disable; not supported by all adapters. WIFI_PWR_ON_AC=off WIFI_PWR_ON_BAT=on # Disable wake on LAN: Y/N. WOL_DISABLE=Y # Enable audio power saving for Intel HDA, AC97 devices (timeout in secs). # A value of 0 disables, >=1 enables power saving (recommended: 1). SOUND_POWER_SAVE_ON_AC=0 SOUND_POWER_SAVE_ON_BAT=1 # Disable controller too (HDA only): Y/N. SOUND_POWER_SAVE_CONTROLLER=Y # Power off optical drive in UltraBay/MediaBay: 0=disable, 1=enable. # Drive can be powered on again by releasing (and reinserting) the eject lever # or by pressing the disc eject button on newer models. # Note: an UltraBay/MediaBay hard disk is never powered off. BAY_POWEROFF_ON_AC=0 BAY_POWEROFF_ON_BAT=0 # Optical drive device to power off (default sr0). BAY_DEVICE="sr0" # Runtime Power Management for PCI(e) bus devices: on=disable, auto=enable. RUNTIME_PM_ON_AC=on RUNTIME_PM_ON_BAT=auto # Exclude PCI(e) device adresses the following list from Runtime PM # (separate with spaces). Use lspci to get the adresses (1st column). #RUNTIME_PM_BLACKLIST="bb:dd.f 11:22.3 44:55.6" # Exclude PCI(e) devices assigned to the listed drivers from Runtime PM. # Default when unconfigured is "amdgpu nouveau nvidia radeon" which # prevents accidential power-on of dGPU in hybrid graphics setups. # Use "" to disable the feature completely. # Separate multiple drivers with spaces. #RUNTIME_PM_DRIVER_BLACKLIST="amdgpu nouveau nvidia radeon" # Set to 0 to disable, 1 to enable USB autosuspend feature. USB_AUTOSUSPEND=1 # Exclude listed devices from USB autosuspend (separate with spaces). # Use lsusb to get the ids. # Note: input devices (usbhid) are excluded automatically #USB_BLACKLIST="1111:2222 3333:4444" # Bluetooth devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. USB_BLACKLIST_BTUSB=0 # Phone devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude (enable charging). USB_BLACKLIST_PHONE=0 # Printers are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. USB_BLACKLIST_PRINTER=1 # WWAN devices are excluded from USB autosuspend: # 0=do not exclude, 1=exclude. USB_BLACKLIST_WWAN=1 # Include listed devices into USB autosuspend even if already excluded # by the blacklists above (separate with spaces). # Use lsusb to get the ids. #USB_WHITELIST="1111:2222 3333:4444" # Set to 1 to disable autosuspend before shutdown, 0 to do nothing # (workaround for USB devices that cause shutdown problems). #USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN=1 # Restore radio device state (Bluetooth, WiFi, WWAN) from previous shutdown # on system startup: 0=disable, 1=enable. # Hint: the parameters DEVICES_TO_DISABLE/ENABLE_ON_STARTUP/SHUTDOWN below # are ignored when this is enabled! RESTORE_DEVICE_STATE_ON_STARTUP=0 # Radio devices to disable on startup: bluetooth, wifi, wwan. # Separate multiple devices with spaces. #DEVICES_TO_DISABLE_ON_STARTUP="bluetooth wifi wwan" # Radio devices to enable on startup: bluetooth, wifi, wwan. # Separate multiple devices with spaces. #DEVICES_TO_ENABLE_ON_STARTUP="wifi" # Radio devices to disable on shutdown: bluetooth, wifi, wwan. # (workaround for devices that are blocking shutdown). #DEVICES_TO_DISABLE_ON_SHUTDOWN="bluetooth wifi wwan" # Radio devices to enable on shutdown: bluetooth, wifi, wwan. # (to prevent other operating systems from missing radios). #DEVICES_TO_ENABLE_ON_SHUTDOWN="wwan" # Radio devices to enable on AC: bluetooth, wifi, wwan. #DEVICES_TO_ENABLE_ON_AC="bluetooth wifi wwan" # Radio devices to disable on battery: bluetooth, wifi, wwan. #DEVICES_TO_DISABLE_ON_BAT="bluetooth wifi wwan" # Radio devices to disable on battery when not in use (not connected): # bluetooth, wifi, wwan. #DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE="bluetooth wifi wwan" # Battery charge thresholds (ThinkPad only, tp-smapi or acpi-call kernel module # required). Charging starts when the remaining capacity falls below the # START_CHARGE_THRESH value and stops when exceeding the STOP_CHARGE_THRESH value. # Main / Internal battery (values in %) #START_CHARGE_THRESH_BAT0=75 #STOP_CHARGE_THRESH_BAT0=80 # Ultrabay / Slice / Replaceable battery (values in %) #START_CHARGE_THRESH_BAT1=75 #STOP_CHARGE_THRESH_BAT1=80 # Restore charge thresholds when AC is unplugged: 0=disable, 1=enable. #RESTORE_THRESHOLDS_ON_BAT=1 # ------------------------------------------------------------------------------ # tlp-rdw - Parameters for the radio device wizard # Possible devices: bluetooth, wifi, wwan. # Hints: # - Parameters are disabled by default, remove the leading # to enable them # - Separate multiple radio devices with spaces # 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.1/man/000077500000000000000000000000001323201726300125645ustar00rootroot00000000000000TLP-1.1/man/bluetooth.1000066400000000000000000000006171323201726300146570ustar00rootroot00000000000000.TH bluetooth 1 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME bluetooth \- enable/disable internal bluetooth device . .SH SYNOPSIS .B bluetooth \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/run-on-ac.1000066400000000000000000000004351323201726300144470ustar00rootroot00000000000000.TH run\-on\-ac 1 2017-01-29 "TLP 1.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) 2017 Thomas Koch TLP-1.1/man/run-on-bat.1000066400000000000000000000004441323201726300146320ustar00rootroot00000000000000.TH run\-on\-bat 1 2017-01-29 "TLP 1.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) 2017 Thomas Koch TLP-1.1/man/tlp-pcilist.1000066400000000000000000000006471323201726300151210ustar00rootroot00000000000000.TH tlp\-pcilist 1 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME tlp\-pcilist \- show PCI(e) device data. . .SH SYNOPSIS .B tlp\-pcilist . .SH DESCRIPTION List syspath, power saving control, class and driver for all devices. . .SH SEE ALSO .BR tlp\-stat (8). . .SH NOTES This command is called as a subroutine from tlp\-stat, do \fInot\fR invoke it directly. . .SH AUTHOR (c) 2017 Thomas Koch . TLP-1.1/man/tlp-sleep.service.8000066400000000000000000000010211323201726300162130ustar00rootroot00000000000000.TH tlp\-sleep.service 8 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME . tlp\-sleep.service \- Disable power saving at suspend and restore upon resume. . .SH SYNOPSIS .B tlp\-sleep\&.service . .SH DESCRIPTION tlp-sleep.service executes the following tasks: .IP " 1." 4 System suspend: save radio states and apply AC settings. .IP " 2. " 4 System resume: restore radio states and apply power saving settings. . .SH SEE ALSO .BR systemd-sleep (8), .BR tlp.service (8). . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/tlp-stat.8000066400000000000000000000022151323201726300144250ustar00rootroot00000000000000.TH tlp\-stat 8 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME tlp\-stat \- show power saving settings . .SH SYNOPSIS .B tlp\-stat \fI[option]\fR "..." . .SH DESCRIPTION Show configuration, system information, active power saving settings and battery data. . .SH OPTIONS . .TP .B \-b, \-\-battery Show battery data. . .TP .B \-c, \-\-config Show configuration. . .TP .B \-d, \-\-disk Show disk data. . .TP .B \-e, \-\-pcie Show PCI(e) device data. . .TP .B \-g, \-\-graphics Show graphics card data. . .TP .B \-p, \-\-processor Show processor data. . .TP .B \-r, \-\-rfkill Show radio device data. . .TP .B \-s, \-\-system Show system information. . .TP .B \-t, \-\-temp Show temperatures and fan speed. . .TP .B \-u, \-\-usb Show USB device data. . .TP .B \-w, \-\-warn Show warnings. . .TP .B \-P, \-\-pev Monitor power supply udev events. . .TP .B \-\-psup Show power supply diagnostic. . .TP .B \-T, \-\-trace Show trace output. . .TP .B \-v, \-\-verbose Be more verbose. . .SH FILES .I /etc/default/tlp .RS System configuration file containing all power saving settings. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/tlp-usblist.1000066400000000000000000000006621323201726300151340ustar00rootroot00000000000000.TH tlp\-usblist 1 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME tlp\-usblist \- show USB device data. . .SH SYNOPSIS .B tlp\-usblist . .SH DESCRIPTION List busnum, devnum, id, autosuspend controls, description and driver for all devices. . .SH SEE ALSO .BR tlp\-stat (8). . .SH NOTES This command is called as a subroutine from tlp\-stat, do \fInot\fR invoke it directly. .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/tlp.8000066400000000000000000000055301323201726300134570ustar00rootroot00000000000000.TH tlp 8 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME tlp \- apply laptop power saving settings . .SH SYNOPSIS .B tlp \fIcommand\fR \fR[\fIparameter\fR] "..." . .SH DESCRIPTION Apply power saving settings manually and control ThinkPad battery features. . .SH COMMANDS . .TP .B start Initialize \fBtlp\fR and apply power saving settings according to the actual power source. . .TP .B bat Apply power saving settings for battery power source. . .TP .B true Same as \fBbat\fR (this command is called when power source changes to battery). . .TP .B ac Apply power saving settings for AC power source. . .TP .B false Same as \fBac\fR (this command is called when power source changes to ac). . .TP .B usb Enable autosuspend for all USB devices, except blacklisted ones. . .TP .B bayoff Turn off optical drive in UltraBay/MediaBay. The drive may be re\-enabled by pulling the eject lever or pushing the media eject button on newer models. . .TP .B setcharge [START_CHARGE STOP_CHARGE] [BAT0|BAT1] \fR(ThinkPads only) Set charge thresholds of main (BAT0) or auxiliary (BAT1) battery temporarily. Values must be between 1 and 100, STOP_CHARGE > START_CHARGE + 3. Configured thresholds are restored upon next system boot-up. When called without arguments, configured thresholds are set. . .TP .B fullcharge [BAT0|BAT1] \fR(ThinkPads only) Set charge thresholds of main (BAT0) or auxiliary (BAT1) battery to factory presets (96/100) temporarily (causing a full charge). Configured thresholds are restored upon next system boot-up. . .TP .B chargeonce [BAT0|BAT1] \fR(ThinkPads only) Charge main (BAT0) or auxiliary (BAT1) battery to the stop charge threshold once (bypassing start threshold). Configured thresholds are restored upon next system boot-up. . .TP .B discharge [BAT0|BAT1] \fR(ThinkPads only) Force complete discharge of main (BAT0) or auxiliary (BAT1) battery. . .TP .B recalibrate [BAT0|BAT1] \fR(ThinkPads only) Battery recalibration: completely discharge main (BAT0) or auxiliary (BAT1) battery and recharge to 100%. . .TP .B stat Same as tlp\-stat. . .TP .B diskid Show disk ids for configuration. . .SH NOTES For ThinkPads with more than one battery the selection works as follows: .IP BAT0 \- Main or internal battery .IP BAT1 \- Ultrabay, Slice or replaceable battery . .SH EXAMPLES Apply thresholds of 70 / 90% to the main battery: .IP tlp setcharge 70 90 BAT0 .PP Charge the auxiliary battery to full capacity: .IP tlp fullcharge BAT1 .PP Recalibrate the main battery: .IP tlp recalibrate BAT0 . .SH FILES .I /etc/default/tlp .RS System-wide configuration file containing all power saving settings. . .SH EXIT STATUS On success, 0 is returned, a non-zero failure code otherwise. . .SH SEE ALSO .BR tlp\-stat (8), .BR bluetooth (1), .BR wifi (1), .BR wwan (1). . .PP .mso www.tmac .URL "http://linrunner.de" "Project homepage: " . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/tlp.service.8000066400000000000000000000012541323201726300151150ustar00rootroot00000000000000.TH tlp.service 8 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME . tlp.service \- Initialize power saving at boot and cleanup upon shutdown . .SH SYNOPSIS .B tlp\&.service . .SH DESCRIPTION tlp.service executes the following tasks: .IP " 1." 4 System boot-up: switch or restore radio states, apply power saving settings and charge thresholds. .IP " 2." 4 System shutdown: switch or save radio states, apply AC settings and cleanup. . .SH SEE ALSO .BR tlp (8), .BR tlp-sleep.service (8). . .SH NOTES Do \fInot\fR employ tlp.service to refresh power saving settings after a configuration change. Use \fBtlp start\fR instead. . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/wifi.1000066400000000000000000000005731323201726300136110ustar00rootroot00000000000000.TH wifi 1 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME wifi \- enable/disable internal wifi device . .SH SYNOPSIS .B wifi \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/man/wwan.1000066400000000000000000000006031323201726300136210ustar00rootroot00000000000000.TH wwan 1 2017-01-29 "TLP 1.0" "Power Management" . .SH NAME wwan \- enable/disable internal wwan (3G/4G) device . .SH SYNOPSIS .B wwan \fR[\fIcommand\fR] . .SH COMMANDS . .TP .B on Switch device on. . .TP .B off Switch device off. . .TP .B toggle Toggle device state. . .TP Show device state. . .SH SEE ALSO .BR tlp (8). . .SH AUTHOR (c) 2017 Thomas Koch TLP-1.1/tlp-functions.in000066400000000000000000002573551323201726300151670ustar00rootroot00000000000000#!/bin/sh # tlp - power management functions # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # # Some concepts and descriptions were adapted from: # - laptop-mode-tools # - thinkwiki.org # ---------------------------------------------------------------------------- # Constants readonly TLPVER="1.1" readonly CONFFILE=@TLP_CONF@ readonly RUNDIR=@TLP_RUN@ readonly ETHTOOL=ethtool readonly HDPARM=hdparm readonly IWC=iwconfig readonly IW=iw readonly MODPRO=modprobe readonly LOGGER=logger readonly UDEVADM=udevadm readonly LAPMODE=laptop_mode readonly NMCLI=nmcli readonly NMD=NetworkManager readonly NMTOOL=nm-tool readonly ENERGYPERF=x86_energy_perf_policy readonly DBUSSEND=dbus-send readonly SYSTEMD=systemd readonly SYSTEMCTL=systemctl readonly INITCTL=initctl readonly FLOCK=flock readonly TPACPIBAT=$LIBDIR/tpacpi-bat # LIBDIR is initialized by main program readonly TPACPIDIR=/sys/devices/platform/thinkpad_acpi readonly SMAPIDIR=/sys/devices/platform/smapi readonly ACPIBATDIR=/sys/class/power_supply 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_TPACPI_ONLY='^(13.*|(Edge )?E[1345][234567][05][ps]?|Helix.*|L[45][34567]0|P[75][01][s]?|S2|(Edge )?S[245][345][01]*|T[45][34567][01][psu]?|W5[345][01][s]?|X1 Carbon.*|X2[34567]0[s]?( Tablet)?|.*Yoga.*)$' readonly RE_TPSMAPI_AND_TPACPI='^(X1|X220[s]?( Tablet)?|T[45]20[s]?|W520)$' readonly RE_TP_NONE='^(L[45]20|SL[345]00|X121e)$' readonly NETD=/sys/class/net readonly BLUETOOTHD=/sys/class/bluetooth readonly PCID=/sys/bus/pci/devices readonly PCIDRV=/sys/bus/pci/drivers readonly I915D='/sys/module/i915*/parameters' readonly RADD=/sys/module/radeon readonly DMID=/sys/class/dmi/id/ readonly CPU_BOOST_ALL_CTRL=/sys/devices/system/cpu/cpufreq/boost readonly INTEL_PSTATED=/sys/devices/system/cpu/intel_pstate readonly CPU_MIN_PERF_PCT=$INTEL_PSTATED/min_perf_pct readonly CPU_MAX_PERF_PCT=$INTEL_PSTATED/max_perf_pct readonly CPU_TURBO_PSTATE=$INTEL_PSTATED/no_turbo readonly AHCID=$PCID'/*/ata*' readonly BLOCKD='/sys/block/sd*' readonly USBD=/sys/bus/usb/devices readonly USB_TIMEOUT=2 readonly USB_TIMEOUT_MS=2000 readonly USB_WWAN_VENDORS="0bdb 05c6 1199" readonly USB_DONE=usb_done readonly DOCKGLOB="/sys/devices/platform/dock.?" readonly MOD_MSR="msr" readonly MOD_TEMP="coretemp" readonly MOD_TPSMAPI="tp_smapi" readonly MOD_TPACPI="acpi_call" readonly PWRRUNFILE=$RUNDIR/last_pwr readonly LOCKFILE=$RUNDIR/lock readonly LOCKTIMEOUT=2 readonly RDW_NM_LOCK="rdw_nm" readonly RDW_DOCK_LOCK="rdw_dock" readonly RDW_LOCKTIME=2 readonly STATEDIR="/var/lib/tlp" readonly RFSTATEFILE=$STATEDIR/rfkill-saved readonly BAYSTATEFILE=$STATEDIR/bay-saved readonly DISK_NOP_WORDS="_ keep" readonly DEFAULT_DISK_DEVICES="sda" readonly DEFAULT_DISK_IO_SCHEDULER="cfq" readonly DEFAULT_PM_DRIVER_BLACKLIST="amdgpu nouveau nvidia radeon" # ---------------------------------------------------------------------------- # Control nodebug=0 # ---------------------------------------------------------------------------- # Functions # --- Sysfiles catsysfd () { # echo contents of a sysfile; if file is non-existent or # read fails, echo default value instead # $1: sysfile, $2: default value local val="$(cat $1 2> /dev/null)" [ -n "$val" ] || val="$2" # sysfile nonexistent or read failed printf "%s" $val return 0 } # --- Tests 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 } 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[$$,$PPID]" "$2" fi } cmd_exists () { # test if command exists -- $1: command command -v $1 > /dev/null 2>&1 } check_sysfs () { # check if sysfile exists -- $1: routine; $2: sysfs path if wordinlist "sysfs" "$TLP_DEBUG"; then if [ ! -e $2 ]; then $LOGGER -p debug -t "tlp[$$,$PPID]" "$1: $2 nonexistent" fi fi } test_root () { # test root privilege -- rc: 0=root, 1=not root [ "$(id -u)" = "0" ] } check_root () { # show error message and exit when root privilege missing if ! test_root; then echo "Error: missing root privilege." 1>&2 exit 1 fi } check_tlp_enabled () { # check if TLP is enabled in config file # rc: 0=disabled/1=enabled if [ ! "$TLP_ENABLE" = "1" ]; then echo "Error: TLP power save is disabled. Set TLP_ENABLE=1 in $CONFFILE." 1>&2 return 1 else return 0 fi } 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 } check_systemd () { # check if systemd is the active init system (PID 1) and systemctl is installed # rc: 0=yes, 1=no [ -d /run/systemd/system ] && cmd_exists $SYSTEMCTL } check_upstart () { # check if upstart is active init system (PID 1) # rc: 0=yes, 1=no cmd_exists $INITCTL && $INITCTL --version | grep -q upstart } check_openrc () { # check if openrc is the active init system (PID 1) # rc: 0=yes, 1=no [ -e /run/openrc/softlevel ] } # --- PATH add_sbin2path () { # check if /sbin /usr/sbin in $PATH, otherwise add them # retval: $PATH, $oldpath, $addpath local sp oldpath="$PATH" addpath="" for sp in /usr/sbin /sbin; do if [ -d $sp ] && [ ! -h $sp ]; then # dir exists and is not a symlink case ":$PATH:" in *":$sp:"*) # $sp already in $PATH ;; *) # $sp not in $PATH, add it addpath="$addpath:$sp" ;; esac fi done if [ -n "$addpath" ]; then export PATH="${PATH}${addpath}" fi return 0 } create_rundir () { # make sure $RUNDIR exists [ -d $RUNDIR ] || mkdir -p $RUNDIR 2> /dev/null 1>&2 } # --- Configuration read_defaults () { # read config file if [ -f $CONFFILE ]; then . $CONFFILE return 0 else return 1 fi } # --- Kernel Modules 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 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="" for key in $*; do ds="$( cat ${DMID}/$key 2> /dev/null | \ egrep -iv 'not available|to be filled|DMI table is broken' )" if [ -n "$outr" ]; then [ -n "$ds" ] && outr="$outr $ds" else outr="$ds" fi done printf '%s' "$outr" return 0 } # --- ThinkPad check_thinkpad () { # check for ThinkPad hardware and save model string, # load ThinkPad specific kernel modules # rc: 0=ThinkPad, 1=other hardware # retval: $tpmodel local pv tpmodel="" if [ -d $TPACPIDIR ]; then # kernel module thinkpad_acpi is loaded # get DMI product string and sanitize it pv="$( read_dmi product_version | tr -C -d '[a-zA-Z0-9 ]' )" # check DMI product string for occurrence of "ThinkPad" if printf '%s' "$pv" | grep -q "ThinkPad"; then # it's a real ThinkPad --> save model substring tpmodel=${pv#ThinkPad } fi fi if [ -n "$tpmodel" ]; then # load tp-smapi when not explicitly unsupported only; # prevents kernel error messages if ! supports_tpacpi_only && ! supports_no_tp_bat_funcs; then load_modules $MOD_TPSMAPI fi # load acpi-call unconditionally load_modules $MOD_TPACPI echo_debug "bat" "check_thinkpad: tpmodel=$tpmodel" return 0 fi # not a ThinkPad echo_debug "bat" "check_thinkpad.not_a_thinkpad" return 1 } is_thinkpad () { # check for ThinkPad by saved model string # rc: 0=ThinkPad, 1=other hardware [ -n "$tpmodel" ] } # --- Power Source get_sys_power_supply () { # get current power supply # retval/rc: $syspwr/0=ac, 1=battery, 2=unknown local psrc syspwr= for psrc in /sys/class/power_supply/*; do # -f $psrc/type not necessary - cat 2>.. handles this case "$(cat $psrc/type 2> /dev/null)" in Mains) # AC detected, check if online; # exclude MacBook Pro 2017 sbs-charger if [ "$(cat $psrc/online 2> /dev/null)" = "1" ] \ && [ "${psrc##/*/}" != "sbs-charger" ]; then syspwr=0 break fi # else AC not online => keep $syspwr as-is ;; Battery) # battery detected, exclude hid peripherals if echo "${psrc##/*/}" | grep -vq "hidpp_battery"; then # not hid --> set rc to battery, but don't stop looking for AC syspwr=1 fi ;; *) echo_debug "pm" "unknown power supply: ${psrc##/*/}" ;; esac done # set pwrsrc to unknown if we haven't seen any AC/battery power source so far : ${syspwr:=2} return $syspwr } get_power_state () { # get current power mode -- rc: 0=ac, 1=battery # similar to get_sys_power_supply(), # but maps unknown power source to TLP_DEFAULT_MODE; # returns TLP_DEFAULT_MODE when TLP_PERSISTENT_DEFAULT=1 get_sys_power_supply local rc=$? if [ -n "$TLP_DEFAULT_MODE" ] \ && [ "$TLP_PERSISTENT_DEFAULT" = "1" ]; then # persistent mode, use configured default mode case "$TLP_DEFAULT_MODE" in bat|BAT) rc=1 ;; *) rc=0 ;; esac else # non-persistent mode, use current power source if [ $rc -eq 2 ]; then # unknown power supply, use configured default mode case "$TLP_DEFAULT_MODE" in ac|AC) rc=0 ;; bat|BAT) rc=1 ;; *) rc=0 ;; # use AC if no default mode configured 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 # read saved state lp=$(cat $PWRRUNFILE 2> /dev/null) # compare if [ -z "$lp" ] || [ "$lp" != "$1" ]; then # saved state is nonexistent/empty or is different --> save new state create_rundir { printf '%s\n' "$1" > $PWRRUNFILE; } 2> /dev/null echo_debug "pm" "compare_and_save_power_state($1).different: old=$lp" return 0 else echo_debug "pm" "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 } echo_started_mode () { # print operation mode -- $1: 0=ac mode, 1=battery mode if [ "$1" = "0" ]; then echo "TLP started in AC mode." else echo "TLP started in battery mode." 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 { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null # fopen/attach successful --> lock fd 9 exclusive and blocking, # wait $LOCKTIME secs to obtain the lock if [ $? -eq 0 ] && $FLOCK -x -w $LOCKTIMEOUT 9 ; then echo_debug "lock" "lock_tlp.success" return 0 else echo_debug "lock" "lock_tlp.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 { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null # fopen/attach successful --> lock fd 9 exclusive and non-blocking if [ $? -eq 0 ] && $FLOCK -x -n 9 ; then echo_debug "lock" "lock_tlp_nb.success" return 0 else echo_debug "lock" "lock_tlp_nb.failed" return 1 fi } unlock_tlp () { # free exclusive lock # defer unlock for $X_DEFER_UNLOCK seconds -- debugging only [ -n "$X_DEFER_UNLOCK" ] && sleep $X_DEFER_UNLOCK # free fd 9 { exec 9>&- ; } 2> /dev/null echo_debug "lock" "unlock_tlp" return 0 } echo_tlp_locked () { # print "locked" message echo "TLP is locked by another operation." return 0 } set_timed_lock () { # create timestamp n seconds in the future # $1: lock id, $2: lock duration [s] local rc local 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 local 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 local 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 } # --- Filesystem set_laptopmode () { # set kernel laptop mode -- $1: 0=ac mode, 1=battery mode check_sysfs "set_laptopmode" "/proc/sys/vm/laptop_mode" local isec if [ "$1" = "1" ]; then isec=${DISK_IDLE_SECS_ON_BAT:-} else isec=${DISK_IDLE_SECS_ON_AC:-} fi # replace with empty string if non-numeric chars are contained isec=$(printf '%s' "$isec" | egrep '^[0-9]+$') if [ -z "$isec" ]; then # do nothing if unconfigured or non numeric value echo_debug "pm" "set_laptopmode($1).not_configured" return 0 fi echo_debug "pm" "set_laptopmode($1): $isec" { printf '%s\n' "$isec" > /proc/sys/vm/laptop_mode; } 2> /dev/null return 0 } set_dirty_parms () { # set filesystem buffer params # $1: 0=ac mode, 1=battery mode # concept from laptop-mode-tools local age cage df check_sysfs "set_dirty_parms" "/proc/sys/vm" if [ "$1" = "1" ]; then age=${MAX_LOST_WORK_SECS_ON_BAT:-0} else age=${MAX_LOST_WORK_SECS_ON_AC:-0} fi # calc age in centisecs, non numeric values result in "0" cage=$(($age * 100)) if [ "$cage" = "0" ]; then # do nothing if unconfigured or invalid age echo_debug "pm" "set_dirty_parms($1).not_configured" return 0 fi echo_debug "pm" "set_dirty_parms($1): $cage" 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 [ -w $df ] && { printf '%s\n' "$cage" > $df; } 2> /dev/null done [ -w /proc/sys/fs/xfs/xfsbufd_centisecs ] \ && { printf '%s\n' "3000" > /proc/sys/fs/xfs/xfsbufd_centisecs; } 2> /dev/null return 0 } # --- CPU check_intel_pstate () { # detect intel_pstate driver -- retval: $intel_pstate # Note: intel_pstate requires Linux 3.9 or higher intel_pstate=0 [ -d $INTEL_PSTATED ] && intel_pstate=1 return 0 } set_scaling_governor () { # set scaling governor -- $1: 0=ac mode, 1=battery mode local gov cpu if [ "$1" = "1" ]; then gov=$CPU_SCALING_GOVERNOR_ON_BAT else gov=$CPU_SCALING_GOVERNOR_ON_AC fi if [ -n "$gov" ]; then echo_debug "pm" "set_scaling_governor($1): $gov" for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do [ -f $cpu ] && { printf '%s\n' "$gov" > $cpu; } 2> /dev/null done fi return 0 } set_scaling_min_max_freq () { # set scaling limits -- $1: 0=ac mode, 1=battery mode local minfreq maxfreq cpu 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" ]; then echo_debug "pm" "set_scaling_min_max_freq($1).min: $minfreq" for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do [ -f $cpu ] && { printf '%s\n' "$minfreq" > $cpu; } 2> /dev/null done fi if [ -n "$maxfreq" ]; then echo_debug "pm" "set_scaling_min_max_freq($1).max: $maxfreq" for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do [ -f $cpu ] && { printf '%s\n' "$maxfreq" > $cpu; } 2> /dev/null done fi return 0 } set_cpu_hwp_pref () { # set HWP energy performance hints -- $1: 0=ac mode, 1=battery mode local hwpp cpu local avail=0 check_intel_pstate if [ "$intel_pstate" != "1" ]; then echo_debug "pm" "set_cpu_perf_pct($1).no_intel_pstate" return 0 fi if [ "$1" = "1" ]; then hwpp=$CPU_HWP_ON_BAT else hwpp=$CPU_HWP_ON_AC fi if [ -n "$hwpp" ]; then for cpu in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do if [ -f $cpu ]; then { printf '%s\n' "$hwpp" > $cpu; } 2> /dev/null avail=1 fi done if [ "$avail" = "1" ]; then echo_debug "pm" "set_cpu_hwp_pref($1): $hwpp" else echo_debug "pm" "set_cpu_hwp_pref($1).no_hwp" fi else echo_debug "pm" "set_cpu_hwp_pref($1).not_configured" fi return 0 } set_cpu_perf_pct () { # set Intel P-state performance # $1: 0=ac mode, 1=battery mode local min max check_intel_pstate if [ "$intel_pstate" != "1" ]; then echo_debug "pm" "set_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_cpu_perf_pct($1).min.not_supported" elif [ -n "$min" ]; then { printf '%s\n' "$min" > $CPU_MIN_PERF_PCT; } 2> /dev/null echo_debug "pm" "set_cpu_perf_pct($1).min: $min" else echo_debug "pm" "set_cpu_perf_pct($1).min.not_configured" fi if [ ! -f $CPU_MAX_PERF_PCT ]; then echo_debug "pm" "set_cpu_perf_pct($1).max.not_supported" elif [ -n "$max" ]; then { printf '%s\n' "$max" > $CPU_MAX_PERF_PCT; } 2> /dev/null echo_debug "pm" "set_cpu_perf_pct($1).max: $max" else echo_debug "pm" "set_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 ival 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 check_intel_pstate if [ $intel_pstate -eq 1 ]; then # use intel_pstate sysfiles if [ -f $CPU_TURBO_PSTATE ]; then ival=$(($val ^ 1)) { printf '%s\n' "$ival" > $CPU_TURBO_PSTATE; } 2> /dev/null 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 { printf '%s\n' "$val" > $CPU_BOOST_ALL_CTRL; } 2> /dev/null; then echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq: $val" else echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq.cpu_not_supported" fi else echo_debug "pm" "set_cpu_boost_all($1).not_available" fi return 0 } set_sched_powersave () { # set multi-core/-thread powersave policy # $1: 0=ac mode, 1=battery mode local pwr pool sdev local avail=0 if [ "$1" = "1" ]; then pwr=${SCHED_POWERSAVE_ON_BAT:-} else pwr=${SCHED_POWERSAVE_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_sched_powersave($1).not_configured" return 0 fi for pool in mc smp smt; do sdev="/sys/devices/system/cpu/sched_${pool}_power_savings" if [ -f $sdev ]; then echo_debug "pm" "set_sched_powersave($1): ${sdev##/*/} $pwr" { printf '%s\n' "$pwr" > "$sdev"; } 2> /dev/null avail=1 fi done [ "$avail" = "1" ] || echo_debug "pm" "set_sched_powersave($1).not_available" return 0 } 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 { printf '%s\n' "$nmiwd" > /proc/sys/kernel/nmi_watchdog; } 2> /dev/null if [ $? = 0 ]; then echo_debug "pm" "set_nmi_watchdog: $nmiwd" else echo_debug "pm" "set_nmi_watchdog.disabled_by_kernel: $nmiwd" fi else echo_debug "pm" "set_nmi_watchdog.not_available" fi return 0 } set_phc_controls () { # set core voltages local control local ctrl_avail="0" phc_controls=${PHC_CONTROLS:-} if [ -z "$phc_controls" ]; then # do nothing if unconfigured echo_debug "pm" "set_phc_controls.not_configured" return 0 fi for control in /sys/devices/system/cpu/cpu*/cpufreq/phc_controls; do if [ -f $control ]; then echo_debug "pm" "set_phc_controls: $control $phc_controls" { printf '%s\n' "$phc_controls" > $control; } 2> /dev/null ctrl_avail="1" fi done [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_phc_controls.not_available" return 0 } set_energy_perf_policy () { # set performance versus energy savings policy # $1: 0=ac mode, 1=battery mode local perf pnum rc if [ "$1" = "1" ]; then perf=${ENERGY_PERF_POLICY_ON_BAT:-} else perf=${ENERGY_PERF_POLICY_ON_AC:-} fi # translate alphanumeric to numeric values for backward compatibility pnum=$(echo $perf | sed -r 's/^performance$/0/; s/^balance-performance$/4/; s/^(default|normal)$/6/; s/^balance-power?$/8/; s/^power(save)?$/15/') if [ -z "$pnum" ]; then echo_debug "pm" "set_energy_perf_policy($1).not_configured" elif ! cmd_exists $ENERGYPERF; then # x86_energy_perf_policy not installed echo_debug "pm" "set_energy_perf_policy($1).not_available" 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_energy_perf_policy($1): $perf($pnum)" ;; 1) echo_debug "pm" "set_energy_perf_policy($1): $perf($pnum) -- unsupported cpu" ;; 2) echo_debug "pm" "set_energy_perf_policy($1): $perf($pnum) -- kernel specific x86_energy_perf_policy missing" ;; *) echo_debug "pm" "set_energy_perf_policy($1): $perf($pnum) -- unknown rc=$rc " ;; esac return $rc fi return 0 } # --- Storage Devices check_disk_hdparm_cap () { # check if relevant disk device # $1: dev; rc: 0=yes/1=no if [ -z "$($HDPARM -I /dev/$1 2>&1 | \ egrep 'Invalid argument|Invalid exchange|missing sense data|No such device')" ]; then return 0 else return 1 fi } echo_disk_model () { # print disk model -- $1: dev local model model=$($HDPARM -I /dev/$1 2>&1 | grep 'Model Number' | \ cut -f2 -d: | sed -r 's/^ *//' ) printf '%s\n' "$model" return 0 } echo_disk_firmware () { # print firmware version --- $1: dev local firmware firmware=$($HDPARM -I /dev/$1 2>&1 | grep 'Firmware Revision' | \ cut -f2 -d: | sed -r 's/^ *//' ) printf '%s\n' "$firmware" return 0 } get_disk_state () { # get disk power state -- $1: dev; retval: $disk_state disk_state=$($HDPARM -C /dev/$1 2>&1 | awk -F ':' '/drive state is/ { gsub(/ /,"",$2); print $2; }') [ -z "$disk_state" ] && disk_state="(not available)" return 0 } spindown_disk () { # stop spindle motor -- $1: dev $HDPARM -y /dev/$1 > /dev/null 2>&1 return 0 } get_disk_apm_level () { # get disk apm level -- $1: dev; rc: apm local apm apm=$($HDPARM -I /dev/$1 2>&1 | grep 'Advanced power management level' | \ cut -f2 -d: | egrep "^ *[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 [ -n "$($HDPARM -I /dev/$1 2>&1 | grep 'Solid State Device')" ]; then if [ -n "$($HDPARM -I /dev/$1 2>&1 | grep 'TRIM supported')" ]; then trim=1 else trim=0 fi else trim=255 fi return $trim } get_disk_dev () { # translate disk id to device (sdX) # $1: id or dev; retval: $disk_dev, $disk_id if [ -h /dev/disk/by-id/$1 ]; then # $1 is disk id disk_id=$1 disk_dev=$(printf '%s' "$disk_id" | sed -r 's/-part[1-9][0-9]*$//') disk_dev=$(readlink /dev/disk/by-id/$disk_dev) disk_dev=${disk_dev##*/} else # $1 is disk dev disk_dev=$1 disk_id="" fi # strip partition number disk_dev=$(printf '%s' "$disk_dev" | sed -r 's/[1-9][0-9]*$//') } set_disk_apm_level () { # set disk apm level # $1: 0=ac mode, 1=battery mode local pwrmode="$1" local dev log_message # when undefined use default : ${DISK_DEVICES:=${DEFAULT_DISK_DEVICES}} # 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 # exit if empty apmlist [ $# -gt 0 ] || return 0 # pairwise iteration DISK_DEVICES[1,n], apmlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], apmlist[min(j,m)] # for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_APM_LEVEL list handling} get_disk_dev $dev log_message="set_disk_apm_level($pwrmode): $disk_dev [$disk_id] $1" if [ ! -b /dev/$disk_dev ]; then echo_debug "disk" "${log_message} -- missing" elif ! check_disk_hdparm_cap $disk_dev; then echo_debug "disk" "${log_message} -- not supported" elif wordinlist "$1" "$DISK_NOP_WORDS"; then echo_debug "disk" "${log_message} -- keep as is" else echo_debug "disk" "${log_message}" $HDPARM -B $1 /dev/$disk_dev > /dev/null 2>&1 fi # last entry in apmlist applies to all remaining disks [ $# -lt 2 ] || shift done return 0 } set_disk_spindown_timeout () { # set disk spindown timeout # $1: 0=ac mode, 1=battery mode local pwrmode="$1" local dev log_message # when undefined use default : ${DISK_DEVICES:=${DEFAULT_DISK_DEVICES}} # set @argv := timeoutlist if [ "$pwrmode" = "1" ]; then set -- $DISK_SPINDOWN_TIMEOUT_ON_BAT else set -- $DISK_SPINDOWN_TIMEOUT_ON_AC fi # exit if empty timeoutlist [ $# -gt 0 ] || return 0 # pairwise iteration DISK_DEVICES[1,n], timeoutlist[1,m]; m > 0 # for j in [1,n]: disk_dev[j], timeoutlist[min(j,m)] # for dev in $DISK_DEVICES; do : ${1:?BUG: broken DISK_SPINDOWN_TIMEOUT list handling} get_disk_dev $dev log_message="set_disk_spindown_timeout($pwrmode): $disk_dev [$disk_id] $1" if [ ! -b /dev/$disk_dev ]; then echo_debug "disk" "${log_message} -- missing" elif ! check_disk_hdparm_cap $disk_dev; then echo_debug "disk" "${log_message} -- not supported" elif wordinlist "$1" "$DISK_NOP_WORDS"; then echo_debug "disk" "${log_message} -- keep as is" else echo_debug "disk" "${log_message}" $HDPARM -S $1 /dev/$disk_dev > /dev/null 2>&1 fi # last entry in timeoutlist applies to all remaining disks [ $# -lt 2 ] || shift done return 0 } set_disk_io_sched () { # set disk io scheduler local dev sched schedctrl log_message # when undefined use default : ${DISK_DEVICES:=${DEFAULT_DISK_DEVICES}} # set @argv := schedlist set -- $DISK_IOSCHED # exit if empty timeoutlist [ $# -gt 0 ] || return 0 # 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], %DEFAULT_DISK_IO_SCHEDULER for dev in $DISK_DEVICES; do get_disk_dev $dev # get sched from argv, use default scheduler when list is too short sched=${1:-${DEFAULT_DISK_IO_SCHEDULER}} schedctrl="/sys/block/$disk_dev/queue/scheduler" log_message="set_disk_io_sched: $disk_dev [$disk_id] $sched" if [ ! -b /dev/$disk_dev ]; then echo_debug "disk" "${log_message} -- missing" elif [ ! -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 echo_debug "disk" "${log_message}" { printf '%s' "$sched" > $schedctrl; } 2> /dev/null fi # using %DEFAULT_DISK_IO_SCHEDULER when argv is empty [ $# -eq 0 ] || shift done return 0 } # --- Device Power Management 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 pwr_bl="" local ctrl_avail="0" if [ "$pm" = "1" ]; then pwrlist=${SATA_LINKPWR_ON_BAT:-} else pwrlist=${SATA_LINKPWR_ON_AC:-} fi if [ -z "$pwrlist" ]; then # do nothing if unconfigured echo_debug "pm" "set_sata_link_power($pm).not_configured" return 0 fi # ALPM blacklist host_bl=${SATA_LINKPWR_BLACKLIST:-} # copy configured values to args set -- $pwrlist # iterate SATA hosts for host in /sys/class/scsi_host/host* ; do linkpol=$host/link_power_management_policy if [ -f $linkpol ]; then hostid=${host##*/} if wordinlist "$hostid" "$host_bl"; then # host blacklisted --> skip echo_debug "pm" "set_sata_link_power($pm).black: $host" ctrl_avail="1" else # host not blacklisted --> iterate all configured values for pwr in "$@"; do { printf '%s\n' "$pwr" > $linkpol; } 2> /dev/null; rc=$? echo_debug "pm" "set_sata_link_power($pm).$pwr: $host; rc=$rc" if [ $rc -eq 0 ]; then # write successful --> goto next host ctrl_avail="1" break else # write failed --> don't use this value for remaining hosts # and try next value shift fi done fi fi done [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_sata_link_power($pm).not_available" return 0 } set_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 { printf '%s\n' "$pwr" > /sys/module/pcie_aspm/parameters/policy; } 2> /dev/null if [ $? = 0 ]; 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 } set_radeon_profile () { # set radeon power profile # $1: 0=ac mode, 1=battery mode local card level pwr rc1 rc2 local sdone=0 # 1=radeon present if [ ! -d $RADD ]; then # No card present --> exit echo_debug "pm" "set_radeon_profile($1).no_card" return 0 fi for card in /sys/class/drm/card[0-9]/device ; do if [ -f $card/power_dpm_state ] && [ -f $card/power_dpm_force_performance_level ]; then # Use new radeon dynamic power management method (dpm) if [ "$1" = "1" ]; then pwr=${RADEON_DPM_STATE_ON_BAT:-} level=${RADEON_DPM_PERF_LEVEL_ON_BAT:-auto} else pwr=${RADEON_DPM_STATE_ON_AC:-} level=${RADEON_DPM_PERF_LEVEL_ON_AC:-auto} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_radeon_profile($1).not_configured: $card" return 0 fi if [ -n "$pwr" ]; then { printf '%s\n' "$pwr" > $card/power_dpm_state; } 2> /dev/null; rc1=$? { printf '%s\n' "$level" > $card/power_dpm_force_performance_level; } 2> /dev/null; rc2=$? echo_debug "pm" "set_radeon_profile($1): $card state=$pwr [rc=$rc1] perf=$level [rc=$rc2]" fi sdone=1 elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then # Use old radeon power profile method if [ "$1" = "1" ]; then pwr=${RADEON_POWER_PROFILE_ON_BAT:-} else pwr=${RADEON_POWER_PROFILE_ON_AC:-} fi if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_radeon_profile($1).not_configured: $card" return 0 fi if [ -n "$pwr" ]; then echo_debug "pm" "set_radeon_profile($1): $card profile=$pwr" { printf '%s\n' "profile" > $card/power_method; } 2> /dev/null { printf '%s\n' "$pwr" > $card/power_profile; } 2> /dev/null fi sdone=1 fi done if [ $sdone -eq 0 ]; then echo_debug "pm" "set_radeon_profile($1).not_available" fi return 0 } 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:-Y} check_sysfs "set_sound_power_mode" "/sys/module" if [ -d /sys/module/snd_hda_intel ]; then echo_debug "pm" "set_sound_power_mode($1).hda: $pwr controller=$cpwr" { printf '%s\n' "$pwr" > /sys/module/snd_hda_intel/parameters/power_save; } 2> /dev/null if [ "$pwr" = "0" ]; then { printf '%s\n' "N" > /sys/module/snd_hda_intel/parameters/power_save_controller; } 2> /dev/null else { printf '%s\n' "$cpwr" > /sys/module/snd_hda_intel/parameters/power_save_controller; } 2> /dev/null fi fi if [ -d /sys/module/snd_ac97_codec ]; then echo_debug "pm" "set_sound_power_mode($1).ac97: $pwr" { printf '%s\n' "$pwr" > /sys/module/snd_ac97_codec/parameters/power_save; } 2> /dev/null fi return 0 } set_runtime_pm () { # set runtime power management # $1: 0=ac mode, 1=battery mode local address class ccontrol control device driver drv_bl pci_bl type vendor if [ "$1" = "1" ]; then ccontrol=${RUNTIME_PM_ON_BAT:-} else ccontrol=${RUNTIME_PM_ON_AC:-} fi if [ -z "$ccontrol" ]; then # do nothing if unconfigured echo_debug "pm" "set_runtime_pm($1).not_configured" return 0 fi # driver specific blacklist: # - undefined = use internal default from $DEFAULT_PM_DRIVER_BLACKLIST # - empty = disable feature drv_bl="${RUNTIME_PM_DRIVER_BLACKLIST-${DEFAULT_PM_DRIVER_BLACKLIST}}" # pci id blacklist pci_bl=${RUNTIME_PM_BLACKLIST:-} # add devices assigned to blacklisted drivers to the pci id blacklist 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"; then pci_bl="$pci_bl $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=$(cat $device/class 2> /dev/null) if wordinlist "$address" "$pci_bl"; then # device is in address blacklist control="black_address" else control=$ccontrol # check for Nvidia gpu's if wordinlist "nouveau" "$drv_bl" || wordinlist "nvidia" "$drv_bl"; then # driver nouveau or nvidia is in blacklist # --> blacklist depending on vendor and class vendor=$(cat $device/vendor 2> /dev/null) if [ "$vendor" = "0x10de" ]; then # vendor nvidia # --> check for display or 3d controller class case $class in "0x030000") control="black_nvgpu" ;; "0x030200") control="black_nvgpu" ;; esac fi fi # if nouveau | nvidia blacklisted case $control in auto|on) { printf '%s\n' $control > $device/power/control; } 2> /dev/null ;; *) ;; # do nothing esac fi # echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]" fi # if class && control done # for device done # for type return 0 } set_ahci_runtime_pm () { # set ahci runtime power management # $1: 0=ac mode, 1=battery mode local control device timeout rc if [ "$1" = "1" ]; then control=${AHCI_RUNTIME_PM_ON_BAT:-} else control=${AHCI_RUNTIME_PM_ON_AC:-} fi # calc timeout in millisecs, default to 15000 timeout=$((${AHCI_RUNTIME_PM_TIMEOUT:-15} * 1000)) [ "$timeout" != "0" ] || timeout=15000 # check values case "$control" in on|auto) ;; *) control="" ;; # invalid input --> unconfigured esac if [ -z "$control" ]; then # do nothing if unconfigured echo_debug "pm" "set_ahci_runtime_pm($1).not_configured" return 0 fi # iterate ahci devices for device in $AHCID; do if [ -f ${device}/power/control ]; then { printf '%s\n' $control $control > ${device}/power/control; } 2> /dev/null echo_debug "pm" "set_ahci_runtime_pm($1).$control: host=$device" fi done # iterate block devices for device in $BLOCKD; do if [ -f ${device}/device/power/control ]; then # write timeout first because writing "auto" with the default # timeout -1 still active will lockup the machine! rc=0 if { printf '%s\n' "$timeout" > ${device}/device/power/autosuspend_delay_ms; } 2> /dev/null; then # writing timeout was successful --> proceed with activation; # rc=2 when unsuccessful { printf '%s\n' $control > ${device}/device/power/control; } 2> /dev/null || rc=2 else # writing timeout was successful rc=1 fi echo_debug "pm" "set_ahci_runtime_pm($1).$control: disk=$device timeout=$timeout; rc=$rc" fi done return 0 } # --- Wifi Power Management 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) [ -n "$drvl" ] && wifidrv=${drvl##*/} fi return 0 } set_wifi_power_mode () { # set wifi power save mode -- $1: 0=ac mode, 1=battery mode local pwr iface local rc=0 local cmdx=0 if [ "$1" = "1" ]; then pwr=${WIFI_PWR_ON_BAT:-} else pwr=${WIFI_PWR_ON_AC:-} fi # check values, translate obsolete syntax case "$pwr" in off|on) ;; 0|1|N) pwr="off" ;; 2|3|4|5|6|Y) pwr="on" ;; *) pwr="" ;; # invalid input --> unconfigured esac if [ -z "$pwr" ]; then # do nothing if unconfigured echo_debug "pm" "set_wifi_power_mode($1).not_configured" return 0 fi get_wifi_ifaces if [ -z "$wifaces" ]; then echo_debug "pm" "set_wifi_power_mode($1).no_ifaces" return 0 fi for iface in $wifaces; do if [ -n "$iface" ]; then if [ "$X_DONT_USE_IW" != "1" ] && cmd_exists $IW; then # try with iw first $IW dev $iface set power_save $pwr > /dev/null 2>&1 rc=$? echo_debug "pm" "set_wifi_power_mode($1, $iface).iw: $pwr; rc=$rc" cmdx=1 # set flag: iw found and called fi if cmd_exists $IWC; then if [ $rc -ne 0 ] || [ $cmdx -eq 0 ]; then # iw did not succeed or iw not installed -> try with iwconfig $IWC $iface power $pwr > /dev/null 2>&1 rc=$? echo_debug "pm" "set_wifi_power_mode($1, $iface).iwconfig: $pwr; rc=$rc" cmdx=1 # set flag: iwconfig found and called fi fi if [ $cmdx -eq 0 ]; then # flag not set: neither iw nor iwconfig installed --> no way echo_debug "pm" "set_wifi_power_mode($1, $iface).no_tool" fi fi done return 0 } wireless_in_use () { # check if wifi or wwan device is in use -- $1: iface if [ -f $NETD/$1/carrier ]; then if [ "$(cat $NETD/$1/carrier 2>/dev/null)" = "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 } # --- 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) [ -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: $bluetoothdrv local drvl bluetoothdrv="" if [ -d $BLUETOOTHD/$1 ]; then drvl=$(readlink $BLUETOOTHD/$1/device/driver) [ -n "$drvl" ] && bluetoothdrv=${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 -qs "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 } # --- LAN get_eth_ifaces () { # get all eth devices -- retval: $ethifaces local ei eic ethifaces="" for eic in $NETD/*/device/class; do if [ "$(cat $eic 2> /dev/null)" = "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 WOL_DISABLE=${WOL_DISABLE:-N} if [ "$WOL_DISABLE" = "Y" ]; then get_eth_ifaces for ei in $ethifaces; do echo_debug "pm" "disable_wake_on_lan: $ei" $ETHTOOL -s $ei wol d > /dev/null 2>&1 done fi return 0 } # --- USB Autosuspend set_usb_suspend () { # activate usb autosuspend for all devices except input and blacklisted # $1: 0=silent/1=report result; $2: on/auto local busdev control dclass devices exc subdev usbd usbdev usbid vendor local ctrlf="control" local autof="autosuspend_delay_ms" local drvlist="" check_sysfs "set_usb_suspend" "$USBD" if [ "$USB_AUTOSUSPEND" = "1" ]; then # autosuspend is configured # iterate devices devices=$(ls $USBD 2> /dev/null | grep -v ':') for usbd in $devices; do # concat full device path usbdev=$USBD/$usbd if [ -f $usbdev/power/autosuspend ] || [ -f $usbdev/power/autosuspend_delay_ms ]; then vendor="$(cat $usbdev/idVendor 2> /dev/null)" usbid="$vendor:$(cat $usbdev/idProduct 2> /dev/null)" busdev="Bus $(cat $usbdev/busnum 2> /dev/null) Dev $(cat $usbdev/devnum 2> /dev/null)" dclass="$(cat $usbdev/bDeviceClass 2> /dev/null)" control="${2:-auto}" exc="" chg=0 # trace only: get drivers for the device and all subdevices if [ "$X_USB_DRIVER_TRACE" = "1" ]; then drvlist=$(for dl in $usbdev/*:*/driver; do readlink $dl | \ sed -r 's/.+\///'; done | sort -u | tr '\n' ' ') drvlist="(${drvlist% })" fi if [ "$control" != "on" ]; then if wordinlist "$usbid" "$USB_WHITELIST"; then # device is in whitelist -- whitelist always wins control="auto" exc="_dev_white" elif wordinlist "$usbid" "$USB_BLACKLIST"; then # device is in blacklist control="on" exc="_dev_black" else # check for hid subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "03" ]; then control="on" exc="_hid_black" break fi done if [ -z "$exc" ]; then # check for bluetooth devices USB_BLACKLIST_BTUSB=${USB_BLACKLIST_BTUSB:-0} # default is include if [ "$USB_BLACKLIST_BTUSB" = "1" ] \ && [ "$dclass" = "e0" ] \ && [ "$(cat $usbdev/bDeviceSubClass 2> /dev/null)" = "01" ] \ && [ "$(cat $usbdev/bDeviceProtocol 2> /dev/null)" = "01" ]; then control="on" exc="_btusb_black" fi fi # bluetooth if [ -z "$exc" ]; then # check for phone devices USB_BLACKLIST_PHONE=${USB_BLACKLIST_PHONE:-0} # default is include if [ "$USB_BLACKLIST_PHONE" = "1" ]; then if [ "$vendor" = "0fca" ]; then # RIM if [ "$dclass" = "ef" ]; then # RIM / BlackBerry control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(cat $subdev/interface 2> /dev/null)" = "BlackBerry" ]; then # Blackberry control="on" exc="_phone_black" break fi fi done fi elif [ "$vendor" = "045e" ] && [ "$dclass" = "ef" ]; then # Windows Phone control="on" exc="_phone_black" elif [ "$vendor" = "05ac" ] && [ "$(cat $usbdev/product 2> /dev/null)" = "iPhone" ]; then # iPhone control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then # class defined at interface level, iterate subdevices for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(cat $subdev/interface 2> /dev/null)" = "MTP" ]; then # MTP: mostly Android control="on" exc="_phone_black" break elif [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "ff" ] \ && [ "$(cat $subdev/bInterfaceSubClass 2> /dev/null)" = "42" ] \ && [ "$(cat $subdev/bInterfaceProtocol 2> /dev/null)" = "01" ]; then # ADB: Android control="on" exc="_phone_black" break elif [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "06" ] \ && [ "$(cat $subdev/bInterfaceSubClass 2> /dev/null)" = "01" ] \ && [ "$(cat $subdev/bInterfaceProtocol 2> /dev/null)" = "01" ]; then # PTP: iPhone, Lumia et al. # caveat: may also be a camera control="on" exc="_phone_black" break fi fi done fi # dclass 00 fi # blacklist phone fi # phone if [ -z "$exc" ]; then # check for printers USB_BLACKLIST_PRINTER=${USB_BLACKLIST_PRINTER:-1} # default is exclude if [ "$USB_BLACKLIST_PRINTER" = "1" ]; then if [ "$dclass" = "00" ]; then # check for printer subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "07" ]; then control="on" exc="_printer_black" break fi done fi fi fi # printer if [ -z "$exc" ]; then # check for wwan devices USB_BLACKLIST_WWAN=${USB_BLACKLIST_WWAN:-1} # default is exclude if [ "$USB_BLACKLIST_WWAN" = "1" ]; then if [ "$dclass" != "00" ]; then # check for cdc subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "0a" ]; then control="on" exc="_wwan_black" break fi done if [ -z "$exc" ]; then # check for vendors if wordinlist "$vendor" "$USB_WWAN_VENDORS"; then control="on" exc="_wwan_black" fi fi fi fi # blacklist wwan fi # wwan fi # !device blacklist fi # control=on if [ -f $usbdev/power/control ]; then if [ "$(cat $usbdev/power/control 2> /dev/null)" != "$control" ]; then # Write actual changes only { printf '%s\n' "$control" > $usbdev/power/control; } 2> /dev/null chg=1 fi else # level is deprecated if [ "$(cat $usbdev/power/level 2> /dev/null)" != "$control" ]; then # Write actual changes only { printf '%s\n' "$control" > $usbdev/power/level; } 2> /dev/null chg=1 fi ctrlf="level" fi if [ "$X_TLP_USB_SET_AUTOSUSPEND_DELAY" = "1" ]; then # set autosuspend_delay if [ -f $usbdev/power/autosuspend_delay_ms ]; then { printf '%s\n' $USB_TIMEOUT_MS > $usbdev/power/autosuspend_delay_ms; } 2> /dev/null else # autosuspend is deprecated { printf '%s\n' $USB_TIMEOUT > $usbdev/power/autosuspend; } 2> /dev/null autof="autosuspend" fi echo_debug "usb" "set_usb_suspend.$control$exc: $busdev ID $usbid $usbdev [$ctrlf $autof] $drvlist" elif [ $chg -eq 1 ]; then # default: change control but not autosuspend_delay, i.e. keep kernel default setting echo_debug "usb" "set_usb_suspend.$control$exc: $busdev ID $usbid $usbdev [$ctrlf] $drvlist" else # we didn't change anything actually echo_debug "usb" "set_usb_suspend.$control$exc: $busdev ID $usbid $usbdev $drvlist" fi fi done [ "$1" = "1" ] && echo "USB autosuspend settings applied." else [ "$1" = "1" ] && echo "Error: USB autosuspend is disabled. Set USB_AUTOSUSPEND=1 in $DEFAULT_FILE." 1>&2 fi # set "startup completion" flag for tlp-usb-udev set_run_flag $USB_DONE return 0 } # --- ThinkPad Battery Features supports_tpsmapi_only () { # rc: 0=ThinkPad supports tpsmapi only/1=false printf '%s' "$tpmodel" | egrep -q "${RE_TPSMAPI_ONLY}" } supports_tpacpi_only () { # rc: 0=ThinkPad supports tpacpi-bat only/1=false printf '%s' "$tpmodel" | egrep -q "${RE_TPACPI_ONLY}" } supports_tpsmapi_and_tpacpi () { # rc: 0=ThinkPad supports tpsmapi and tpacpi-bat/1=false printf '%s' "$tpmodel" | egrep -q "${RE_TPSMAPI_AND_TPACPI}" } supports_no_tp_bat_funcs () { # rc: 0=ThinkPad doesn't support battery features/1=false printf '%s' "$tpmodel" | egrep -q "${RE_TP_NONE}" } check_tpsmapi () { # check if tp_smapi is supported and loaded # rc: 0=supported/2=module tp_smapi not loaded/ # 127=not installed//255=not supported # global param: $tpmodel # retval: $tpsmapi if [ -d $SMAPIDIR ]; then # module loaded tpsmapi=0 else if [ -n "$(modinfo tp_smapi 2> /dev/null)" ]; then # module installed but not loaded if supports_tpacpi_only || supports_no_tp_bat_funcs; then # not tp-smapi capable models tpsmapi=255 else tpsmapi=2 fi else # module not installed if supports_tpacpi_only || supports_no_tp_bat_funcs; then # not tp-smapi capable models tpsmapi=255 else tpsmapi=127 fi fi fi echo_debug "bat" "check_tp_smapi: rc=$tpsmapi" return $tpsmapi } check_tpacpi () { # check if tpacpi-bat is supported # rc: 0=supported/2=acpi_call not loaded/4=disabled/ # 127=acpi_call not installed/255=not supported # retval: $tpacpi if supports_tpsmapi_only || supports_no_tp_bat_funcs; then # not tpacpi-bat capable models tpacpi=255 elif [ ! -e /proc/acpi/call ] && [ -z "$(modinfo acpi_call 2> /dev/null)" ]; then # module not installed tpacpi=127 else # module loaded --> try tpacpi-bat $TPACPIBAT -g FD 1 > /dev/null 2>&1 tpacpi=$? if [ $tpacpi -eq 0 ] && [ "$DISABLE_TPACPIBAT" = "1" ]; then tpacpi=4 fi fi echo_debug "bat" "check_tpacpi: rc=$tpacpi" return $tpacpi } check_tp_battery () { # check ThinkPad battery presence and return index # $1: BAT0/BAT1/DEF # global param: $tpacpi, $tpsmapi # rc: 0=bat exists/1=bat nonexistent/255=no thresh api available # retval: $bat_str: BAT0/BAT1; $bat_idx: 1/2 # defaults local rc=255 # no threshold API available bat_idx=0 # no tpacpi-bat index bat_str="" # no default bat found local blist bs # load modules and check prerequisites: tpacpi-bat or tp-smapi check_thinkpad check_tpacpi check_tpsmapi # validate param case $1 in BAT0|BAT1) blist="$1" ;; DEF) blist="BAT0 BAT1" ;; *) return 1 ;; esac if [ $tpsmapi -eq 0 ]; then # tp-smapi available rc=1 for bs in $blist; do # check tp-smapi name space if [ "$(cat $SMAPIDIR/$bs/installed 2> /dev/null)" = "1" ]; then rc=0 # battery detected case $bs in BAT0) bat_idx=1; bat_str="$bs" ;; BAT1) bat_idx=2; bat_str="$bs" ;; esac break # exit loop on first battery detected fi done elif [ $tpacpi -eq 0 ]; then # tpacpi-bat available rc=1 for bs in $blist; do # check acpi name space if [ "$(cat $ACPIBATDIR/$bs/present 2> /dev/null)" = "1" ]; then rc=0 # battery detected # determine tpacpi-bat index case $bs in BAT0) bat_idx=1 # BAT0 is always assumed main battery bat_str="$bs" ;; BAT1) # check with tpacpi-bat(2) if BAT1 is main or aux battery 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 bat_str="$bs" ;; esac break # exit loop on first battery detected fi done fi echo_debug "bat" "check_tp_battery($1): idx=$bat_idx; str=$bat_str; tpacpi=$tpacpi; tpsmapi=$tpsmapi; rc=$rc" return $rc } read_tpacpi_threshold () { # $1: ST/SP (start/stop); $2: 0/1 (battery) # rc: threshold (1..99, 0=default, 255=error) local thresh rc thresh=$($TPACPIBAT -g $1 $2 2> /dev/null | cut -f1 -d' ') rc=$? if [ $rc -eq 0 ] && [ -n "$thresh" ]; then [ $thresh -ge 128 ] && thresh=$(($thresh - 128)) # Remove offset of 128 for Edge S430 return $thresh else return 255 fi } do_threshold () { # $1: start/stop, $2: BAT0/BAT1, $3: new value # global param: $bat_idx, $tpsmapi, $tpacpi # rc: 0=ok/1=read error/2=thresh not present/255=no thresh api local bsys ts local old_thresh=-1 local new_thresh=$3 local rc=0 [ $3 -eq -1 ] && return 0 # -1 = do not set threshold if [ $tpacpi -eq 0 ]; then # use tpacpi-bat if [ $bat_idx -ne 0 ]; then # replace factory default values with 0 for tpacpi case $1 in start) [ $new_thresh -eq 96 ] && new_thresh=0 ts="ST" ;; stop) [ $new_thresh -eq 100 ] && new_thresh=0 ts="SP" ;; esac read_tpacpi_threshold $ts $bat_idx old_thresh=$? if [ $new_thresh -ne $old_thresh ]; then $TPACPIBAT -s $ts $bat_idx $new_thresh > /dev/null 2>&1 rc=$? fi fi elif [ $tpsmapi -eq 0 ]; then # use tp-smapi bsys=$SMAPIDIR/$2/${1}_charge_thresh if [ -f $bsys ]; then old_thresh=$(cat $bsys 2> /dev/null) if [ -z "$old_thresh" ]; then rc=1 elif [ "$old_thresh" -ne "$new_thresh" ]; then { printf '%s\n' $new_thresh > $bsys; } 2> /dev/null rc=$? fi else rc=2 # invalid bat argument fi else # no threshold API available rc=255 fi echo_debug "bat" "do_threshold($1, $2): bat_idx=$bat_idx; tpacpi=$tpacpi; tpsmapi=$tpsmapi; old=$old_thresh; new=$new_thresh; rc=$rc" return $rc } normalize_thresholds () { # check values and enforce start < stop - 3 # $1: start threshold; $2: stop_threshold # global param: $tpacpi, $tpsmapi # rc: 0 # retval: $start_thresh, $stop_thresh local type thresh for type in start stop; do case $type in start) thresh=$1 ;; stop) thresh=$2 ;; esac # check for 1..3 digits, replace with empty string if non-numeric chars are contained thresh=$(echo "$thresh" | egrep '^[0-9]{1,3}$') # replace empty string with -1 [ -z "$thresh" ] && thresh=-1 # ensure min/max values; replace 0 with defaults 96/100 case $type in start) [ $thresh -eq 0 ] || [ $thresh -gt 96 ] && thresh=96 start_thresh=$thresh ;; stop) [ $thresh -eq 0 ] || [ $thresh -gt 100 ] && thresh=100 [ $thresh -ne -1 ] && [ $thresh -lt 5 ] && thresh=5 stop_thresh=$thresh ;; esac done # enforce start < stop - 3 if [ $start_thresh -ne -1 ] && [ $stop_thresh -ne -1 ]; then [ $start_thresh -ge $(($stop_thresh - 3)) ] && start_thresh=$(($stop_thresh - 4)) fi echo_debug "bat" "normalize_thresholds($1, $2): start=$start_thresh; stop=$stop_thresh" return 0 } set_charge_thresholds () { # write all charge thresholds from configuration # global param: $tpacpi, $tpsmapi # rc: 0 local rc if check_tp_battery BAT0; then normalize_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" if [ $stop_thresh -ne -1 ]; then do_threshold stop BAT0 $stop_thresh; rc=$? echo_debug "bat" "set_charge_thresholds.stop(BAT0): $stop_thresh; rc=$rc" else echo_debug "bat" "set_charge_thresholds.stop(BAT0).not_set" fi if [ $start_thresh -ne -1 ]; then do_threshold start BAT0 $start_thresh; rc=$? echo_debug "bat" "set_charge_thresholds.start(BAT0): $start_thresh; rc=$rc" else echo_debug "bat" "set_charge_thresholds.start(BAT0).not_set" fi fi if check_tp_battery BAT1; then normalize_thresholds "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1" if [ $stop_thresh -ne -1 ]; then do_threshold stop BAT1 $stop_thresh; rc=$? echo_debug "bat" "set_charge_thresholds.stop(BAT1): $stop_thresh; rc=$rc" else echo_debug "bat" "set_charge_thresholds.stop(BAT1).not_set" fi if [ $start_thresh -ne -1 ]; then do_threshold start BAT1 $start_thresh; rc=$? echo_debug "bat" "set_charge_thresholds.start(BAT1): $start_thresh; rc=$rc" else echo_debug "bat" "set_charge_thresholds.start(BAT1).not_set" fi fi return 0 } do_force_discharge () { # write force discharge state # $1: BAT0/BAT1, $2: 0=off/1=on # global param: $bat_idx, $tpacpi, $tpsmapi # rc: 0=done/1=write error/2=discharge not present/255=no thresh api local bsys rc=0 if [ $tpacpi -eq 0 ]; then # use tpacpi-bat $TPACPIBAT -s FD $bat_idx $2 > /dev/null 2>&1; rc=$? echo_debug "bat" "do_force_discharge.tpacpi-bat($1, $2): rc=$rc" elif [ $tpsmapi -eq 0 ]; then # use tp-smapi bsys=$SMAPIDIR/$1/force_discharge if [ -f $bsys ]; then { printf '%s\n' $2 > $bsys; } 2> /dev/null; rc=$? else rc=2 fi echo_debug "bat" "do_force_discharge.tp-smapi($1, $2): rc=$rc" else # no threshold API available rc=255 echo_debug "bat" "do_force_discharge.noapi($1, $2)" fi return $rc } get_force_discharge () { # $1: BAT0/BAT1, # global param: $bat_idx, $tpacpi, $tpsmapi # rc: 0=off/1=on/2=discharge not present/255=no thresh api local bsys rc=0 if [ $tpacpi -eq 0 ]; then # use tpacpi-bat case $($TPACPIBAT -g FD $bat_idx 2> /dev/null) in yes) rc=1 ;; no) rc=0 ;; *) rc=2 ;; esac elif [ $tpsmapi -eq 0 ]; then # use tp-smapi bsys=$SMAPIDIR/$1/force_discharge if [ -f $bsys ]; then rc=$(cat $bsys 2> /dev/null) else rc=2 fi else # no threshold API available rc=255 fi echo_debug "bat" "get_force_discharge($1): rc=$rc" return $rc } cancel_force_discharge () { # called from trap -- global param: $bat_str do_force_discharge $bat_str 0 echo_debug "bat" "force_discharge.cancelled($bat_str)" echo " Cancelled." exit 0 } bat_discharging () { # check if battery is discharging -- $1: BAT0/BAT1, # global param: $bat_idx, $bat_str, $tpacpi, $tpsmapi # rc: 0=discharging/1=not discharging/255=no battery api local bsys rc=255 # determine battery api if [ $tpsmapi -eq 0 ]; then # use tp-smapi bsys=$SMAPIDIR/$bat_str/state elif [ $tpacpi -eq 0 ]; then # use tpacpi-bat bsys=$ACPIBATDIR/$bat_str/status fi # get battery state if [ -f $bsys ]; then case "$(cat $bsys 2> /dev/null)" in [Dd]ischarging) rc=0 ;; *) rc=1 ;; esac fi echo_debug "bat" "bat_discharging($1): rc=$rc" return $rc } 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 } setcharge_battery () { # write charge thresholds (called from cmd line) # $1: start charge threshold, $2: stop charge threshold, $3: battery # global param: $tpacpi, $tpsmapi # rc: 0=ok/1=error local bat rc st sp local use_cfg=0 # $bat_str is global for cancel_force_discharge() trap # check params case $# in 0) # no args bat=DEF # use default(1st) battery use_cfg=1 # use configured values ;; 1) # assume $1 is battery bat=$1 use_cfg=1 # use configured values ;; 2) # assume $1,$2 are thresholds st=$1 sp=$2 bat=DEF # use default(1st) battery ;; 3) # assume $1,$2 are thresholds, $3 is battery st=$1 sp=$2 bat=$3 ;; esac # convert bat to uppercase bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") # check bat presence and/or get default(1st) battery check_tp_battery $bat case $? in 0) # battery present # get configured values if requested if [ $use_cfg -eq 1 ]; then eval st="\$START_CHARGE_THRESH_$bat_str" eval sp="\$STOP_CHARGE_THRESH_$bat_str" fi ;; 255) # no api echo "Error: ThinkPad battery features not available." 1>&2 echo_debug "bat" "setcharge_battery.noapi" return 1 ;; *) # not present echo "Error: battery $bat not present." 1>&2 echo_debug "bat" "setcharge_battery.not_present($bat)" return 1 ;; esac # validate thresholds normalize_thresholds $st $sp # write threshold values echo "Setting temporary charge thresholds for $bat_str:" if [ $stop_thresh -ne -1 ]; then do_threshold stop $bat_str $stop_thresh; rc=$? echo_debug "bat" "setcharge_battery.stop($bat_str): $stop_thresh; rc=$rc" if [ $rc -eq 0 ]; then echo " stop = $stop_thresh" else echo " stop => Error: cannot set threshold. Aborting." 1>&2 return 1 fi else echo_debug "bat" "setcharge_battery.stop($bat_str).not_configured" echo " stop = not configured" fi if [ $start_thresh -ne -1 ]; then do_threshold start $bat_str $start_thresh; rc=$? echo_debug "bat" "setcharge_battery.start($bat_str): $start_thresh; rc=$rc" if [ $rc -eq 0 ]; then echo " start = $start_thresh" else echo " start => Warning: cannot set threshold." 1>&2 return 1 fi else echo_debug "bat" "setcharge_battery.start($bat_str).not_configured" echo " start = not configured" fi return 0 } get_sysval () { # $1: file; rc: sysfile value local sysf="$1" local val="" # read sysval when it exists [ -f $sysf ] && val=$(cat $sysf 2> /dev/null) # replace with 0 if empty string or non-numeric chars are contained [ -z "$(printf '%s' "$val" | egrep '^[0-9]+$')" ] && val=0 return $val } chargeonce_battery () { # charge battery to upper threshold once # $1: battery # global param: $tpacpi, $tpsmapi # rc: 0=ok/1=error local bat bdir temp_start_thresh local start_thresh="" local stop_thresh="" local efull=0 local enow=0 local ccharge=0 # check params if [ $# -gt 0 ]; then # some parameters given, check them # get battery arg bat=${1:-DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") else # no parameters given, use default(1st) battery bat=DEF fi # check if selected battery is present check_tp_battery $bat case $? in 0) ;; # battery present 255) # no api echo "Error: ThinkPad battery features not available." 1>&2 echo_debug "bat" "chargeonce_battery.noapi" return 1 ;; *) # not present echo "Error: battery $bat_str not present." 1>&2 echo_debug "bat" "chargeonce_battery.not_present($bat_str)" return 1 ;; esac # get and check thresholds from configuration) eval stop_thresh=\$STOP_CHARGE_THRESH_$bat_str eval start_thresh=\$START_CHARGE_THRESH_$bat_str [ -z "$stop_thresh" ] && stop_thresh=100 if [ -z "$start_thresh" ] ; then echo_debug "bat" "chargeonce_battery($bat_str).start_threshold_not_configured" echo "Error: no start charge threshold configured for $bat_str." 1>&2 return 1 fi # get current charge level (in %) if [ $tpsmapi -eq 0 ]; then # use tp-smapi bdir="$SMAPIDIR/$bat_str" get_sysval $bdir/remaining_percent; ccharge=$? else # use ACPI data bdir="$ACPIBATDIR/$bat_str" if [ -f $bdir/energy_full ]; then get_sysval $bdir/energy_full; efull=$? get_sysval $dir/energy_now; enow=$? fi if [ $efull -ne 0 ]; then ccharge=$(( 100 * $enow / $efull )) else ccharge=-1 fi fi if [ $ccharge -eq -1 ] ; then echo_debug "bat" "chargeonce_battery($bat_str).charge_level_unknown: enow=$enow; efull=$efull; ccharge=$ccharge" echo "Error: cannot determine charge level for $bat_str." 1>&2 return 1 else echo_debug "bat" "chargeonce_battery($bat_str).charge_level: enow=$enow; efull=$efull; ccharge=$ccharge" fi temp_start_thresh=$(( $stop_thresh - 4 )) if [ $temp_start_thresh -le $ccharge ] ; then echo_debug "bat" "chargeonce_battery($bat_str).charge_level_too_high: $temp_start_thresh $stop_thresh" echo "Error: current charge level ($ccharge) of $bat_str is higher than stop charge threshold - 4 ($temp_start_thresh)." 1>&2 return 1 else echo_debug "bat" "chargeonce_battery($bat_str).setcharge: $temp_start_thresh $stop_thresh" fi setcharge_battery $temp_start_thresh $stop_thresh $bat_str return $? } discharge_battery () { # discharge battery # $1: battery # global param: $tpacpi, $tpsmapi # rc: 0=ok/1=error local bat bdir en ef pn rc # $bat_str is global for cancel_force_discharge() trap # check params bat=$1 bat=${bat:=DEF} bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]") # check if selected battery is present check_tp_battery $bat case $? in 0) ;; # battery present 255) # no api echo "Error: ThinkPad battery features not available." 1>&2 echo_debug "bat" "discharge_battery.noapi" return 1 ;; *) # not present echo "Error: battery $bat not present." 1>&2 echo_debug "bat" "discharge_battery.not_present($bat)" return 1 ;; esac # start discharge do_force_discharge $bat_str 1; rc=$? if [ $rc -ne 0 ]; then echo_debug "bat" "discharge_battery.force_discharge_not_available($bat_str)" echo "Error: discharge function not available for this ThinkPad model." 1>&2 return 1 fi trap cancel_force_discharge INT # enable ^C hook # wait for start == while status not "discharging" while ! bat_discharging $bat_str; do sleep 0.5; done echo_debug "bat" "discharge_battery.running($bat_str)" # wait for completion == while status "discharging" while bat_discharging $bat_str; do clear echo "Currently discharging battery $bat_str:" # show current battery state if [ $tpsmapi -eq 0 ]; then # use tp_smapi bdir=$SMAPIDIR/$bat_str printf "voltage = %6s [mV]\n" "$(cat $bdir/voltage 2> /dev/null)" printf "remaining capacity = %6s [mWh]\n" "$(cat $bdir/remaining_capacity 2> /dev/null)" printf "remaining percent = %6s [%%]\n" "$(cat $bdir/remaining_percent 2> /dev/null)" printf "remaining time = %6s [min]\n" "$(cat $bdir/remaining_running_time_now 2> /dev/null)" printf "power = %6s [mW]\n" "$(cat $bdir/power_avg 2> /dev/null)" printf "state = %s\n" "$(cat $bdir/state 2> /dev/null)" get_force_discharge $bat_str; printf "force discharge = %s\n" "$?" else # use acpi bdir=$ACPIBATDIR/$bat_str if [ -d $bdir ]; then perl -e 'printf ("voltage = %6d [mV]\n", '$(catsysfd $bdir/voltage_now 0 2> /dev/null)' / 1000.0);' en=$(catsysfd $bdir/energy_now 0 2> /dev/null) perl -e 'printf ("remaining capacity = %6d [mWh]\n", '$en' / 1000.0);' ef=$(catsysfd $bdir/energy_full 0 2> /dev/null) if [ "$ef" != "0" ]; then perl -e 'printf ("remaining percent = %6d [%%]\n", 100.0 * '$en' / '$ef' );' else printf "remaining percent = not available [%]\n" fi pn=$(catsysfd $bdir/power_now 0 2> /dev/null) 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" "$(cat $bdir/status 2> /dev/null)" get_force_discharge $bat_str; printf "force discharge = %s\n" "$?" fi fi echo "Press Ctrl+C to cancel." sleep 5 done trap - INT # remove ^C hook # crappy ThinkPad E-series firmware may keep force_discharge active --> cancel it ! get_force_discharge $bat_str && do_force_discharge $bat_str 0 echo echo "Done: battery $bat_str was completely discharged." echo_debug "bat" "discharge_battery.complete($bat_str)" return 0 } # --- 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 http://www.thinkwiki.org/wiki/How_to_hotswap_UltraBay_devices local pwr optdrv syspath if [ "$1" = "1" ]; then pwr=${BAY_POWEROFF_ON_BAT:-0} else pwr=${BAY_POWEROFF_ON_AC:-0} fi # Run only if either explicitly enabled or forced [ "$pwr" = "1" ] || [ "$2" = "1" ] || return 0 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=/dev/${BAY_DEVICE:=sr0} if [ ! -b "$optdrv" ]; then echo_debug "pm" "poweroff_drivebay($1).no_opt_drive: $optdrv" [ "$2" = "1" ] && echo "No optical drive in bay ($optdrv)." return 0 else echo_debug "pm" "poweroff_drivebay($1): optdrv=$optdrv" echo -n "Powering off drive bay..." # Unmount media umount -l $optdrv > /dev/null 2>&1 # Sync drive sync sleep 1 # Power off drive $HDPARM -Y $optdrv > /dev/null 2>&1 sleep 5 # Unregister scsi device if syspath="$($UDEVADM info --query=path --name=$optdrv)"; then syspath="/sys${syspath%/block/*}" if [ "$syspath" != "/sys" ]; then echo_debug "pm" "poweroff_drivebay($1): syspath=$syspath" { printf '%s\n' "1" > $syspath/delete; } 2> /dev/null 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 { printf '%s\n' "1" > $dock/undock; } 2> /dev/null [ "$2" = "1" ] && echo "done." echo_debug "pm" "poweroff_drivebay($1).bay_powered_off" 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 mkdir -p $STATEDIR 2> /dev/null 1>&2 if ! check_is_docked; then { printf '%s\n' "off" > $BAYSTATEFILE; } 2> /dev/null echo_debug "pm" "suspend_drivebay($1): bay=off" else { printf '%s\n' "on" > $BAYSTATEFILE; } 2> /dev/null echo_debug "pm" "suspend_drivebay($1): bay=on" 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 [ "$(cat $BAYSTATEFILE 2> /dev/null)" = "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 -o $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.1/tlp-nop.in000066400000000000000000000012431323201726300137320ustar00rootroot00000000000000#!/bin/sh # tlp - if tlp is enabled, override corresponding pm-utils script # in /usr/lib/pm-utils/power.d/ CONFFILE=@TLP_CONF@ LIBDIR='@TLP_PLIB@' if [ -d "$LIBDIR/power.d" ]; then # pm-utils script dir exists blocked="$LIBDIR/power.d/${0##*/}" if [ -x "$blocked" ]; then # overridable pm-utils script exists --> check if TLP enabled if [ -e "$CONFFILE" ] && . "$CONFFILE" && [ "$TLP_ENABLE" = '1' ]; then # TLP is enabled --> disable $blocked echo "Notice: '${blocked}' disabled by TLP." else exec "$blocked" $* fi fi # else: nothing to disable -> don't read $CONFFILE fi exit 0 TLP-1.1/tlp-pcilist000066400000000000000000000036431323201726300142060ustar00rootroot00000000000000#!/usr/bin/perl # tlp-pcilist - list pci devices with runtime pm mode and device class # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. package tlp_pcilist; use strict; use warnings; # Read content from a sysfile # $_[0]: input file # return: content / empty string if nonexistent or not readable sub catsysf { my $sysval = ""; if (open SYSF, "$_[0]") { chomp ($sysval = ); 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 $driver = ""; if ( open (SYSF, "$_[0]/uevent") ) { # read file line by line while () { # match line content and return DRIVER= value if ( s/^DRIVER=(.*)/$1/ ) { chomp ($driver = $_); last; # break loop } } close (SYSF); } return $driver } # Output device list with Runtime PM mode and device class foreach (`lspci -m`) { # parse lspci output: get short PCI(e) id and long description of device my ($dev, $classdesc) = /(\S+) \"(.+?)\"/; # join device path my $devp = "/sys/bus/pci/devices/0000:$dev"; # control file for Runtime PM my $devc = "$devp/power/control"; # get device class my $class = catsysf ("$devp/class"); # get device driver my $driver = getdriver ("$devp") || "no driver"; if (-f $devc) { # control file exists # get Runtime PM mode my $pmode = catsysf ("$devc"); # output pm mode and device data printf "%s/power/control = %-4s (%s, %s, %s)\n", $devp, $pmode, $class, $classdesc, $driver; } else { # control file missing --> output device data only printf "%s/power/control = (not available) (%s, %s, %s)\n", $devp, $class, $classdesc, $driver; } } TLP-1.1/tlp-rdw-nm.in000066400000000000000000000124031323201726300143420ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - network manager dispatcher hook: # enable/disable radios on ifup/ifdown # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions tlp-rf-func" # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$lib 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=$(cat $RUNDIR/${1}.itype 2> /dev/null); rc=$? rm -f $RUNDIR/${1}.itype return $rc } # --- MAIN read_defaults check_tlp_enabled || exit 0 add_sbin2path # Get args iface="$1" action="$2" itype="" # Quit for invalid interfaces [ -n "$iface" ] && [ "$iface" != "none" ] || exit 0 # Quit for actions other than "up" and "down" [ "$action" = "up" ] || [ "$action" = "down" ] || 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" 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" exit 0 fi fi echo_debug "nm" "+++ rdw_nm($iface).$action" if [ -n "$addpath" ]; then echo_debug "path" "PATH=$oldpath[$addpath]" else 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 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 exit 0 TLP-1.1/tlp-rdw-udev.in000066400000000000000000000054651323201726300147050ustar00rootroot00000000000000#!/bin/sh # tlp-rdw - handle dock/undock events # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions tlp-rf-func" # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$lib done # --- MAIN read_defaults check_tlp_enabled || 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", exit if not type=$(cat $ddev/type 2> /dev/null) [ "$type" = "dock_station" ] || exit 0 docked=$(cat $ddev/docked 2> /dev/null) action=$EVENT echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev type=$type docked=$docked syspwr=$syspwr" ;; usb_dock) case $ACTION in add) action="dock" ;; remove) action="undock" ;; esac echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev syspwr=$syspwr" ;; *) 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" 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 exit 0 TLP-1.1/tlp-rdw.rules.in000066400000000000000000000030301323201726300150570ustar00rootroot00000000000000# tlp-rdw - udev rules # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Dock/undock events # ThinkPad Advanced Mini Dock (and all older models), ThinkPad UltraBase ACTION=="change", SUBSYSTEM=="platform", KERNEL=="dock.*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p dock" # ThinkPad Mini Dock (Plus) Series 3 ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/100a/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Ultra Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1010/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Pro Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/1012/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad Basic Dock # *** TODO: yet unknown *** # ThinkPad OneLink Pro Dock (USB3 Gigabit LAN interface) ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/304b/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/304f/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Dock ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/3049/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" # ThinkPad OneLink Dock Plus ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/3054/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock" TLP-1.1/tlp-rf-func000066400000000000000000000350601323201726300140750ustar00rootroot00000000000000#!/bin/sh # tlp - rf switch functions # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # ---------------------------------------------------------------------------- # Constants readonly RFKILL="rfkill" readonly RFKD="/dev/rfkill" readonly ALLDEV="bluetooth wifi wwan" # ---------------------------------------------------------------------------- # Functions get_devc () { # get control device for radio type # $1: rftype bluetooth/wifi/wwan # retval $devc: sysdev, # $rfkdev: 1/0=is/is not an rfkill device, # $devon, $devoff: value to write directly to the sysdev # to achieve the desired switch state local i check_sysfs "get_devc" "/sys/class/rfkill" # preset retvals devc="" devs=254 rfkdev="1" devon="1" devoff="0" case $1 in wwan|bluetooth) for i in /sys/class/rfkill/rfkill* ; do if [ "$(cat $i/type 2> /dev/null)" = "$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 [ "$(cat $i/type 2> /dev/null)" = "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=$(cat $devc 2> /dev/null) 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 nm $1 $2 > /dev/null 2>&1; rc=$? echo_debug "rf" "invoke_nmcli($1, $2).nm: rc=$rc" if [ $rc -eq 2 ]; then # option "nm" is invalid for this NM version, try "radio" instead $NMCLI radio $1 $2 > /dev/null 2>&1; rc=$? echo_debug "rf" "invoke_nmcli($1, $2).radio: rc=$rc" fi 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 echo_debug "rf" "device_switch($1, $2).devc" { printf '%s' $devn > $devc; } 2> /dev/null # record device state after direct write get_devs $1 else err_no_root_priv "device_switch($1, $2).devc" fi fi fi # rfkill not disabled or bluetooth fi # states did not match # wifi, wwan: after rkfill (default) -- notify NM of desired state if [ "$X_USE_NMCLI" != "0" ] && [ "$1" != "bluetooth" ]; then case $newst in 1) invoke_nmcli $1 on ;; 0) invoke_nmcli $1 off ;; esac # record final device state get_devs $1 fi # quit if device state is hard blocked or invalid if [ $devs -ge 2 ]; then case $devs in 2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$devs" ;; *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$devs" ;; esac return $devs fi # compare old and new device state if [ "$curst" = "$devs" ]; then # state did not change echo_debug "rf" "device_switch($1, $2).no_change: rc=4" return 4 else echo_debug "rf" "device_switch($1, $2).ok: rc=0" return 0 fi } echo_device_state () { # print radio type state -- $1: rftype, $2: state case $1 in bluetooth) devstr="bluetooth" ;; wifi) devstr="wifi " ;; wwan) devstr="wwan " ;; *) devstr=$1 ;; esac case $2 in 0) echo "$devstr = off (software)" ;; 1) echo "$devstr = on" ;; 2) echo "$devstr = off (hardware)" ;; 254) echo "$devstr = none (no device)" ;; *) echo "$devstr = invalid state" esac return 0 } save_device_states () { # save radio states -- $1: list of rftypes local dev local devlist="${1:-$ALLDEV}" # when arg empty -> use all echo_debug "rf" "save_device_states($devlist)" # create empty state file mkdir -p $STATEDIR 2> /dev/null 1>&2 { : > $RFSTATEFILE; } 2> /dev/null # 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 done return 0 } restore_device_states () { # restore radio type states local sline echo_debug "rf" "restore_device_states" if [ -f $RFSTATEFILE ]; then # read state file while read sline; do set -- $sline # read dev, state into $1, $2 device_switch $1 $2 done < $RFSTATEFILE return 0 else return 1 fi } 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:-0} else restore=0 fi if [ "$restore" = "1" ]; then # "save/restore" mode echo_debug "rf" "set_radio_device_states($1).restore" case $1 in start) restore_device_states if [ $? = 0 ]; then echo "Radio device states restored." else echo "No saved radio device states found." fi ;; stop) 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 case $dev in bluetooth) any_bluetooth_in_use ;; wifi) any_wifi_in_use ;; wwan) any_wwan_in_use ;; esac # if device is not connected and not in list yet --> add to disable list [ $? = 0 ] || 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" ] || echo -n " $dev" device_switch $dev off done [ "$quiet" = "1" ] || echo "." fi # Enable configured radios if [ -n "$devs2enable" ]; then if [ "$1" = "radiosw" ]; then # radiosw mode: disable radios not listed for dev in bluetooth wifi wwan; do if ! wordinlist "$dev" "$devs2enable"; then device_switch $dev off fi done else # start mode: enable listed radios [ "$quiet" = "1" ] || echo -n "Enabling radios:" for dev in $devs2enable; do [ "$quiet" = "1" ] || echo -n " $dev" device_switch $dev on done [ "$quiet" = "1" ] || echo "." fi fi # clean up: discard state file rm -f $RFSTATEFILE 2> /dev/null fi return 0 } TLP-1.1/tlp-rf.in000066400000000000000000000022331323201726300135450ustar00rootroot00000000000000#!/bin/sh # tlp - switch bluetooth/wifi/wwan on/off # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions tlp-rf-func" # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$lib done # --- MAIN read_defaults add_sbin2path self=${0##*/} case $self in bluetooth|wifi|wwan) case $1 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 exit 1 ;; esac exit 0 TLP-1.1/tlp-run-on.in000066400000000000000000000016461323201726300143630ustar00rootroot00000000000000#!/bin/sh # tlp - run commands depending on power source # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions" # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$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_state; then $cmd $@ fi ;; run-on-bat) if ! get_power_state; then $cmd $@ fi ;; *) echo "Error: unknown mode $self." 1>&2 exit 1 ;; esac TLP-1.1/tlp-sleep.service.in000066400000000000000000000006331323201726300157070ustar00rootroot00000000000000# tlp - systemd suspend/resume service # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. [Unit] Description=TLP suspend/resume Before=sleep.target StopWhenUnneeded=yes Documentation=http://linrunner.de/tlp [Service] Type=oneshot RemainAfterExit=yes ExecStart=@TLP_SBIN@/tlp suspend ExecStop=@TLP_SBIN@/tlp resume [Install] WantedBy=sleep.target TLP-1.1/tlp-stat.in000066400000000000000000001364371323201726300141270ustar00rootroot00000000000000#!/bin/sh # tlp - display power save and usb autosuspend status # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions tlp-rf-func" readonly TLPUSB=tlp-usblist readonly TLPPCI=tlp-pcilist readonly SMARTCTL=smartctl readonly LSBREL=lsb_release readonly ASPM=/sys/module/pcie_aspm/parameters/policy readonly NMIWD=/proc/sys/kernel/nmi_watchdog readonly WQPE=/sys/module/workqueue/parameters/power_efficient readonly IBMTHERMAL=/proc/acpi/ibm/thermal readonly CORETEMP_DIRS=" /sys/devices/platform/coretemp.0 /sys/devices/platform/coretemp.0/hwmon/hwmon*" readonly IBMFAN=/proc/acpi/ibm/fan readonly HWMONFAN_DIRS=" /sys/class/hwmon/hwmon*/device /sys/class/hwmon/hwmon*" readonly JOURNALCTL=journalctl readonly DEBUGLOG=/var/log/debug readonly SYSTEMD_SERVICES="tlp.service tlp-sleep.service" readonly RFKILL_SERVICES="systemd-rfkill.service" readonly EFID=/sys/firmware/efi readonly RE_ATA_ERROR='ata[0-9]+: SError: {.*CommWake }' # --- Variables needs_root_priv= show_all=1 show_bat=0 show_conf=0 show_disk=0 show_graf=0 show_pcie=0 show_pev=0 show_proc=0 show_psup=0 show_rfkill=0 show_system=0 show_temp=0 show_trace=0 show_usb=0 show_verbose=0 show_warn=0 no_runtimepm=0 # --- Functions printparm () { # formatted output of sysfile - general # $1: format, $2: sysfile, $3: namsg, $4: cutoff local format="$1" local sysf="$2" local namsg="$3" local cutoff="$4" local val="" if [ -f $sysf ]; then # sysfile exists val=$(cat $sysf 2> /dev/null) if [ $? = 0 ]; then # sysfile read successful if [ -n "$cutoff" ]; then val=${val%$cutoff} fi 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 "s/##(.*)##/($namsg)/" | sed -r "s/\[.*\]//" ) else # _ = skip sysf="" fi else # empty n/a text, use default text format=$( echo $format | sed -r "s/##(.*)##/(not available)/" | sed -r "s/\[.*\]//" ) fi # output n/a text or skip [ -n "$sysf" ] && printf "$format\n" "$sysf" else # non empty value: strip delimiters from format str format=$( echo $format | sed -r "s/##(.*)##/\1/" ) printf "$format\n" "$sysf" "$val" fi return 0 } printparm_i915 () { # formatted output of sysfile - i915 kernel module variant # $1: sysfile; $2: alternative; $3: 1=psr/0=other local sysf val # Check if sysfile or alternative exist if [ -f $1 ]; then sysf=$1 elif [ -f $2 ]; then sysf=$2 else sysf="" fi if [ -n "$sysf" ]; then # sysfile exists, get content val=$(cat $sysf 2> /dev/null) if [ $? = 0 ]; then # sysfile was readable, output content printf "%-44s = %2d " "$sysf" "$val" # Explain content if [ "$val" = "-1" ]; then echo "(use per-chip default)" else echo -n "(" if [ "$3" = "1" ]; then # enable_psr case $val in 0) echo -n "disabled" ;; 1) echo -n "enabled" ;; 2) echo -n "force link-standby mode" ;; 3) echo -n "force link-off mode" ;; *) echo -n "unknown" ;; esac else # other parms if [ $(( $val & 1 )) -ne 0 ]; then echo -n "enabled" else echo -n "disabled" fi [ $(( $val & 2 )) -ne 0 ] && echo -n " + deep" [ $(( $val & 4 )) -ne 0 ] && echo -n " + deepest" fi echo ")" fi else # sysfile was not readable printf "%-44s = (not available)\n" "$sysf" fi fi return 0 } printsysf () { # output a sysfile # $1: format; $2: sysfile local val val=$(cat $2 2> /dev/null) if [ $? = 0 ]; then # sysfile readable printf "$1" "$val" else # sysfile not readable printf "$1" "(not available)" fi return 0 } print_tp_batstate () { # print battery charging state with an explanation when # a threshold inhibits charging # $1: sysfile; $2: 1=ThinkPad battery/0=other local sysf val # Check if bat state sysfile exists if [ -f $1 ]; then sysf=$1 else sysf="" fi if [ -n "$sysf" ]; then # bat state sysfile exists, get content val=$(cat $sysf 2> /dev/null) if [ $? = 0 ]; then # sysfile was readable, output content printf "%-59s = %s" "$sysf" "$val" # Explain content if necessary case $val in Unknown) # "Unknown" means a threshold forbids charging printf " (threshold effective)\n" ;; *) # Nothing to do printf "\n" ;; esac else # sysfile was not readable printf "%-59s = (not available)\n" "$sysf" fi else # sysfile nonexistent printf "%-59s = (not available)\n" "$1" fi return 0 } print_tpacpi_thresholds () { # formatted output of ThinkPad charging thresholds # - tpcapi-bat variant # $1: BAT0/BAT1; $2: bat # = 1/2 local start_thresh stop_thresh force read_tpacpi_threshold ST $2 start_thresh=$? if [ $start_thresh -ne 255 ]; then [ $start_thresh -eq 0 ] && start_thresh=96 printf "%-59s = %6d [%%]\n" "tpacpi-bat.${1}.startThreshold" "$start_thresh" else printf "%-59s = (not available)\n" "tpacpi-bat.${1}.startThreshold" fi read_tpacpi_threshold SP $2 stop_thresh=$? if [ $stop_thresh -ne 255 ]; then [ $stop_thresh -eq 0 ] && stop_thresh=100 printf "%-59s = %6d [%%]\n" "tpacpi-bat.${1}.stopThreshold" "$stop_thresh" else printf "%-59s = (not available)\n" "tpacpi-bat.${1}.stopThreshold" fi get_force_discharge $2; force=$? if [ $force -ne 2 ]; then printf "%-59s = %6d\n" "tpacpi-bat.${1}.forceDischarge" "$force" else printf "%-59s = %s\n" "tpacpi-bat.${1}.forceDischarge" "(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 case "$(cat $PWRRUNFILE 2> /dev/null)" in 0) echo "AC" ;; 1) echo "battery" ;; *) echo "unknown" ;; esac return 0 } 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 echo $( dmesg | egrep -c "${RE_ATA_ERROR}" 2> /dev/null ) else # no values in question configured echo "0" fi return 0 } # @stdout glob_files ( glob_pattern, dir[, dir...] ) # # Nested for-loop that applies a glob expression to several directories # (or file path prefixes) and prints matching file paths to stdout. # glob_files () { [ -n "${1-}" ] || return 64 local glob_pattern file_iter glob_pattern="${1}" while shift && [ $# -gt 0 ]; do for file_iter in ${1}${glob_pattern}; do [ ! -f "${file_iter}" ] || echo "${file_iter}" done done } read_args () { # read command line arguments for a in $*; do case $a in "-b"|"--battery") show_all=0 show_bat=1 needs_root_priv=1 ;; "-c"|"--config") show_all=0 show_conf=1 : ${needs_root_priv:=0} ;; "-d"|"--disk") show_all=0 show_disk=1 needs_root_priv=1 ;; "-e"|"--pcie") show_all=0 show_pcie=1 : ${needs_root_priv:=0} ;; "-g"|"--graphics") show_all=0 show_graf=1 : ${needs_root_priv:=0} ;; "-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 ;; *) echo "Usage: tlp-stat [ -b | --battery | -c | --config |" echo " -d | --disk | -e | --pcie |" echo " -g | --graphics | -p | --processor |" echo " -r | --rfkill | -s | --system |" echo " -t | --temp | -u | --usb |" echo " -w | --warn | -v | --verbose |" echo " -P | --pev | | --psup |" echo " -T | --trace ]" exit 3 ;; esac done } # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$lib done # --- MAIN add_sbin2path read_args $* : ${needs_root_priv:=1} # inhibit trace output nodebug=1 # check for and read conffile read_defaults conf_present=$? # check prerequisites if [ "$needs_root_priv" = "1" ]; then check_root load_modules $MOD_MSR $MOD_TEMP check_thinkpad check_tpacpi check_tpsmapi fi echo "--- TLP $TLPVER --------------------------------------------" echo # --- show configuration if [ "$show_conf" = "1" ] || [ "$show_all" = "1" ]; then if [ $conf_present -eq 0 ]; then echo "+++ Configured Settings: $CONFFILE" egrep -v '^#|^\s*$' $CONFFILE echo else echo "Error: config file $CONFFILE not present." 1>&2 echo fi fi # show_conf if [ "$show_system" = "1" ] || [ "$show_all" = "1" ] ; then # --- show system info echo "+++ System Info" echo "System = $( read_dmi sys_vendor product_version product_name )" echo "BIOS = $( read_dmi bios_version )" # --- show release & kernel info cmd_exists $LSBREL && echo "Release = $($LSBREL -d -s)" echo "Kernel = $(uname -r -m -v)" printparm "%-14s = %s" /proc/cmdline # --- show init system info if check_systemd; then echo "Init system = systemd $(systemd --version 2> /dev/null | sed -rn 's/systemd ([0-9]+)/v\1/p')" elif check_upstart; then echo "Init system = upstart" elif check_openrc; then echo "Init system = openrc" else echo "Init system = sysvinit" fi if [ -d $EFID ]; then echo "Boot mode = UEFI" else echo "Boot mode = BIOS (CSM, Legacy)" fi echo # --- show TLP status echo "+++ TLP Status" if [ "$TLP_ENABLE" = "1" ]; then printf "State = enabled\n" else printf "State = disabled\n" fi # --- show last invocation time printf "Last run = %s\n" "$(print_file_modtime_and_age ${LOCKFILE}_tlp)" # --- 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 echo # -- check systemd service units status if check_systemd; then cnt=0 for su in $SYSTEMD_SERVICES; do if ! $SYSTEMCTL is-enabled $su > /dev/null 2>&1 ; then echo "Notice: $su is not enabled -- invoke \"systemctl enable $su\" to correct this!" cnt=$((cnt+1)) fi done for su in $RFKILL_SERVICES; do ise=$($SYSTEMCTL is-enabled $su 2> /dev/null) if [ -n "$ise" ] && [ "$ise" != "masked" ]; then echo "Notice: $su is not masked -- invoke \"systemctl mask $su\" to correct this!" cnt=$((cnt+1)) fi done [ $cnt -gt 0 ] && echo fi # -- show warning if l-m-t detected check_laptop_mode_tools fi # show_system if [ "$show_proc" = "1" ] || [ "$show_all" = "1" ]; then # --- show cpu info echo "+++ Processor" sed -rn 's/model name[ \t]+: (.+)/CPU model = \1/p' /proc/cpuinfo | head -1 echo # -- show scaling gov and freq info for cpuf in /sys/devices/system/cpu/cpu*/cpufreq; do if [ -f $cpuf/scaling_driver ]; then printparm "%-54s = ##%s##" $cpuf/scaling_driver printparm "%-54s = ##%s##" $cpuf/scaling_governor printparm "%s = ##%s##" $cpuf/scaling_available_governors _ if [ -f $cpuf/scaling_min_freq ]; then printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_min_freq" "$(cat $cpuf/scaling_min_freq 2> /dev/null)" fi if [ -f $cpuf/scaling_max_freq ]; then printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_max_freq" "$(cat $cpuf/scaling_max_freq 2> /dev/null)" fi if [ -f $cpuf/scaling_available_frequencies ]; then printf "%s = " "$cpuf/scaling_available_frequencies" for freq in $(cat $cpuf/scaling_available_frequencies 2> /dev/null); do printf "%s " "$freq" done printf "[kHz]\n" fi if [ -f $cpuf/energy_performance_preference ]; then printparm "%s = ##%s##" $cpuf/energy_performance_preference fi if [ -f $cpuf/energy_performance_available_preferences ]; then printparm "%s = ##%s##" $cpuf/energy_performance_available_preferences fi printf "\n" fi done check_intel_pstate if [ $intel_pstate -eq 1 ]; then # show Intel P-state info printparm "%-54s = ##%3d## [%%]" $CPU_MIN_PERF_PCT printparm "%-54s = ##%3d## [%%]" $CPU_MAX_PERF_PCT printparm "%-54s = ##%3d##" $CPU_TURBO_PSTATE printparm "%-54s = ##%3d## [%%]" $INTEL_PSTATED/turbo_pct printparm "%-54s = ##%3d##" $INTEL_PSTATED/num_pstates elif [ -f $CPU_BOOST_ALL_CTRL ]; then # show turbo boost info get_sysval $CPU_BOOST_ALL_CTRL; boost=$? # simple test for attribute "w" doesn't work, so actually write if { printf '%s\n' "$boost" > $CPU_BOOST_ALL_CTRL; } 2> /dev/null; then printparm "%-54s = ##%d##" $CPU_BOOST_ALL_CTRL else printparm "%-54s = ##%d## (cpu not supported)" $CPU_BOOST_ALL_CTRL fi else printparm "%-54s = (not available)" $CPU_BOOST_ALL_CTRL fi # --- show sched power save info for pool in mc smp smt; do sdev="/sys/devices/system/cpu/sched_${pool}_power_savings" printparm "%-54s = ##%d##" $sdev _ done echo # --- show x86 energy perf policy info if cmd_exists $ENERGYPERF; then # check CPU support $ENERGYPERF -r > /dev/null 2>&1 case $? in 0) # parse x86_energy_perf_policy output: # - replace numbers with descriptive strings # - remove ":" # - indent and align $ENERGYPERF -r 2>/dev/null | \ sed -r 's/://; s/(0x0000000000000000|EPB 0)/performance/; s/(0x0000000000000004|EPB 4)/balance-performance/; s/(0x0000000000000006|EPB 6)/default/; s/(0x0000000000000008|EPB 8)/balance-power/; s/(0x000000000000000f|EPB 15)/power/' | \ awk '{ printf "x86_energy_perf_policy.%-31s = %s %s\n", $1, $2, $3; }' ;; 1) echo "x86_energy_perf_policy: unsupported CPU." ;; 2) echo "x86_energy_perf_policy: program for your kernel not installed." ;; *) echo "x86_energy_perf_policy: not available." ;; esac echo else echo "x86_energy_perf_policy: program not installed." echo fi # --- show workqueue power efficient status printparm "%-54s = ##%s##" $WQPE # --- show nmi watchdog printparm "%-54s = ##%d##" $NMIWD echo # --- show voltages echo "+++ Undervolting" phc_avail=0 for cpuf in /sys/devices/system/cpu/cpu*/cpufreq; do if [ -f $cpuf/phc_controls ]; then phc_avail=1 printparm "%-58s = ##%s##" $cpuf/phc_controls printparm "%-58s = ##%s##" $cpuf/phc_default_controls echo fi done if [ $phc_avail = 0 ]; then echo "PHC kernel not available." echo fi fi # show_proc if [ "$show_temp" = "1" ] || [ "$show_all" = "1" ]; then # --- show temperatures echo "+++ Temperatures" if [ -f $IBMTHERMAL ]; then # use thinkpad-specific sysfile echo "$IBMTHERMAL = $(cat $IBMTHERMAL 2> /dev/null | cut -f2 ) [°C]" else # use sensors cmax=0 for sens in $(glob_files '/temp?*_input' $CORETEMP_DIRS); do if grep -q -- 'Physical' ${sens%input}label 2>/dev/null; then # package info is available -> ignore remaining sensors read -r cmax < $sens break else # core info -> find max value read -r ctemp < $sens && [ $ctemp -gt $cmax ] && cmax=$ctemp fi done if [ $cmax -gt 0 ]; then perl -e 'printf ("CPU temp = %5d [°C]\n", '$cmax' / 1000.0);' fi fi # --- show fan speed if is_thinkpad && [ -f $IBMFAN ]; then # use thinkpad-specific sysfile awk '$1 ~ /speed:/ { printf "'$IBMFAN' = %5d [/min]\n", $2 }' $IBMFAN else # use hwmon have_any_fan= for fan in $(glob_files '/fan?*_input' $HWMONFAN_DIRS); do if read -r fan_speed < $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 form hdparm echo "+++ Storage Devices" : ${DISK_DEVICES:=${DEFAULT_DISK_DEVICES}} for dev in $DISK_DEVICES; do # iterate all devices get_disk_dev $dev if [ -b /dev/$disk_dev ]; then get_disk_state $disk_dev check_disk_hdparm_cap $disk_dev if [ $? = 0 ]; then echo "/dev/$disk_dev:" if [ -n "$disk_id" ]; then echo " Disk ID = $disk_id" fi echo -n " Model = " echo_disk_model $disk_dev echo -n " Firmware = " echo_disk_firmware $disk_dev get_disk_apm_level $disk_dev apm=$? echo -n " APM Level = " case $apm in 0|255) echo "none/disabled" ;; *) echo $apm ;; esac echo " Status = $disk_state" get_disk_trim_capability $disk_dev trim=$? case $trim in 0) echo " TRIM = not supported" ;; 1) echo " TRIM = supported" ;; esac if [ -f /sys/block/$disk_dev/queue/scheduler ]; then sched="$(cat /sys/block/$disk_dev/queue/scheduler 2> /dev/null | sed -r 's/.*\[(.*)\].*/\1/')" if [ "$sched" = "none" ] && [ -d /sys/block/$disk_dev/mq ]; then sched="blk-mq" fi printf " Scheduler = %s\n" "$sched" fi ddev=/sys/block/$disk_dev/device/power if [ -f $ddev/control ]; then echo printsysf " Runtime PM: control = %s, " $ddev/control printsysf "autosuspend_delay = %4s\n" $ddev/autosuspend_delay_ms fi if cmd_exists $SMARTCTL ; then # --- show SMART data echo echo " SMART info:" $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, "[%]" }' fi echo # restore standby state [ "$disk_state" = "standby" ] && spindown_disk $disk_dev fi fi done echo # --- show sata alpm mode echo "+++ AHCI Link Power Management (ALPM)" if stat -t /sys/class/scsi_host/host*/link_power_management_policy > /dev/null 2>&1; then for i in /sys/class/scsi_host/host* ; do printparm "%-56s = ##%s##" $i/link_power_management_policy _ done else echo "No AHCI-enabled host controller detected." fi echo # --- show ahci runtime pm if stat -t ${AHCID}/power > /dev/null 2>&1; then echo "+++ AHCI Host Controller Runtime Power Management" for dev in ${AHCID}/power ; do printparm "%-40s = ##%s##" $dev/control done echo fi # -- show docks cnt=0 for dock in $DOCKGLOB; do [ ! -d $dock ] && break # no dock/bay detected # dock/bay detected, print header [ $cnt -eq 0 ] && echo "+++ Docks and Device Bays" cnt=$((cnt+1)) # get dock type { read -r dock_type < $dock/type; } 2>/dev/null # get dock state if check_is_docked; then # docked case $dock_type in ata_bay) dock_state="drive present" ;; battery_bay) dock_state="battery present" ;; dock_station) dock_state="docked" ;; *) dock_state="docked" dock_type="unknown" ;; esac else # not docked case $dock_type in ata_bay) dock_state="no drive (or powered off)" ;; battery_bay) dock_state="no battery " ;; dock_station) dock_state="undocked" ;; *) dock_state="undocked" dock_type="unknown" ;; esac fi # print dock data printf "%s: %-13s = %s\n" "$dock" "$dock_type" "$dock_state" done [ $cnt -gt 0 ] && echo fi # show_disk if [ "$show_all" = "1" ]; then # --- show pcie aspm state echo "+++ PCIe Active State Power Management" if [ -f $ASPM ]; then pol=$(cat $ASPM 2> /dev/null | sed -r 's/.*\[(.*)\].*/\1/') { printf '%s' "$pol" > $ASPM; } 2> /dev/null if [ $? = 0 ]; then echo "$ASPM = $pol" else echo "$ASPM = $pol (using bios preferences)" fi else echo "$ASPM = (not available)" fi echo fi # show_all if [ "$show_graf" = "1" ] || [ "$show_all" = "1" ]; then # --- show i915 power mgmt for card in $I915D; do if [ -d $card ]; then echo "+++ Intel Graphics" printparm_i915 $card/powersave printparm_i915 $card/enable_rc6 $card/i915_enable_rc6 printparm_i915 $card/enable_dc printparm_i915 $card/enable_fbc $card/i915_enable_fbc printparm_i915 $card/enable_psr "" 1 printparm_i915 $card/lvds_downclock printparm_i915 $card/modeset printparm_i915 $card/semaphores echo fi done # --- show radeon power profile or dpm state if [ -d $RADD ]; then for card in /sys/class/drm/card[0-9]/device ; do if [ -f $card/power_dpm_state ] && [ -f $card/power_dpm_force_performance_level ]; then # Use new radeon dpm state echo "+++ Radeon Graphics" printparm "%-25s = ##%s##" $card/power_dpm_state printparm "%-25s = ##%s##" $card/power_dpm_force_performance_level echo break elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then # Use old radeon power profile echo "+++ Radeon Graphics" printparm "%-25s = ##%s##" $card/power_method printparm "%-25s = ##%s##" $card/power_profile echo break fi done fi fi # show_graf if [ "$show_rfkill" = "1" ] || [ "$show_all" = "1" ]; then echo "+++ Wireless" # --- show rfkill state for i in bluetooth wifi wwan; do get_devc $i get_devs $i echo_device_state $i $devs done echo ifshown=0 # --- show bluetooth get_bluetooth_ifaces for iface in $bifaces; do if [ -n "$iface" ]; then ifshown=1 # get bluetooth driver get_bluetooth_driver $iface printf "%-30s: bluetooth, " "$iface($bluetoothdrv)" if bluetooth_in_use $iface; then echo "connected" else echo "not connected" fi fi done # --- show wifi data get_wifi_ifaces for iface in $wifaces; do if [ -n "$iface" ]; then ifshown=1 # get wifi power mgmt state wifipm="" if [ "$X_DONT_USE_IW" != "1" ] && cmd_exists $IW; then # try with iw first wifipm=$($IW dev $iface get power_save 2> /dev/null | \ grep "Power save" | \ sed -r 's/.*Power save: (on|off).*/\1/') fi if cmd_exists $IWC; then if [ -z "$wifipm" ]; then # iw did not succeed or iw not installed -> try with iwconfig wifipm=$($IWC $iface 2> /dev/null | \ grep "Power Management" | \ sed -r 's/.*Power Management:(on|off).*/\1/') fi fi # get wifi driver get_wifi_driver $iface printf "%-30s: wifi, " "$iface($wifidrv)" if wireless_in_use $iface; then printf "connected, " else printf "not connected, " fi printf "power management = " case $wifipm in on|off) printf "$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 runtime pm echo "+++ Runtime Power Management" echo "Device blacklist = ${RUNTIME_PM_BLACKLIST:=(not configured)}" pmdbl="${RUNTIME_PM_DRIVER_BLACKLIST-${DEFAULT_PM_DRIVER_BLACKLIST} (default)}" echo "Driver blacklist = ${pmdbl:-(disabled)}" echo if cmd_exists $TLPPCI; then $TLPPCI [ $? -eq 4 ] && no_runtimepm=1 else echo "Error: missing subcommand $TLPPCI." 1>&2 fi echo fi # show_pcie if [ "$show_usb" = "1" ] || [ "$show_all" = "1" ]; then # -- show usb autosuspend echo "+++ USB" if [ "$USB_AUTOSUSPEND" = "1" ]; then echo "Autosuspend = enabled" else echo "Autosuspend = disabled" fi echo "Device whitelist = ${USB_WHITELIST:=(not configured)}" echo "Device blacklist = ${USB_BLACKLIST:=(not configured)}" if [ "${USB_BLACKLIST_BTUSB:-0}" = "1" ]; then echo "Bluetooth blacklist = enabled" else echo "Bluetooth blacklist = disabled" fi if [ "${USB_BLACKLIST_PHONE:-0}" = "1" ]; then echo "Phone blacklist = enabled" else echo "Phone blacklist = disabled" fi if [ "${USB_BLACKLIST_WWAN:-1}" = "1" ]; then echo "WWAN blacklist = enabled" else echo "WWAN blacklist = disabled" fi if [ -n "$USB_DRIVER_BLACKLIST" ] && [ "$USB_DRIVER_BLACKLIST" != "usbhid" ]; then echo "Notice: USB_DRIVER_BLACKLIST is no longer supported, use USB_BLACKLIST." fi echo if cmd_exists $TLPUSB; then $TLPUSB [ $? -eq 4 ] && no_runtimepm=1 else echo "Error: missing subcommand $TLPUSB." 1>&2 fi echo fi # show_usb if [ "$show_bat" = "1" ] || [ "$show_all" = "1" ]; then # --- show battery info & charge thresholds bcnt=0 efsum=0 ensum=0 # --- show availability of ThinkPad battery features if is_thinkpad; then echo "+++ ThinkPad Battery Features" echo -n "tp-smapi = " case $tpsmapi in 0) echo "active" ;; 2) echo "inactive (kernel module 'tp_smapi' load error)" ;; 127) echo "inactive (kernel module 'tp_smapi' not installed)" ;; 255) echo "inactive (unsupported hardware)" ;; *) echo "unknown status" esac echo -n "tpacpi-bat = " case $tpacpi in 0) echo "active" ;; 2) echo "inactive (kernel module 'acpi_call' load error)" ;; 4) echo "inactive (disabled by user configuration)" ;; 127) echo "inactive (kernel module 'acpi_call' not installed)" ;; 255) echo "inactive (unsupported hardware)" ;; esac echo fi if [ $tpsmapi -eq 0 ]; then # it's a ThinkPad with tp-smapi for batd in $SMAPIDIR/BAT[01]; do if [ -d $batd ]; then batt=${batd##/*/} if check_tp_battery $batt; then # battery is present case $bat_idx in 1) echo "+++ ThinkPad Battery Status: $batt (Main / Internal)" ;; 2) echo "+++ ThinkPad Battery Status: $batt (Ultrabay / Slice / Replaceable)" ;; 0) echo "+++ ThinkPad Battery Status: $batt" ;; esac printparm "%-59s = ##%s##" $batd/manufacturer printparm "%-59s = ##%s##" $batd/model printparm "%-59s = ##%s##" $batd/manufacture_date printparm "%-59s = ##%s##" $batd/first_use_date printparm "%-59s = ##%6d##" $batd/cycle_count if [ -f $batd/temperature ]; then perl -e 'printf ("%-59s = %6d [°C]\n", "'$batd/temperature'", '$(catsysfd $batd/temperature 0)' / 1000.0);' fi printparm "%-59s = ##%6d## [mWh]" $batd/design_capacity printparm "%-59s = ##%6d## [mWh]" $batd/last_full_capacity printparm "%-59s = ##%6d## [mWh]" $batd/remaining_capacity printparm "%-59s = ##%6d## [%%]" $batd/remaining_percent printparm "%-59s = ##%6s## [min]" $batd/remaining_running_time_now printparm "%-59s = ##%6s## [min]" $batd/remaining_charging_time printparm "%-59s = ##%6d## [mW]" $batd/power_now printparm "%-59s = ##%6d## [mW]" $batd/power_avg print_tp_batstate $batd/state echo if [ $show_verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $batd/design_voltage printparm "%-59s = ##%6s## [mV]" $batd/voltage printparm "%-59s = ##%6s## [mV]" $batd/group0_voltage printparm "%-59s = ##%6s## [mV]" $batd/group1_voltage printparm "%-59s = ##%6s## [mV]" $batd/group2_voltage printparm "%-59s = ##%6s## [mV]" $batd/group3_voltage echo fi if [ $tpacpi -eq 0 ]; then # --- show ThinkPad charge thresholds via tpacpi-bat print_tpacpi_thresholds $batt $bat_idx else # show thresholds via tp-smapi printparm "%-59s = ##%6d## [%%]" $batd/start_charge_thresh printparm "%-59s = ##%6d## [%%]" $batd/stop_charge_thresh printparm "%-59s = ##%6d##" $batd/force_discharge fi echo # store values for charge / capacity calculation below ed=$(catsysfd $batd/design_capacity 0) ef=$(catsysfd $batd/last_full_capacity 0) en=$(catsysfd $batd/remaining_capacity 0) efsum=$(($efsum + $ef)) ensum=$(($ensum + $en)) # show charge + capacity lcnt=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lcnt=$(($lcnt+1)) fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lcnt=$(($lcnt+1)) fi [ $lcnt -gt 0 ] && echo bcnt=$(($bcnt+1)) fi fi done elif [ -d $ACPIBATDIR ]; then # --- show ACPI data for batd in $ACPIBATDIR/*; do batt=${batd##/*/} tpbat=0 if check_tp_battery $batt; then # ThinkPad battery is present tpbat=1 if [ $tpacpi -eq 0 ]; then # it's a ThinkPad with tpacpi-bat only case $bat_idx in 1) echo "+++ ThinkPad Battery Status: $batt (Main / Internal)" ;; 2) echo "+++ ThinkPad Battery Status: $batt (Ultrabay / Slice / Replaceable)" ;; 0) echo "+++ ThinkPad Battery Status: $batt" ;; esac else # it's a ThinkPad with neither tp-smapi nor tpacpi-bat echo "+++ Battery Status" fi elif [ -d $batd ] \ && [ "$(cat $batd/type 2> /dev/null)" = "Battery" ] \ && [ "$(cat $batd/present 2> /dev/null )" = "1" ]; then # it's some other laptop model or brand echo "+++ Battery Status" else batt="" # power supply is not a battery fi if [ -n "$batt" ]; then printparm "%-59s = ##%s##" $batd/manufacturer printparm "%-59s = ##%s##" $batd/model_name cc=$(cat $batd/cycle_count 2> /dev/null) if [ $? -eq 0 ] && [ -n "$cc" ] && [ $cc -gt 0 ]; then printf "%-59s = %6d\n" "$batd/cycle_count" "$cc" else printf "%-59s = (not supported)\n" "$batd/cycle_count" fi if [ -f $batd/energy_full ]; then printparm "%-59s = ##%6d## [mWh]" $batd/energy_full_design "" 000 printparm "%-59s = ##%6d## [mWh]" $batd/energy_full "" 000 printparm "%-59s = ##%6d## [mWh]" $batd/energy_now "" 000 printparm "%-59s = ##%6d## [mW]" $batd/power_now "" 000 # store values for charge / capacity calculation below ed=$(catsysfd $batd/energy_full_design 0) ef=$(catsysfd $batd/energy_full 0) en=$(catsysfd $batd/energy_now 0) efsum=$(($efsum + $ef)) ensum=$(($ensum + $en)) elif [ -f $batd/charge_full ]; then printparm "%-59s = ##%6d## [mAh]" $batd/charge_full_design "" 000 printparm "%-59s = ##%6d## [mAh]" $batd/charge_full "" 000 printparm "%-59s = ##%6d## [mAh]" $batd/charge_now "" 000 printparm "%-59s = ##%6d## [mA]" $batd/current_now "" 000 # store values for charge / capacity calculation below ed=$(catsysfd $batd/charge_full_design 0) ef=$(catsysfd $batd/charge_full 0) en=$(catsysfd $batd/charge_now 0) efsum=$(($efsum + $ef)) ensum=$(($ensum + $en)) else ed=0 ef=0 en=0 fi if [ $tpbat -eq 1 ]; then print_tp_batstate $batd/status else printparm "%-59s = ##%s##" $batd/status fi echo if [ $show_verbose -eq 1 ]; then printparm "%-59s = ##%6s## [mV]" $batd/voltage_min_design "" 000 printparm "%-59s = ##%6s## [mV]" $batd/voltage_now "" 000 echo fi if [ $tpacpi -eq 0 ]; then # --- show ThinkPad charge thresholds via tpacpi-bat print_tpacpi_thresholds $batt $bat_idx echo fi # if $tpcacpi # show charge + capacity lcnt=0 if [ $ef -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Charge", 100.0 * '$en' / '$ef');' lcnt=$(($lcnt+1)) fi if [ $ed -ne 0 ]; then perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');' lcnt=$(($lcnt+1)) fi [ $lcnt -gt 0 ] && echo bcnt=$(($bcnt+1)) fi # if $batt done # $batd fi # if /sys/class/power_supply if [ $bcnt -eq 0 ]; then # no battery detected printf "+++ Battery Status\n" printf "No batteries detected.\n\n" elif [ $bcnt -gt 1 ] && [ $efsum -ne 0 ]; then # more than one battery detected --> show charge total perl -e 'printf ("%-59s = %6.1f [%%]\n", "+++ Charge total", 100.0 * '$ensum' / '$efsum');' echo fi fi # show_bat if [ "$show_warn" = "1" ] || [ "$show_disk" = "1" ] || [ "$show_all" = "1" ]; then # --- show warnings # ata errors (possibly) caused by SATA_LINKPWR_ON_AC/BAT != max_performance ecnt=$( check_ata_errors ) if [ $ecnt -ne 0 ]; then echo "+++ Warnings" printf "* Kernel log shows ata errors (%d) possibly caused by the configuration\n" $ecnt printf " SATA_LINKPWR_ON_AC/BAT=min_power or medium_power.\n" printf " Consider using medium_power or max_performance instead.\n" printf " See the FAQ: http://linrunner.de/en/tlp/docs/tlp-faq.html#warnings\n" printf " Details:\n" dmesg | egrep -A 5 "${RE_ATA_ERROR}" echo elif [ "$show_warn" = "1" ]; then echo "No warnings detected." echo "" fi fi # show_warn if [ "$show_all" = "1" ]; then # -- show suggestions suout="" if [ "$no_runtimepm" = "1" ]; then suout="${suout}Reconfigure your Linux kernel with PM_RUNTIME=y to reduce your laptop's power consumption.\n" fi if is_thinkpad; then # add ThinkPad specific suggestions -- $tpmodel preset by check_thinkpad() if supports_tpsmapi_only; then # tp-smapi only capable models [ $tpsmapi -eq 127 ] && suout="${suout}Install tp-smapi kernel modules for ThinkPad battery features\n" elif supports_tpacpi_only; then # tpacpi-bat only capable models [ $tpacpi -eq 127 ] && suout="${suout}Install acpi-call kernel module for ThinkPad battery features\n" elif supports_tpsmapi_and_tpacpi; then # tp-smapi and tpacpi-bat capable models [ $tpsmapi -eq 127 ] && suout="${suout}Install tp-smapi kernel modules for ThinkPad battery features\n" [ $tpacpi -eq 127 ] && suout="${suout}Install acpi-call kernel module for ThinkPad battery features\n" elif supports_no_tp_bat_funcs; then : # models that are neither tp_smapi nor tpacpi-bat capable else # all others assumed tp-smapi capable ... [ $tpsmapi -eq 127 ] && suout="${suout}Install tp-smapi kernel modules for ThinkPad battery features\n" fi fi # if ThinkPad # add other suggestions cmd_exists ethtool || suout="${suout}Install ethtool to disable Wake On LAN (WOL)\n" cmd_exists smartctl || suout="${suout}Install smartmontools for disk drive health info\n" if [ -n "$suout" ]; then echo "+++ Suggestions" printf "$suout" | sed -r 's/^/\* /' echo fi fi # show_all if [ "$show_pev" = "1" ]; then # --- show udev power_supply events # check for udevadm if cmd_exists $UDEVADM; then echo "+++ Monitor power supply events -- cancel with ^C" echo $UDEVADM monitor --udev --property --subsystem-match=power_supply fi fi # show_pev if [ "$show_psup" = "1" ]; then # --- show power_supply diagnostic echo "+++ Power supply diagnostic" for ps in /sys/class/power_supply/*; do grep -s '.*' $ps/type $ps/online $ps/present $ps/device/path done fi # show_sup if [ "$show_trace" = "1" ]; then # --- show debug log # check for systemd journal jdone=0 if cmd_exists $JOURNALCTL; then # retrieve trace output from journal $JOURNALCTL -p debug --no-pager SYSLOG_IDENTIFIER=tlp 2> /dev/null # check result -- rc=1 if journald has no data available [ $? -eq 0 ] && jdone=1 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 exit 0 TLP-1.1/tlp-usb-udev.in000066400000000000000000000232241323201726300146730ustar00rootroot00000000000000#!/bin/sh # tlp - handle added usb devices # # Copyright (c) 2018 Thomas Koch # 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. # --- Constants readonly LOGGER=logger readonly USBD=/sys/bus/usb/devices readonly USB_TIMEOUT=2 readonly USB_TIMEOUT_MS=2000 readonly USB_WWAN_VENDORS="0bdb 05c6 1199" readonly RUNDIR=@TLP_RUN@ readonly USB_DONE=usb_done readonly CONFFILE=@TLP_CONF@ # --- Subroutines 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 } echo_debug () { # $1: tag; $2: msg; echo debug msg if tag matches if wordinlist "$1" "$TLP_DEBUG"; then $LOGGER -p debug -t "tlp[$$,$PPID]" "$2" fi } # --- MAIN # read config [ -f $CONFFILE ] || exit 0 . $CONFFILE # exit if TLP or autosuspend disabled [ "$TLP_ENABLE" = "1" ] && [ "$USB_AUTOSUSPEND" = "1" ] || exit 0 # USB autosuspend has two principal operation modes: # # Mode 1 (optional): # - System startup is handled by tlp-functions:set_usb_suspend() # - Startup completion is signaled by "flag file" $USB_DONE # - Newly added devices are handled by this udev script # - Mode 1 is enabled by the private config variable X_TLP_USB_MODE=1 # # Mode 2 (default): # - Everything - including system startup, but not shutdown - is handled by this udev script # do exit if mode 1 and no startup completion flag [ "$X_TLP_USB_MODE" = "1" ] && [ ! -f $RUNDIR/$USB_DONE ] && exit 0 # get args usbdev=/sys$1 # handle device if [ -f $usbdev/power/autosuspend ] || [ -f $usbdev/power/autosuspend_delay_ms ]; then # device is autosuspendable # apply autosuspend ctrlf="control" autof="autosuspend_delay_ms" vendor="$(cat $usbdev/idVendor 2> /dev/null)" usbid="$vendor:$(cat $usbdev/idProduct 2> /dev/null)" busdev="Bus $(cat $usbdev/busnum) Dev $(cat $usbdev/devnum 2> /dev/null)" dclass="$(cat $usbdev/bDeviceClass 2> /dev/null)" control="auto" exc="" chg=0 drvlist="" # trace only: get drivers for all subdevices if [ "$X_USB_DRIVER_TRACE" = "1" ]; then drvlist=$(for dl in $usbdev/*:*/driver; do readlink $dl | \ sed -r 's/.+\///'; done | sort -u | tr '\n' ' ') drvlist="(${drvlist% })" fi if wordinlist "$usbid" "$USB_WHITELIST"; then # device is in whitelist -- whitelist always wins control="auto" exc="_dev_white" elif wordinlist "$usbid" "$USB_BLACKLIST"; then # device is in blacklist control="on" exc="_dev_black" else # wait for subdevices to populate sleep 0.5 # check for hid subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "03" ]; then control="on" exc="_hid_black" break fi done if [ -z "$exc" ]; then # check for bluetooth devices USB_BLACKLIST_BTUSB=${USB_BLACKLIST_BTUSB:-0} # default is include if [ "$USB_BLACKLIST_BTUSB" = "1" ] \ && [ "$dclass" = "e0" ] \ && [ "$(cat $usbdev/bDeviceSubClass 2> /dev/null)" = "01" ] \ && [ "$(cat $usbdev/bDeviceProtocol 2> /dev/null)" = "01" ]; then control="on" exc="_btusb_black" fi fi # bluetooth if [ -z "$exc" ]; then # check for phone devices USB_BLACKLIST_PHONE=${USB_BLACKLIST_PHONE:-0} # default is include if [ "$USB_BLACKLIST_PHONE" = "1" ]; then if [ "$vendor" = "0fca" ]; then # RIM if [ "$dclass" = "ef" ]; then # RIM / BlackBerry control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(cat $subdev/interface 2> /dev/null)" = "BlackBerry" ]; then # Blackberry control="on" exc="_phone_black" break fi fi done fi elif [ "$vendor" = "045e" ] && [ "$dclass" = "ef" ]; then # Windows Phone control="on" exc="_phone_black" elif [ "$vendor" = "05ac" ] && [ "$(cat $usbdev/product 2> /dev/null)" = "iPhone" ]; then # iPhone control="on" exc="_phone_black" elif [ "$dclass" = "00" ]; then # class defined at interface level, iterate subdevices for subdev in $usbdev/*:*; do if [ -d $subdev ]; then if [ "$(cat $subdev/interface 2> /dev/null)" = "MTP" ]; then # MTP: mostly Android control="on" exc="_phone_black" break elif [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "ff" ] \ && [ "$(cat $subdev/bInterfaceSubClass 2> /dev/null)" = "42" ] \ && [ "$(cat $subdev/bInterfaceProtocol 2> /dev/null)" = "01" ]; then # ADB: Android control="on" exc="_phone_black" break elif [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "06" ] \ && [ "$(cat $subdev/bInterfaceSubClass 2> /dev/null)" = "01" ] \ && [ "$(cat $subdev/bInterfaceProtocol 2> /dev/null)" = "01" ]; then # PTP: iPhone, Lumia et al. # caveat: may also be a camera control="on" exc="_phone_black" break fi fi done fi # dclass 00 fi # blacklist phone fi # phone if [ -z "$exc" ]; then # check for printers USB_BLACKLIST_PRINTER=${USB_BLACKLIST_PRINTER:-1} # default is exclude if [ "$USB_BLACKLIST_PRINTER" = "1" ]; then if [ "$dclass" = "00" ]; then # check for printer subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "07" ]; then control="on" exc="_printer_black" break fi done fi fi fi # printer if [ -z "$exc" ]; then # check for wwan devices USB_BLACKLIST_WWAN=${USB_BLACKLIST_WWAN:-1} # default is exclude if [ "$USB_BLACKLIST_WWAN" = "1" ]; then if [ "$dclass" != "00" ]; then # check for cdc subdevices for subdev in $usbdev/*:*; do if [ "$(cat $subdev/bInterfaceClass 2> /dev/null)" = "0a" ]; then control="on" exc="_wwan_black" break fi done fi if [ -z "$exc" ]; then # check for vendors if wordinlist "$vendor" "$USB_WWAN_VENDORS"; then control="on" exc="_wwan_black" fi fi fi # blacklist wwan fi # wwan fi # !device blacklist if [ -f $usbdev/power/control ]; then if [ "$(cat $usbdev/power/control 2> /dev/null)" != "$control" ]; then # Write actual changes only { printf '%s\n' "$control" > $usbdev/power/control; } 2> /dev/null chg=1 fi else # level is deprecated if [ "$(cat $usbdev/power/level 2> /dev/null)" != "$control" ]; then # Write actual changes only { printf '%s\n' "$control" > $usbdev/power/level; } 2> /dev/null chg=1 fi ctrlf="level" fi if [ "$X_TLP_USB_SET_AUTOSUSPEND_DELAY" = "1" ]; then # set autosuspend_delay if [ -f $usbdev/power/autosuspend_delay_ms ]; then { printf '%s\n' $USB_TIMEOUT_MS > $usbdev/power/autosuspend_delay_ms; } 2> /dev/null else # autosuspend is deprecated { printf '%s\n' $USB_TIMEOUT > $usbdev/power/autosuspend; } 2> /dev/null autof="autosuspend" fi echo_debug "usb" "udev_usb.$control$exc: $busdev ID $usbid $usbdev [$ctrlf $autof] $drvlist" elif [ $chg -eq 1 ]; then # default: change control but not autosuspend_delay, i.e. keep kernel default setting echo_debug "usb" "udev_usb.$control$exc: $busdev ID $usbid $usbdev [$ctrlf] $drvlist" else # we didn't change anything actually echo_debug "usb" "udev_usb.$control$exc: $busdev ID $usbid $usbdev $drvlist" fi fi exit 0 TLP-1.1/tlp-usblist000066400000000000000000000070561323201726300142260ustar00rootroot00000000000000#!/usr/bin/perl # tlp-usblist - list usb device info with autosuspend attributes # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. package tlp_usblist; use strict; use warnings; # --- Constants use constant USBD => "/sys/bus/usb/devices"; # --- Global vars my %usbdevices; my $udev; my $no_runtimepm = 0; # --- Subroutines # Read content from a sysfile # $_[0]: input file # return: content / empty string if nonexistent or not readable sub catsysf { my $sysval = ""; if (open (SYSF, "$_[0]")) { chomp ($sysval = ); 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 $driver = ""; if ( open (SYSF, "$_[0]/uevent") ) { # read file line by line while () { # 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 $driverlist = ""; my $subdev; # iterate subdevices foreach $subdev (glob "$_[0]/*:*") { # get subdevice driver my $driver = getdriver ("$subdev"); if ( $driver ) { if (index ($driverlist, $driver) == -1) { if ($driverlist) { $driverlist = $driverlist . ", " . $driver; } else { $driverlist = $driver; } } # if index } # if $driver } # foreach $subdev if (! $driverlist) { $driverlist = "no driver"; } return $driverlist } # --- MAIN # Check if Runtime PM is enabled $no_runtimepm = 1 if ( ! glob USBD . "/*/power/autosuspend*"); # Read USB device tree attributes as arrays into %usbdevices hash, indexed by Bus_Device foreach $udev (grep { ! /:/ } glob USBD . "/*") { my ($asf, $asv); my ($cnf, $cnv); my $usbv = "(autosuspend not available)"; # get device id my $usbk = sprintf ("%03d_%03d", catsysf ("$udev/busnum"), catsysf ("$udev/devnum") ); # look for autosuspend_delay_ms then autosuspend (deprecated) foreach $asf ( "autosuspend_delay_ms", "autosuspend" ) { if ( length ( $asv = catsysf ("$udev/power/$asf") ) ) { # autosuspend* exists --> check for control then level (deprecated) foreach $cnf ( "control", "level" ) { if ( $cnv = catsysf ("$udev/power/$cnf") ) { if ( $asf eq "autosuspend_delay_ms" ) { $usbv = sprintf ("%s = %-5s %s = %5d", $cnf, $cnv . ",", $asf, $asv); } else { $usbv = sprintf ("%s = %-5s %s = %2d", $cnf, $cnv . ",", $asf, $asv); } last; # break loop } } last; # break loop } } # save formatted result in hash @{$usbdevices{$usbk}} = ($udev, $usbv); } # Output device list with attributes and drivers foreach (`lsusb 2> /dev/null`) { my ($bus, $dev, $usbid, $desc) = /Bus (\S+) Device (\S+): ID (\S+)[ ]+(.*)/; my $usbk = $bus . "_" . $dev; $desc ||= ""; print "Bus $bus Device $dev ID $usbid $usbdevices{$usbk}[1] -- $desc (" . usbdriverlist ($usbdevices{$usbk}[0]) . ")\n"; } exit 4 if ( $no_runtimepm == 1 ); exit 0; TLP-1.1/tlp.bash_completion000066400000000000000000000033711323201726300157040ustar00rootroot00000000000000# bash completion for TLP _batteries() { # show list of batteries local bats b bats=$( { for b in /sys/class/power_supply/BAT*; do if [ $(cat $b/present 2> /dev/null) = "1" ]; 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 stat diskid" case $cword in 1) # subcmds only COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; 2) case "${prev}" in fullcharge|chargeonce|discharge|recalibrate) _batteries return 0 ;; esac ;; 4) if [ "${COMP_WORDS[1]}" = "setcharge" ]; then _batteries return 0 fi ;; esac } && complete -F _tlp tlp _tlp_rf() { local cur prev words cword opts _init_completion || return opts="on off toggle" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_rf bluetooth complete -F _tlp_rf wwan complete -F _tlp_rf wifi _tlp_stat() { local cur prev words cword opts _init_completion || return opts="--battery --config --disk --pcie --pev --psup --processor --rfkill --system --temp --usb --warn --trace --verbose" if [ $cword -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } && complete -F _tlp_stat tlp-stat TLP-1.1/tlp.in000066400000000000000000000227061323201726300131470ustar00rootroot00000000000000#!/bin/sh # tlp - adjust power settings # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # --- Constants readonly LIBDIR="@TLP_TLIB@" readonly LIBS="tlp-functions tlp-rf-func" # --- Source libraries for lib in $LIBS; do if [ ! -f $LIBDIR/$lib ]; then echo "Error: missing function library \'$LIBDIR/$lib\'." 1>&2 exit 1 fi . $LIBDIR/$lib done # --- 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_scaling_governor $1 set_scaling_min_max_freq $1 set_cpu_hwp_pref $1 set_cpu_perf_pct $1 set_cpu_boost_all $1 set_sched_powersave $1 set_nmi_watchdog set_phc_controls $1 set_energy_perf_policy $1 set_disk_apm_level $1 set_disk_spindown_timeout $1 set_disk_io_sched set_sata_link_power $1 set_ahci_runtime_pm $1 set_pcie_aspm $1 set_radeon_profile $1 set_wifi_power_mode $1 disable_wake_on_lan set_sound_power_mode $1 set_runtime_pm $1 return 0 } # --- MAIN read_defaults check_tlp_enabled || exit 1 add_sbin2path check_laptop_mode_tools # get cmd line args mode=$(echo $1 | tr "[:upper:]" "[:lower:]") mode2=$(echo $2 | tr "[:upper:]" "[:lower:]") # inhibit trace output for tlp stat [ "$mode" = "stat" ] && nodebug=1 # get current power state get_power_state; pwrmode=$? case "$mode" in true|bat) pwrmode=1 ;; false|ac) pwrmode=0 ;; esac modedebug=$mode [ -n "$mode2" ] && modedebug="$modedebug $mode2" echo_debug "run" "+++ mode=$modedebug ($TLPVER) ++++++++++++++++++++++++++++++++++++++++" if [ -n "$addpath" ]; then echo_debug "path" "PATH=$oldpath[$addpath]" else echo_debug "path" "PATH=$oldpath" fi if [ $pwrmode -eq 1 ]; then echo_debug "run" "power_mode=bat" else echo_debug "run" "power_mode=ac" fi case "$syspwr" in 0) echo_debug "run" "power_source=ac" ;; 1) echo_debug "run" "power_source=bat" ;; *) echo_debug "run" "power_source=unknown" ;; esac exitcode=0 case "$mode" in init) # system initialization/shutdown: sysv, upstart, systemd, ... check_root # try to obtain lock (with timeout) locked=0 if lock_tlp; then locked=1 else echo "Failed to get lock, continuing anyway." 1>&2 fi # do init business ... case $mode2 in start) # apply power save settings compare_and_save_power_state $pwrmode echo -n "Applying power save settings..." apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto echo "done." # apply battery settings echo -n "Setting battery charge thresholds..." set_charge_thresholds echo "done." # apply radio states set_radio_device_states start ;; restart|force-reload) # apply power save settings compare_and_save_power_state $pwrmode echo -n "Applying power save settings..." apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto echo "done." # apply battery settings echo -n "Setting battery charge thresholds..." set_charge_thresholds echo "done." ;; stop) # remove usb startup flag [ -f $USB_DONE ] && rm $USB_DONE # clear saved power state clear_saved_power_state # apply ac settings for faster shutdown echo -n "Applying power save settings..." apply_common_settings 0 poweroff_drivebay $pwrmode 0 echo "done." # disable usb autosuspend if configured if [ "$USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN" = "1" ]; then echo -n "Disabling usb autosuspend..." set_usb_suspend 0 on echo "done." fi # apply radio states set_radio_device_states stop ;; *) echo "Usage: tlp init {start|stop|restart|force-reload}" >&2 exit 3 ;; esac # unlock if necessary [ $locked -eq 0 ] || unlock_tlp ;; auto) # set mode depending on state (called by udev rule) # -- but only if not previously run for the same power state # rationale: filter out duplicate power_supply udev events check_root if lock_tlp_nb; then if compare_and_save_power_state $pwrmode; then apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 set_radio_device_states $pwrmode if [ "$RESTORE_THRESHOLDS_ON_BAT" = "1" ] && [ "$pwrmode" = "1" ]; then set_charge_thresholds fi fi unlock_tlp else exitcode=4 fi ;; start) # set mode depending on state (interactive mode) check_root if lock_tlp; then compare_and_save_power_state $pwrmode apply_common_settings $pwrmode poweroff_drivebay $pwrmode 0 set_usb_suspend 0 auto set_charge_thresholds set_radio_device_states $pwrmode unlock_tlp echo_started_mode $pwrmode else echo_tlp_locked exitcode=4 fi ;; true|bat) # set battery power mode check_root if lock_tlp; then compare_and_save_power_state 1 apply_common_settings 1 poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto set_radio_device_states 1 unlock_tlp echo_started_mode 1 else echo_tlp_locked exitcode=4 fi ;; false|ac) # set ac power mode check_root if lock_tlp; then compare_and_save_power_state 0 apply_common_settings 0 poweroff_drivebay $pwrmode 0 [ "$X_TLP_USB_MODE" = "1" ] && set_usb_suspend 0 auto set_radio_device_states 0 unlock_tlp echo_started_mode 0 else echo_tlp_locked exitcode=4 fi ;; suspend) # handle suspend/hibernate check_root if lock_tlp; then save_device_states "bluetooth wwan" apply_common_settings 0 suspend_drivebay $pwrmode unlock_tlp else exitcode=4 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 unlock_tlp else exitcode=4 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 $2 $3 $4 exitcode=$? ;; fullcharge) # charge battery up to 100% (temporarily) if check_ac_power fullcharge; then check_root setcharge_battery 96 100 $2 exitcode=$? else exitcode=2 fi ;; chargeonce) # charge battery to upper threshold once if check_ac_power chargeonce; then check_root chargeonce_battery $2 exitcode=$? else exitcode=2 fi ;; discharge) # discharge battery completely (to recalibrate) if check_ac_power discharge; then check_root discharge_battery $2 exitcode=$? else exitcode=2 fi ;; recalibrate) # recalibrate battery, i.e. discharge and charge to 100% if check_ac_power recalibrate; then check_root setcharge_battery 96 100 $2 sleep 1 discharge_battery $2 exitcode=$? if [ $exitcode -eq 0 ]; then echo " The battery starts charging now. For a complete recalibration" echo " keep AC connected until the battery is fully charged." fi else exitcode=2 fi ;; stat) # show status shift tlp-stat $* exitcode=$? ;; diskid) # show disk-by-id { for dev in $(ls /dev/disk/by-id/ | egrep '^ata' | egrep -v '\-part[1-9]+'); do if [ -n "$dev" ]; then get_disk_dev $dev echo "$disk_dev: $disk_id" fi done } | sort ;; *) echo "Error: unknown command \"$mode\"." 1>&2 echo "Usage: tlp start|true|bat|false|ac|usb|bayoff|discharge|setcharge|fullcharge|recalibrate|stat|diskid" 1>&2 exitcode=3 ;; esac exit $exitcode TLP-1.1/tlp.init000066400000000000000000000014071323201726300134770ustar00rootroot00000000000000#!/bin/sh # tlp - system startup/shutdown # # Copyright (c) 2018 Thomas Koch # 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 . /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.1/tlp.rules.in000066400000000000000000000006451323201726300142760ustar00rootroot00000000000000# tlp - udev rules # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. # handle change of power source ac/bat ACTION=="change", SUBSYSTEM=="power_supply", RUN+="@TLP_SBIN@/tlp auto" # handle added usb devices (exclude subdevices via DRIVER=="USB") ACTION=="add", SUBSYSTEM=="usb", DRIVER=="usb", ENV{DEVTYPE}=="usb_device", RUN+="@TLP_ULIB@/tlp-usb-udev %p" TLP-1.1/tlp.service.in000066400000000000000000000010171323201726300145760ustar00rootroot00000000000000# tlp - systemd startup/shutdown service # # Copyright (c) 2018 Thomas Koch # This software is licensed under the GPL v2 or later. [Unit] Description=TLP system startup/shutdown Wants=bluetooth.service NetworkManager.service After=multi-user.target bluetooth.service NetworkManager.service Before=shutdown.target Documentation=http://linrunner.de/tlp [Service] Type=oneshot RemainAfterExit=yes ExecStart=@TLP_SBIN@/tlp init start ExecStop=@TLP_SBIN@/tlp init stop [Install] WantedBy=multi-user.target TLP-1.1/tlp.upstart.in000066400000000000000000000006441323201726300146450ustar00rootroot00000000000000# tlp - system startup/shutdown # # Copyright (c) 2018 Thomas Koch # 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.1/tpacpi-bat000066400000000000000000000336441323201726300137720ustar00rootroot00000000000000#!/usr/bin/perl ########################################################################## # Thinkpad ACPI Battery Control # Copyright 2011 Elliot Wolk ########################################################################## # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ########################################################################## # Exposes inhibit charge, start/stop charge threshold, and force discharge # through ACPI as an alternative to SMAPI, which is broken in W520, etc. # # Makes ACPI calls using the acpi_call kernel module, which is REQUIRED. # # Supports 2011-released thinkpads, and possibly later ones. # Tested and works on: W520, T420, X220, E130 # Tested and does NOT work on: T400, X201S # # Limitations/Known Issues: # 1) you cant force discharge on battery, so balancing is still impossible # 2) sometimes you cant convince a slice battery to charge before the main # ANYONE who figures out this second issue gets a cookie! # if my main is above 80, and my slice is at 80, my main charges. # i can inhibit the main, but i CANNOT trick the slice into charging # 3) you can only inhibit for 720 minutes instead of 1440 minutes # this seems like a BIOS error ########################################################################## use strict; use warnings; use File::Basename; my $acpiCallDev = '/proc/acpi/call'; my $psDeviceGlob = "/sys/class/power_supply/{BAT0,BAT1,AC,ADP0,ADP1}/device/path"; sub getMethod($$); sub setMethod($$@); sub readPeakShiftState($); sub readInhibitCharge($); sub readStartChargeThreshold($); sub readStopChargeThreshold($); sub readForceDischarge($); sub writePeakShiftState($); sub writeInhibitCharge($); sub writeStartChargeThreshold($); sub writeStopChargeThreshold($); sub writeForceDischarge($); sub acpiCall($); sub runAcpiCall($$); sub acpiCallGet($$); sub acpiCallSet($$); sub revpadzero($$); sub bitRangeToDec(\@$$); sub parseArgDecToBin($); sub parseStatusHexToBitArray($); sub decToBin($); sub binToDec($); sub hexToBin($); sub binToHex($); sub synList(@); our $verbose = 0; my ($methodST, $methodSP, $methodIC, $methodFD, $methodPS) = ("ST", "SP", "IC", "FD", "PS"); my $methodSyns = { $methodST => synList ("st", "startThreshold", "start"), $methodSP => synList ("sp", "stopThreshold", "stop"), $methodIC => synList ("ic", "inhibitCharge", "inhibit"), $methodFD => synList ("fd", "forceDischarge"), $methodPS => synList ("ps", "peakShiftState"), }; my $name = File::Basename::basename $0; my $usage = "Usage: Show this message: $name [-h|--help] Get charge thresholds / inhibit charge / force discharge: $name [-v] -g $methodST $name [-v] -g $methodSP $name [-v] -g $methodIC $name [-v] -g $methodFD Set charge thresholds / inhibit charge / force discharge: $name [-v] -s $methodST $name [-v] -s $methodSP $name [-v] -s $methodIC [] $name [-v] -s $methodFD [] Set peak shift state, which is mysterious and inhibits charge: $name [-v] -s $methodPS [] Synonyms: $methodST -> $$methodSyns{$methodST} $methodSP -> $$methodSyns{$methodSP} $methodIC -> $$methodSyns{$methodIC} $methodFD -> $$methodSyns{$methodFD} $methodPS -> $$methodSyns{$methodPS} Options: -v show ASL call and response 1 for main, 2 for secondary, 0 for either/both number of minutes, or 0 for never, or 65535 for forever 0 for default, 1-99 for percentage 1 for inhibit charge, 0 for stop inhibiting charge 1 for force discharge, 0 for stop forcing discharge 1 for stop forcing when AC is detached, 0 for do not [] means optional: sets value to 0 "; my $noReadMethods = join "|", ($methodPS); my $noBothReadMethods = join "|", ($methodST, $methodSP, $methodFD); my $noBothWriteMethods = join "|", ($methodFD); sub main(@){ if(@_ == 1 and $_[0] =~ /^(-h|--help)$/){ print $usage; exit 0; } if(@_ > 0 and $_[0] eq '-v'){ $verbose = 1; shift; } my $cmd = shift() || ''; my $method; my $methodSyn = shift() || ''; for my $m(keys %$methodSyns){ $method = $m if $methodSyn eq $m or $methodSyn =~ /^($$methodSyns{$m})$/; } die $usage if not defined $method or $cmd !~ /^(-g|-s)$/; my $bat; if($method eq $methodPS){ $bat = 0; }else{ $bat = shift; } die " missing or incorrect\n" if not defined $bat or $bat !~ /^0|1|2$/; if($cmd eq '-g' and @_ == 0){ print getMethod($method, $bat) . "\n"; }elsif($cmd eq '-s'){ print setMethod($method, $bat, @_); }else{ die $usage; } } sub getMethod($$){ my $method = shift; my $bat = shift; if($method =~ /^($noReadMethods)$/){ die "Cannot read $method\n"; } if($bat == 0 and $method =~ /^($noBothReadMethods)$/){ die "Cannot specify 'either/both' for reading $method\n"; } $bat = parseArgDecToBin $bat; if($method eq $methodST){ return readStartChargeThreshold(acpiCallGet 'BCTG', $bat); }elsif($method eq $methodSP){ return readStopChargeThreshold(acpiCallGet 'BCSG', $bat); }elsif($method eq $methodIC){ #this is actually reading peak shift state return readInhibitCharge(acpiCallGet 'PSSG', $bat); }elsif($method eq $methodFD){ return readForceDischarge(acpiCallGet 'BDSG', $bat); }else{ die $usage; } } sub setMethod($$@){ my $method = shift; my $bat = shift; if($bat == 0 and $method =~ /^($noBothWriteMethods)$/){ die "Cannot specify 'either/both' for writing $method\n"; } my %info; $info{bat} = $bat; if($method =~ /^($methodIC|$methodPS)$/){ $info{inhibit} = shift @_; if(not defined $info{inhibit} or $info{inhibit} !~ /^0|1$/){ die "missing or invalid value for \n"; } $info{min} = shift @_; $info{min} = 0 if not defined $info{min}; ############################################################ #they are shifting a bit somewhere; the limit should be 1440 #the same range in peak-shift-state is used, except shifted to the left #the value returned by peak-shift-state is the REAL duration, though $info{min} *= 2 if $method eq $methodIC and $info{min} != 65535; ############################################################ if($info{min} !~ /^\d+$/ or ($info{min} > 1440 and $info{min} != 65535)){ die "invalid value for \n"; } }elsif($method =~ /^($methodFD)$/){ $info{discharge} = shift @_; if(not defined $info{discharge} or $info{discharge} !~ /^0|1$/){ die "missing or invalid value for \n"; } $info{acbreak} = shift @_; $info{acbreak} = 0 if not defined $info{acbreak}; if($info{acbreak} !~ /^0|1$/){ die "invalid value for \n"; } }elsif($method =~ /^($methodST|$methodSP)$/){ $info{percent} = shift @_; if(not defined $info{percent} or $info{percent} !~ /^\d+$/ or $info{percent} > 99){ die "missing or invalid value for \n"; } } die $usage if @_ > 0; %info = map {$_ => parseArgDecToBin $info{$_}} keys %info; if($method eq $methodST){ acpiCallSet 'BCCS', writeStartChargeThreshold(\%info); }elsif($method eq $methodSP){ acpiCallSet 'BCSS', writeStopChargeThreshold(\%info); }elsif($method eq $methodIC){ acpiCallSet 'BICS', writeInhibitCharge(\%info); }elsif($method eq $methodFD){ acpiCallSet 'BDSS', writeForceDischarge(\%info); }elsif($method eq $methodPS){ acpiCallSet 'PSSS', writePeakShiftState(\%info); }else{ die $usage; } } sub readInhibitCharge($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[5] != 1){ die "\n"; } my $val; if($bits[0] == 1){ $val = "yes"; my $min = bitRangeToDec @bits, 8, 23; if($min == 0){ $val .= " (unspecified min)"; }elsif($min == 65535){ $val .= " (forever)"; }else{ $val .= " ($min min)"; } }else{ $val = "no"; } return $val; } sub readStartChargeThreshold($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val = bitRangeToDec @bits, 0, 7; if($val == 0){ $val .= " (default)"; }elsif($val > 0 and $val < 100){ $val .= " (relative percent)"; }else{ $val .= " (unknown)"; } return $val; } sub readStopChargeThreshold($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val = bitRangeToDec @bits, 0, 7; if($val == 0){ $val .= " (default)"; }elsif($val > 0 and $val < 100){ $val .= " (relative percent)"; }else{ $val .= " (unknown)"; } return $val; } sub readForceDischarge($){ my @bits = parseStatusHexToBitArray $_[0]; if($bits[8] != 1 and $bits[9] != 1){ die "\n"; } my $val; if($bits[0] == 1){ $val = 'yes'; }else{ $val = 'no'; } if($bits[1] == 1){ $val .= ' (break on AC detach)'; } return $val; } sub writePeakShiftState($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{inhibit}) . revpadzero( 3, 0) . revpadzero( 4, 0) . revpadzero(16, $$info{min}) . revpadzero( 8, 0) ; } sub writeInhibitCharge($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{inhibit}) . revpadzero( 3, 0) . revpadzero( 2, $$info{bat}) . revpadzero( 2, 0) . revpadzero(16, $$info{min}) . revpadzero( 8, 0) ; } sub writeStartChargeThreshold($){ my $info = shift; return reverse '' . revpadzero( 8, $$info{percent}) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub writeStopChargeThreshold($){ my $info = shift; return reverse '' . revpadzero( 8, $$info{percent}) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub writeForceDischarge($){ my $info = shift; return reverse '' . revpadzero( 1, $$info{discharge}) . revpadzero( 1, $$info{acbreak}) . revpadzero( 6, 0) . revpadzero( 2, $$info{bat}) . revpadzero(22, 0) ; } sub acpiCall($){ my $call = shift; if(not -e $acpiCallDev){ $ENV{'PATH'} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; system "modprobe acpi_call"; } if(not -e $acpiCallDev){ die "Could not find $acpiCallDev. Is module acpi_call loaded?\n"; } my $dmiVersion = `cat /sys/class/dmi/id/product_version 2>/dev/null`; chomp $dmiVersion; my %aslBaseOverrides = ( 'ThinkPad S2' => '\_SB.PCI0.LPCB.EC.HKEY', 'ThinkPad 13 2nd Gen' => '\_SB.PCI0.LPCB.EC.HKEY', 'ThinkPad Edge E130' => '\_SB.PCI0.LPCB.EC.HKEY', ); my $aslBase; if (exists($aslBaseOverrides{$dmiVersion})) { $aslBase = $aslBaseOverrides{$dmiVersion}; } else { my $psdev = ""; foreach my $dev (glob "$psDeviceGlob") { if(-e $dev) { $psdev = $dev; last; }; } if(not -e $psdev){ die "No power supply device path to read ASL base from: $psDeviceGlob\n"; } $aslBase = `cat $psdev`; } chomp $aslBase; $aslBase =~ s/_+(\.|$)/$1/g; #trim trailing _s from components $aslBase =~ s/(\.([A-Z,0-9])+)$/.HKEY/; #replace final .dddd with .HKEY my $val = runAcpiCall $call, $aslBase; if($val =~ /Error: AE_NOT_FOUND/){ die "Error: AE_NOT_FOUND for ASL base: $aslBase\n$val"; } print "Call : $aslBase.$call\n" if $verbose; print "Response: $val\n" if $verbose; return $val; } sub runAcpiCall($$){ my ($call, $aslBase) = @_; open FH, "> $acpiCallDev" or die "Cannot write to $acpiCallDev: $!"; print FH "$aslBase.$call\n"; close FH; open FH, "< $acpiCallDev" or die "Cannot read $acpiCallDev: $!"; my $val = ; close FH; return $val; } sub acpiCallGet($$){ my ($method, $bits) = @_; my $call = "$method 0x" . binToHex($bits); my $val = acpiCall $call; if($val eq '0x80000000'){ die "Call failure status returned: $val"; } return $val; } sub acpiCallSet($$){ my ($method, $bits) = @_; my $call = "$method 0x" . binToHex($bits); my $val = acpiCall $call; if($val eq '0x80000000'){ die "Call failure status returned: $val"; } } sub revpadzero($$){ return reverse ('0' x ($_[0] - length $_[1]) . $_[1]); } sub bitRangeToDec(\@$$){ my @bits = @{shift()}; my $start = shift; my $end = shift; my $bin = reverse(join '', @bits[$start .. $end]); return binToDec $bin; } sub parseArgDecToBin($){ my $dec = shift; die "not a positive integer: " . $dec . "\n\n$usage" if $dec !~ /^\d+$/; return decToBin $dec; } sub parseStatusHexToBitArray($){ my $hex = shift; if($hex !~ /0x([0-9a-f]+)/i){ my $msg = "Bad status returned: $hex\n"; if($hex =~ /Error: AE_NOT_FOUND/){ $msg .= '' . "ASL base not found for this machine\n" . " {perhaps it does not have the ThinkPad ACPI interface}\n" . "ASL base parsed using: $psDeviceGlob\n" ; } die $msg; } return split //, revpadzero 32, hexToBin($1); } sub decToBin($){ my $bits = unpack("B32", pack("N", $_[0])); $bits =~ s/^0*//; return $bits; } sub binToDec($){ return oct "0b$_[0]"; } sub hexToBin($){ return decToBin(oct "0x$_[0]"); } sub binToHex($){ return sprintf("%x", binToDec $_[0]); } sub synList(@){ return join "|", ((map {"--$_"} @_), @_); } &main(@ARGV);