[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#927006: marked as done (unblock: tlp/1.2.1-1)



Your message dated Sat, 11 May 2019 19:00:15 +0200
with message-id <08f20e81-8017-fd02-076f-b7796aeab858@debian.org>
and subject line Re: unblock: tlp/1.2.1-1
has caused the Debian Bug report #927006,
regarding unblock: tlp/1.2.1-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
927006: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=927006
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
User: release.debian.org@packages.debian.org
Usertags: unblock
Severity: normal

Please unblock package tlp

The reason for this request is that 1.2 introduces support for new
hardware (NVMe disks) and support for a new kernel API (introduced in
4.17, so matching Buster's kernel) which would allow a lot of users to
tune battery (dis)charge thresholds without need for tp-smapi or
acpi-call, thus lessening TLP's run-time dependencies.

I know it's a bit late, but TLP 1.2 was released just the day after the
deep freeze took place. I was quite busy and couldn't package it right
away, but it seems it was all in all a blessing in disguise, since a few
days after that a bugfix 1.2.1 was released by upstream (not really a
bug, just an action present in previous versions which was missing in
1.2). In addition to that, I noticed that I forgot to update my GPG key
on Debian's key server, which explains the delay between my initial
upload (2019-04-02) and the availability in unstable (2019-04-13).

I would perfectly understand why this request would be rejected, since a
new version is likely to introduce bugs (but, as you can see, the
community of TLP users is quite active and 1.2's missing function was
quickly spotted and fixed) and we don't want that so close to release; I
will provide (sloppy-)backports anyway (as I always do since I maintain
this package), but it would be really nice to Buster users to enjoy the
new features without needing to manually install the backported version.

The debdiff may seem huge, but it's mainly because a big functions file
was split into several files (it grossly accounts for 67% of the diff).

Thanks in advance !

unblock tlp/1.2.1-1

-- System Information:
Debian Release: buster/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 4.19.0-4-amd64 (SMP w/4 CPU cores)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8),
LANGUAGE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash

Regards,

-- 
Raphaël Halimi
diff -Nru tlp-1.1/49tlp tlp-1.2.1/49tlp
--- tlp-1.1/49tlp	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/49tlp	1970-01-01 01:00:00.000000000 +0100
@@ -1,22 +0,0 @@
-#!/bin/sh
-# tlp - handle suspend/hibernate/resume tasks
-#
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
-# 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
diff -Nru tlp-1.1/AUTHORS tlp-1.2.1/AUTHORS
--- tlp-1.1/AUTHORS	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/AUTHORS	2019-03-18 18:08:25.000000000 +0100
@@ -1,6 +1,7 @@
 Main author:
  Thomas Koch - <linrunner at gmx.net>
 
-Contributors (refer to Git commits for details):
+Contributors:
  André Erdmann <dywi at mailerd.de>
  Pali Rohár <pali.rohar at gmail.com>
+ https://github.com/linrunner/TLP/graphs/contributors
diff -Nru tlp-1.1/changelog tlp-1.2.1/changelog
--- tlp-1.1/changelog	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/changelog	2019-03-18 18:08:25.000000000 +0100
@@ -1,7 +1,65 @@
++++ 1.2.1 --- 18.03.2019 +++
+
+  * Bugfix
+    - Issue #391: unknown command "chargeonce"
+
++++ 1.2 --- 11.03.2019 +++
+
+  * Features
+    Disks:
+      - Support for NVMe devices
+      - Support for removable drives e.g. USB / IEE1394 devices
+      - Improve support for multi queue I/O schedulers (blk-mq)
+    General:
+      - tlp bat/ac: keep manual power settings until tlp start (Issue #349)
+      - Remove all pm-utils scripts (Issue #363)
+      - tlp/tlp-stat: Temporarily overwrite configuration for one program
+        invocation only: -- PARAM=value ...
+      - Document intrinsic defaults in config file (Issue #353)
+      - Code verified with ShellCheck
+    Graphics:
+      - INTEL_GPU_MIN_FREQ_ON_AC/BAT, INTEL_GPU_MAX_FREQ_ON_AC/BAT,
+        INTEL_GPU_BOOST_FREQ_ON_AC/BAT: Intel GPU frequency limits
+    Radio Devices:
+       - tlp-rdw: new command to disable RDW actions temporarily (until reboot)
+       - Support ThinkPad Pro Dock CS18 (17ef:306f)
+       - USB_BLACKLIST_WWAN: disable by default
+       - Retire compatibility with Network Manager 0.9.8 (Ubuntu 14.04 EOL)
+    PCI(e) devices:
+       - RUNTIME_PM_BLACKLIST: add mei_me, pcieport
+    ThinkPad Battery:
+      - New native kernel API for battery features "natacpi" (Issue #321);
+        requires kernel 4.17; enabled by default
+      - NATACPI_ENABLE, TPACPI_ENABLE, TPSMAPI_ENABLE: make all battery feature
+        drivers switchable
+      - tlp discharge/recalibrate: exclude multiple simultaneous invocations
+      - Support ThinkPad 25, *80 (Coffee Lake) and all newer models
+    tlp-stat:
+      - Check systemd-rfkill.socket masked status
+      - Disks: show all configured devices (consider default)
+      - Intel GPU: show frequency limits and available frequencies
+      - Rename "Suggestions" section to "Recommendations"
+      - Remove invocation via 'tlp stat'
+    USB:
+      - Exclude scanners managed by libsane from autosuspend
+      - Remove long deprecated level/autosuspend attributes
+
+  * Bugfixes
+    - Issue #193: do not try to start NetworkManager (systemd)
+    - Issue #319: get_disk_dev logic is not compatible with NVMe devices
+    - Issue #320: AC mode not detected with USB charger
+    - Issue #331: Process '/usr/bin/tlp auto' failed with exit code 4
+    - Issue #332: zsh parse error in tlp diskid
+    - Issue #343, #362, #375: circumvent broken AC/charger detection
+    - Issue #344: keep ASPM default to enable deeper C-states on AC
+    - Issue #356: fix writing sequence for start/stop charge thresholds
+    - Issue #368: recognize Think*p*ad P50
+    - tlp-stat: filter HWP lines from x86_energy_perf_policy output
+
 +++ 1.1 --- 24.01.2018 +++
 
   * Features
-   Disks:
+    Disks:
       - SATA_LINKPWR_ON_AC/BAT: try multiple values to support new recommended
         ALPM policy "med_power_with_dipm" in kernel 4.15
     Processor:
diff -Nru tlp-1.1/COPYING tlp-1.2.1/COPYING
--- tlp-1.1/COPYING	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/COPYING	2019-03-18 18:08:25.000000000 +0100
@@ -1,10 +1,10 @@
-Author:
+Main Author:
 
     Thomas Koch <linrunner at gmx.net>
 
 Copyright:
 
-    Copyright (c) 2018 Thomas Koch, André Erdmann, Pali Rohár
+    Copyright (c) 2019 Thomas Koch, André Erdmann, Pali Rohár
 
     See https://github.com/linrunner/TLP/ for additional contributors
 
@@ -12,7 +12,7 @@
     - laptop-mode-tools
          Copyright (c) 2004 by Bart Samwel, Kiko Piris, Micha Feigin,
          Andrew Morton, Herve Eychenne, Dax Kelson, Jan Topinski
-    - http://thinkwiki.org
+    - https://thinkwiki.org
 
     Thinkpad ACPI Battery Control (tpacpi-bat):
     Copyright (c) 2011-2016 Elliot Wolk
diff -Nru tlp-1.1/debian/changelog tlp-1.2.1/debian/changelog
--- tlp-1.1/debian/changelog	2018-03-27 15:02:15.000000000 +0200
+++ tlp-1.2.1/debian/changelog	2019-04-02 19:05:16.000000000 +0200
@@ -1,3 +1,52 @@
+tlp (1.2.1-1) unstable; urgency=medium
+
+  * [9c53a90] New upstream version 1.2 (LP: #1814083)
+  * [c273e03] New upstream version 1.2.1 (bugfix release)
+
+  Highlights:
+
+  - Disks
+    + Support for NVMe, USB, IEEE1394 devices
+    + Support for multi queue I/O schedulers (blk-mq)
+
+  - General
+    + Manual mode: keep tlp ac/bat power settings (until reboot or tlp start)
+
+  - Graphics
+    + Intel GPU frequency limits
+
+  - Radio Devices
+    + tlp-rdw: new command to disable RDW actions temporarily (until reboot)
+    + USB_BLACKLIST_WWAN: disable by default
+
+  - ThinkPad Battery
+    + New native kernel API for battery features "natacpi"; used by default
+      for kernels >= 4.17
+    + Support ThinkPad 25, *80 (Coffee Lake) and all newer models
+    + tlp-stat: improved recommendations for external kernel modules
+
+  - USB
+    + Exclude scanners managed by libsane from autosuspend
+
+  [ Thomas Koch ]
+  * [047ba95] Remove all pm-utils related stuff
+  * [dcfe4a1] Demote linux-tools Recommends to Suggests and prefer generic
+    (LP: #1783455)
+  * [0bd7f4b] debian/install: reflect func.d changes
+  * [6148158] New command tlp-rdw
+  * [c4d9030] debian/tlp.docs: README --> README.md
+  * [e6fc28f] debian/control: use https for Homepage field
+
+  [ Raphaël Halimi ]
+  * [de3c365] Bump Standards-Version to 4.3.0
+  * [d180097] debian/copyright: update copyrights years
+  * [35e4c0a] debian/copyright: add AppStream metadata file license (MIT)
+  * [f1798c6] Remove obsolete lintian override about unusual WantedBy target
+    (fixed in lintian 2.5.118)
+  * [4178f68] Include upstream elogind sleep hook
+
+ -- Raphaël Halimi <raphael.halimi@gmail.com>  Tue, 02 Apr 2019 19:05:16 +0200
+
 tlp (1.1-2) unstable; urgency=medium
 
   * [59b316a] Demote smartmontools from Recommends to Suggests
diff -Nru tlp-1.1/debian/control tlp-1.2.1/debian/control
--- tlp-1.1/debian/control	2018-03-27 15:02:15.000000000 +0200
+++ tlp-1.2.1/debian/control	2019-04-02 19:05:16.000000000 +0200
@@ -3,16 +3,16 @@
 Section: utils
 Priority: optional
 Build-Depends: debhelper (>= 9.20160709)
-Standards-Version: 4.1.3
+Standards-Version: 4.3.0
 Vcs-Browser: https://salsa.debian.org/moonsweep-guest/tlp
 Vcs-Git: https://salsa.debian.org/moonsweep-guest/tlp.git
-Homepage: http://linrunner.de/tlp
+Homepage: https://linrunner.de/tlp
 
 Package: tlp
 Architecture: all
 Depends: lsb-base, hdparm, iw | wireless-tools, pciutils, rfkill, usbutils, ${misc:Depends}
-Recommends: tlp-rdw, ethtool, ${dist:Recommends}
-Suggests: tp-smapi-dkms, acpi-call-dkms, smartmontools
+Recommends: tlp-rdw, ethtool
+Suggests: tp-smapi-dkms, acpi-call-dkms, smartmontools, ${dist:Suggests}
 Conflicts: laptop-mode-tools
 Description: Save battery power on laptops
  TLP is an advanced power management tool for Linux. It comes with a
diff -Nru tlp-1.1/debian/copyright tlp-1.2.1/debian/copyright
--- tlp-1.1/debian/copyright	2018-02-17 13:52:50.000000000 +0100
+++ tlp-1.2.1/debian/copyright	2019-04-02 19:05:16.000000000 +0200
@@ -4,7 +4,7 @@
 Source: https://github.com/linrunner/TLP/
 
 Files: *
-Copyright: 2018 Thomas Koch, André Erdmann, Pali Rohár
+Copyright: 2019 Thomas Koch, André Erdmann, Pali Rohár
 License: GPL-2+
 Comment:
   Some code and descriptions were adapted from:
@@ -13,12 +13,16 @@
      Andrew Morton, Herve Eychenne, Dax Kelson, Jan Topinski
  - http://thinkwiki.org
 
+Files: de.linrunner.tlp.metainfo.xml
+Copyright: 2019 Raphaël Halimi, Thomas Koch
+License: MIT
+
 Files: tpacpi-bat
 Copyright: 2011-2016 Elliot Wolk
 License: GPL-3+
 
 Files: debian/*
-Copyright: 2018 Thomas Koch, Raphaël Halimi
+Copyright: 2019 Thomas Koch, Raphaël Halimi
 License: GPL-2+
 
 License: GPL-2+
@@ -28,3 +32,22 @@
 License: GPL-3+
  On Debian/Ubuntu systems, the full text of the GPL v3 can be found in
  `/usr/share/common-licenses/GPL-3'.
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff -Nru tlp-1.1/debian/rules tlp-1.2.1/debian/rules
--- tlp-1.1/debian/rules	2018-02-10 17:14:45.000000000 +0100
+++ tlp-1.2.1/debian/rules	2019-04-02 19:05:16.000000000 +0200
@@ -5,16 +5,16 @@
 
 # Debian and Ubuntu provide x86_energy_perf_policy in different packages
 ifeq ($(shell dpkg-vendor --derives-from Ubuntu && echo yes),yes)
-	SUBSTVARS = -Vdist:Recommends="linux-tools"
+	SUBSTVARS = -Vdist:Suggests="linux-tools-generic | linux-tools"
 else
-	SUBSTVARS = -Vdist:Recommends="linux-cpupower"
+	SUBSTVARS = -Vdist:Suggests="linux-cpupower"
 endif
 
 %:
 	dh $@ --with systemd
 
 override_dh_auto_install:
-	dh_auto_install -- TLP_WITH_SYSTEMD=1
+	dh_auto_install -- TLP_WITH_SYSTEMD=1 TLP_WITH_ELOGIND=1
 
 override_dh_systemd_enable:
 	dh_systemd_enable --package=tlp tlp.service tlp-sleep.service
diff -Nru tlp-1.1/debian/tlp.docs tlp-1.2.1/debian/tlp.docs
--- tlp-1.1/debian/tlp.docs	2018-02-10 17:14:45.000000000 +0100
+++ tlp-1.2.1/debian/tlp.docs	2019-04-02 19:05:16.000000000 +0200
@@ -1 +1 @@
-README
+README.md
diff -Nru tlp-1.1/debian/tlp.install tlp-1.2.1/debian/tlp.install
--- tlp-1.1/debian/tlp.install	2018-02-17 13:52:50.000000000 +0100
+++ tlp-1.2.1/debian/tlp.install	2019-04-02 19:05:16.000000000 +0200
@@ -1,5 +1,6 @@
 etc/default/tlp
 etc/init.d/tlp
+lib/elogind/system-sleep/49-tlp-sleep
 lib/systemd/system/tlp.service
 lib/systemd/system/tlp-sleep.service
 lib/udev/rules.d/85-tlp.rules
@@ -12,15 +13,13 @@
 usr/bin/tlp-usblist
 usr/bin/wifi
 usr/bin/wwan
-usr/lib/pm-utils/sleep.d/49tlp
 usr/sbin/tlp
 usr/share/bash-completion/completions/bluetooth
 usr/share/bash-completion/completions/tlp
 usr/share/bash-completion/completions/tlp-stat
 usr/share/bash-completion/completions/wifi
 usr/share/bash-completion/completions/wwan
-usr/share/tlp/tlp-functions
-usr/share/tlp/tlp-nop
-usr/share/tlp/tlp-rf-func
+usr/share/tlp/tlp-func-base
+usr/share/tlp/func.d/*
 usr/share/tlp/tpacpi-bat
 usr/share/metainfo/de.linrunner.tlp.metainfo.xml
diff -Nru tlp-1.1/debian/tlp.lintian-overrides tlp-1.2.1/debian/tlp.lintian-overrides
--- tlp-1.1/debian/tlp.lintian-overrides	2018-03-27 15:02:15.000000000 +0200
+++ tlp-1.2.1/debian/tlp.lintian-overrides	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-# This unusual WantedBy target is intentional
-tlp: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/tlp-sleep.service sleep.target
diff -Nru tlp-1.1/debian/tlp.postinst tlp-1.2.1/debian/tlp.postinst
--- tlp-1.1/debian/tlp.postinst	2018-02-10 17:14:45.000000000 +0100
+++ tlp-1.2.1/debian/tlp.postinst	2019-04-02 19:05:16.000000000 +0200
@@ -3,9 +3,6 @@
 
 #DEBHELPER#
 
-PMD="/usr/lib/pm-utils/power.d"
-ETD="/etc/pm/power.d"
-NOP="/usr/share/tlp/tlp-nop"
 SYD="/lib/systemd/system"
 ESY="/etc/systemd/system"
 NUL="/dev/null"
@@ -13,31 +10,6 @@
 
 case "$1" in
     configure)
-        # Mask conflicting pm-utils hooks:
-        # creating a file in /etc/pm/power.d/ with the same name as the
-        # hook script in /usr/lib/pm-utils/power.d/ is the documented
-        # way to disable pm-utils' builtin hooks
-        #
-        if [ -d ${PMD} ] && [ -d ${ETD} ]; then
-            for i in 95hdparm-apm disable_wol hal-cd-polling intel-audio-powersave harddrive \
-                 laptop-mode journal-commit pci_devices pcie_aspm readahead sata_alpm \
-                     sched-powersave usb_bluetooth wireless xfs_buffer; do
-                if [ -x ${PMD}/${i} ]; then
-                    # Executable hook in /usr/lib/pm-utils/power.d/ exists
-                    if [ -f ${ETD}/${i} ]; then
-                        # Exclude symlinks to tlp-nop
-                        if ! readlink ${ETD}/${i} | egrep -q 'tlp-nop$' ; then
-                            # Move aside superseding hook of same name in /etc/pm/power.d/
-                            mv -n ${ETD}/${i} ${ETD}/${i}.tlp-save
-                        fi
-                    fi
-                    # Make a soft link to tlp-nop in /etc/pm/power.d/
-                    # to disable corresponding hook /usr/lib/pm-utils/power.d/
-                    ln -sf ${NOP} ${ETD}/${i}
-                fi
-            done
-        fi
-
         # Mask conflicting upstart jobs (Package rfkill in Ubuntu)
         for i in /etc/init/rfkill-*.conf; do
             if [ -f "$i" ] && [ ! -f "${i%.conf}.override" ]; then
diff -Nru tlp-1.1/debian/tlp.postrm tlp-1.2.1/debian/tlp.postrm
--- tlp-1.1/debian/tlp.postrm	2018-02-10 17:14:45.000000000 +0100
+++ tlp-1.2.1/debian/tlp.postrm	2019-04-02 19:05:16.000000000 +0200
@@ -3,8 +3,6 @@
 
 #DEBHELPER#
 
-PMD="/usr/lib/pm-utils/power.d"
-ETD="/etc/pm/power.d"
 SYD="/lib/systemd/system"
 ESY="/etc/systemd/system"
 NUL="/dev/null"
@@ -12,15 +10,6 @@
 
 case "$1" in
     remove)
-        # Unmask pm-utils hooks
-        for i in 95hdparm-apm disable_wol hal-cd-polling intel-audio-powersave harddrive \
-            laptop-mode journal-commit pci_devices pcie_aspm readahead sata_alpm \
-            sched-powersave usb_bluetooth wireless xfs_buffer; do
-            if readlink ${ETD}/${i} | egrep -q 'tlp-nop$' ; then
-                rm ${ETD}/${i}
-            fi
-        done
-
         # Unmask conflicting upstart jobs (Package rfkill in Ubuntu)
         for i in /etc/init/rfkill-*.override; do
             [ ! -f "$i" ] || rm -f $i
diff -Nru tlp-1.1/debian/tlp-rdw.install tlp-1.2.1/debian/tlp-rdw.install
--- tlp-1.1/debian/tlp-rdw.install	2018-02-10 17:14:45.000000000 +0100
+++ tlp-1.2.1/debian/tlp-rdw.install	2019-04-02 19:05:16.000000000 +0200
@@ -1,3 +1,5 @@
+usr/bin/tlp-rdw
 etc/NetworkManager/dispatcher.d/99tlp-rdw-nm
 lib/udev/rules.d/85-tlp-rdw.rules
 lib/udev/tlp-rdw-udev
+usr/share/bash-completion/completions/tlp-rdw
diff -Nru tlp-1.1/debian/tlp-rdw.manpages tlp-1.2.1/debian/tlp-rdw.manpages
--- tlp-1.1/debian/tlp-rdw.manpages	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/debian/tlp-rdw.manpages	2019-04-02 19:05:16.000000000 +0200
@@ -0,0 +1 @@
+man-rdw/*
diff -Nru tlp-1.1/default tlp-1.2.1/default
--- tlp-1.1/default	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/default	2019-03-18 18:08:25.000000000 +0100
@@ -1,32 +1,42 @@
 # ------------------------------------------------------------------------------
 # tlp - Parameters for power saving
-# See full explanation: http://linrunner.de/en/tlp/docs/tlp-configuration.html
+# See full explanation: https://linrunner.de/en/tlp/docs/tlp-configuration.html
 
-# Hint: some features are disabled by default, remove the leading # to enable
-# them.
+# Notes:
+# - Some parameters are disabled, remove the leading '#' to enable # them;
+#   shown values are suggestions not defaults
+# - Default *: intrinsic default that is effective when the parameter is missing
+#   or disabled by a leading '#'; use PARAM="" to disable intrinsic defaults for
+#   parameters with text string values
+# - Default <none>: do nothing or use kernel/hardware defaults
 
 # Set to 0 to disable, 1 to enable TLP.
+# Default: 0
 TLP_ENABLE=1
 
 # Operation mode when no power supply can be detected: AC, BAT.
 # Concerns some desktop and embedded hardware only.
+# Default: <none>
 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.
+# Default: 0
 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.
+# Default: 0 (AC), 2 (BAT)
 DISK_IDLE_SECS_ON_AC=0
 DISK_IDLE_SECS_ON_BAT=2
 
 # Dirty page values (timeouts in secs).
+# Default: 15 (AC + BAT)
 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.
+# Note: CPU parameters below are disabled by default, remove the leading #
+# to enable them, otherwise kernel defaults will be used.
 
 # Select a CPU frequency scaling governor.
 # Intel Core i processor with intel_pstate driver:
@@ -34,19 +44,21 @@
 # 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.
+# 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.
+# Default: <none>
 #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
+# Possible values depend on your CPU. For available frequencies see
 # the output of tlp-stat -p.
+# Default: <none>
 #CPU_SCALING_MIN_FREQ_ON_AC=0
 #CPU_SCALING_MAX_FREQ_ON_AC=0
 #CPU_SCALING_MIN_FREQ_ON_BAT=0
@@ -56,6 +68,7 @@
 #   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.
+# Default: <none>
 CPU_HWP_ON_AC=balance_performance
 CPU_HWP_ON_BAT=balance_power
 
@@ -63,6 +76,7 @@
 # Limit the max/min P-state to control the power dissipation of the CPU.
 # Values are stated as a percentage of the available performance.
 # Requires an Intel Core i processor with intel_pstate driver.
+# Default: <none>
 #CPU_MIN_PERF_ON_AC=0
 #CPU_MAX_PERF_ON_AC=100
 #CPU_MIN_PERF_ON_BAT=0
@@ -73,39 +87,47 @@
 # Important:
 # - This may conflict with your distribution's governor settings
 # - A value of 1 does *not* activate boosting, it just allows it
+# Default: <none>
 #CPU_BOOST_ON_AC=1
 #CPU_BOOST_ON_BAT=0
 
 # Minimize number of used CPU cores/hyper-threads under light load conditions:
 #   0=disable, 1=enable.
+# Default: <none>
 SCHED_POWERSAVE_ON_AC=0
 SCHED_POWERSAVE_ON_BAT=1
 
 # Kernel NMI Watchdog:
 #   0=disable (default, saves power), 1=enable (for kernel debugging only).
+# Default: <none>
 NMI_WATCHDOG=0
 
 # Change CPU voltages aka "undervolting" - Kernel with PHC patch required.
 # Frequency voltage pairs are written to:
 #   /sys/devices/system/cpu/cpu0/cpufreq/phc_controls
 # CAUTION: only use this, if you thoroughly understand what you are doing!
+# Default: <none>.
 #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.
+# Default: <none>
 ENERGY_PERF_POLICY_ON_AC=performance
 ENERGY_PERF_POLICY_ON_BAT=power
 
-# Disk devices; separate multiple devices with spaces (default: sda).
+# Disk devices; separate multiple devices with spaces.
 # Devices can be specified by disk ID also (lookup with: tlp diskid).
-DISK_DEVICES="sda sdb"
+# Note: DISK parameters below are effective only when this option is configured.
+# Default: "nvme0n1 sda"
+DISK_DEVICES="nvme0n1 sda"
 
 # Disk advanced power management level: 1..254, 255 (max saving, min, off).
 # Levels 1..127 may spin down the disk; 255 allowable on most drives.
 # Separate values for multiple disks with spaces. Use the special value 'keep'
 # to keep the hardware default for the particular disk.
+# Default: <none>
 DISK_APM_LEVEL_ON_AC="254 254"
 DISK_APM_LEVEL_ON_BAT="128 128"
 
@@ -116,177 +138,247 @@
 # See 'man hdparm' for details.
 # Separate values for multiple disks with spaces. Use the special value 'keep'
 # to keep the hardware default for the particular disk.
+# Default: <none>
 #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).
+# Select I/O scheduler for the disk devices.
+# Multi queue (blk-mq) schedulers:
+#   mq-deadline(*), none, kyber, bfq
+# Single queue schedulers:
+#   deadline(*), cfq, bfq, noop
+# (*) recommended.
 # Separate values for multiple disks with spaces. Use the special value 'keep'
 # to keep the kernel default scheduler for the particular disk.
-#DISK_IOSCHED="cfq cfq"
+# Notes:
+# - Multi queue (blk-mq) may need kernel boot option 'scsi_mod.use_blk_mq=1'
+#   and 'modprobe mq-deadline-iosched|kyber|bfq' on kernels < 5.0
+# - Single queue schedulers are legacy now and were removed together with
+#   the old block layer in kernel 5.0
+# Default: keep
+#DISK_IOSCHED="mq-deadline mq-deadline"
 
 # AHCI link power management (ALPM) for disk devices:
 #   min_power, med_power_with_dipm(*), medium_power, max_performance.
 # (*) Kernel >= 4.15 required, then recommended.
 # Multiple values separated with spaces are tried sequentially until success.
+# Default: <none>
 SATA_LINKPWR_ON_AC="med_power_with_dipm max_performance"
 SATA_LINKPWR_ON_BAT="med_power_with_dipm min_power"
 
 # Exclude host devices from AHCI link power management.
 # Separate multiple hosts with spaces.
+# Default: <none>
 #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.
+# EXPERIMENTAL ** WARNING: auto may cause system lockups/data loss.
+# Default: <none>
 #AHCI_RUNTIME_PM_ON_AC=on
 #AHCI_RUNTIME_PM_ON_BAT=on
 
 # Seconds of inactivity before disk is suspended.
+# Note: effective only when AHCI_RUNTIME_PM_ON_AC/BAT is activated.
+# Default: 15
 AHCI_RUNTIME_PM_TIMEOUT=15
 
 # PCI Express Active State Power Management (PCIe ASPM):
-#   default, performance, powersave.
-PCIE_ASPM_ON_AC=performance
-PCIE_ASPM_ON_BAT=powersave
+#   default(*), performance, powersave.
+# (*) keeps BIOS ASPM defaults (recommended)
+# Default: <none>
+#PCIE_ASPM_ON_AC=default
+#PCIE_ASPM_ON_BAT=default
+
+# Set the min/max/turbo frequency for the Intel GPU.
+# Possible values depend on your hardware. For available frequencies see
+# the output of tlp-stat -g.
+# Default: <none>
+#INTEL_GPU_MIN_FREQ_ON_AC=0
+#INTEL_GPU_MIN_FREQ_ON_BAT=0
+#INTEL_GPU_MAX_FREQ_ON_AC=0
+#INTEL_GPU_MAX_FREQ_ON_BAT=0
+#INTEL_GPU_BOOST_FREQ_ON_AC=0
+#INTEL_GPU_BOOST_FREQ_ON_BAT=0
 
 # Radeon graphics clock speed (profile method): low, mid, high, auto, default;
-# auto = mid on BAT, high on AC; default = use hardware defaults.
-RADEON_POWER_PROFILE_ON_AC=high
-RADEON_POWER_PROFILE_ON_BAT=low
+# auto = mid on BAT, high on AC.
+# Default: default
+RADEON_POWER_PROFILE_ON_AC=default
+RADEON_POWER_PROFILE_ON_BAT=default
 
 # Radeon dynamic power management method (DPM): battery, performance.
+# Default: <none>
 RADEON_DPM_STATE_ON_AC=performance
 RADEON_DPM_STATE_ON_BAT=battery
 
 # Radeon DPM performance level: auto, low, high; auto is recommended.
+# Note: effective only when RADEON_DPM_STATE_ON_AC/BAT is activated.
+# Default: auto
 RADEON_DPM_PERF_LEVEL_ON_AC=auto
 RADEON_DPM_PERF_LEVEL_ON_BAT=auto
 
 # WiFi power saving mode: on=enable, off=disable; not supported by all adapters.
+# Default: <none>
 WIFI_PWR_ON_AC=off
 WIFI_PWR_ON_BAT=on
 
 # Disable wake on LAN: Y/N.
+# Default: 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).
+# Default: <none>
 SOUND_POWER_SAVE_ON_AC=0
 SOUND_POWER_SAVE_ON_BAT=1
 
 # Disable controller too (HDA only): Y/N.
+# Note: effective only when SOUND_POWER_SAVE_ON_AC/BAT is activated.
+# Default: Y
 SOUND_POWER_SAVE_CONTROLLER=Y
 
 # Power off optical drive in UltraBay/MediaBay: 0=disable, 1=enable.
 # Drive can be powered on again by releasing (and reinserting) the eject lever
 # or by pressing the disc eject button on newer models.
 # Note: an UltraBay/MediaBay hard disk is never powered off.
+# Default: 0
 BAY_POWEROFF_ON_AC=0
 BAY_POWEROFF_ON_BAT=0
-# Optical drive device to power off (default sr0).
+# Optical drive device to power off
+# Default: sr0
 BAY_DEVICE="sr0"
 
 # Runtime Power Management for PCI(e) bus devices: on=disable, auto=enable.
+# Default: <none>
 RUNTIME_PM_ON_AC=on
 RUNTIME_PM_ON_BAT=auto
 
 # Exclude PCI(e) device adresses the following list from Runtime PM
 # (separate with spaces). Use lspci to get the adresses (1st column).
+# Default: <none>
 #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"
+# Default: "amdgpu mei_me nouveau nvidia pcieport radeon", use "" to disable
+# completely.
+#RUNTIME_PM_DRIVER_BLACKLIST="amdgpu mei_me nouveau nvidia pcieport radeon"
 
 # Set to 0 to disable, 1 to enable USB autosuspend feature.
+# Default: 0
 USB_AUTOSUSPEND=1
 
 # Exclude listed devices from USB autosuspend (separate with spaces).
 # Use lsusb to get the ids.
 # Note: input devices (usbhid) are excluded automatically
+# Default: <none>
 #USB_BLACKLIST="1111:2222 3333:4444"
 
 # Bluetooth devices are excluded from USB autosuspend:
 #   0=do not exclude, 1=exclude.
+# Default: 0
 USB_BLACKLIST_BTUSB=0
 
 # Phone devices are excluded from USB autosuspend:
 #   0=do not exclude, 1=exclude (enable charging).
+# Default: 0
 USB_BLACKLIST_PHONE=0
 
 # Printers are excluded from USB autosuspend:
 #   0=do not exclude, 1=exclude.
+# Default: 1
 USB_BLACKLIST_PRINTER=1
 
 # WWAN devices are excluded from USB autosuspend:
 #   0=do not exclude, 1=exclude.
-USB_BLACKLIST_WWAN=1
+# Default: 0
+USB_BLACKLIST_WWAN=0
 
 # Include listed devices into USB autosuspend even if already excluded
-# by the blacklists above (separate with spaces).
-# Use lsusb to get the ids.
+# by the blacklists above (separate with spaces). Use lsusb to get the ids.
+# Default: <none>
 #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).
+# Default: 0
 #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!
+# Note: the parameters DEVICES_TO_DISABLE/ENABLE_ON_STARTUP/SHUTDOWN below
+#   are ignored when this is enabled.
+# Default: 0
 RESTORE_DEVICE_STATE_ON_STARTUP=0
 
 # Radio devices to disable on startup: bluetooth, wifi, wwan.
 # Separate multiple devices with spaces.
+# Default: <none>
 #DEVICES_TO_DISABLE_ON_STARTUP="bluetooth wifi wwan"
 
 # Radio devices to enable on startup: bluetooth, wifi, wwan.
 # Separate multiple devices with spaces.
+# Default: <none>
 #DEVICES_TO_ENABLE_ON_STARTUP="wifi"
 
 # Radio devices to disable on shutdown: bluetooth, wifi, wwan.
 # (workaround for devices that are blocking shutdown).
+# Default: <none>
 #DEVICES_TO_DISABLE_ON_SHUTDOWN="bluetooth wifi wwan"
 
 # Radio devices to enable on shutdown: bluetooth, wifi, wwan.
 # (to prevent other operating systems from missing radios).
+# Default: <none>
 #DEVICES_TO_ENABLE_ON_SHUTDOWN="wwan"
 
 # Radio devices to enable on AC: bluetooth, wifi, wwan.
+# Default: <none>
 #DEVICES_TO_ENABLE_ON_AC="bluetooth wifi wwan"
 
 # Radio devices to disable on battery: bluetooth, wifi, wwan.
+# Default: <none>
 #DEVICES_TO_DISABLE_ON_BAT="bluetooth wifi wwan"
 
 # Radio devices to disable on battery when not in use (not connected):
 #   bluetooth, wifi, wwan.
+# Default: <none>
 #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 %)
+# Default: <none>
 #START_CHARGE_THRESH_BAT0=75
 #STOP_CHARGE_THRESH_BAT0=80
 # Ultrabay / Slice / Replaceable battery (values in %)
+# Default: <none>
 #START_CHARGE_THRESH_BAT1=75
 #STOP_CHARGE_THRESH_BAT1=80
 
 # Restore charge thresholds when AC is unplugged: 0=disable, 1=enable.
+# Default: 0
 #RESTORE_THRESHOLDS_ON_BAT=1
 
+# Battery feature drivers: 0=disable, 1=enable
+# Default: 1 (all)
+NATACPI_ENABLE=1
+TPACPI_ENABLE=1
+TPSMAPI_ENABLE=1
+
 # ------------------------------------------------------------------------------
 # tlp-rdw - Parameters for the radio device wizard
 # Possible devices: bluetooth, wifi, wwan.
 
-# Hints:
+# Notes:
 # - Parameters are disabled by default, remove the leading # to enable them
 # - Separate multiple radio devices with spaces
 
+# Default: <none> (for all parameters below)
+
 # Radio devices to disable on connect.
 #DEVICES_TO_DISABLE_ON_LAN_CONNECT="wifi wwan"
 #DEVICES_TO_DISABLE_ON_WIFI_CONNECT="wwan"
diff -Nru tlp-1.1/de.linrunner.tlp.metainfo.xml tlp-1.2.1/de.linrunner.tlp.metainfo.xml
--- tlp-1.1/de.linrunner.tlp.metainfo.xml	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/de.linrunner.tlp.metainfo.xml	2019-03-18 18:08:25.000000000 +0100
@@ -38,7 +38,7 @@
 
   <project_license>GPL-2.0+</project_license>
   <developer_name>Thomas Koch</developer_name>
-  <url type="homepage">http://linrunner.de/tlp</url>
+  <url type="homepage">https://linrunner.de/tlp</url>
 
   <provides>
     <!-- Match "Portable" chassis type -->
diff -Nru tlp-1.1/func.d/05-tlp-func-pm tlp-1.2.1/func.d/05-tlp-func-pm
--- tlp-1.1/func.d/05-tlp-func-pm	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/05-tlp-func-pm	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,236 @@
+#!/bin/sh
+# tlp-func-pm - Device Power Management Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly ETHTOOL=ethtool
+
+readonly PCID=/sys/bus/pci/devices
+readonly PCIDRV=/sys/bus/pci/drivers
+
+readonly DEFAULT_RUNTIME_PM_DRIVER_BLACKLIST="amdgpu mei_me nouveau nvidia pcieport radeon"
+readonly DEFAULT_SOUND_POWER_SAVE_CONTROLLER=Y
+readonly DEFAULT_WOL_DISABLE=N
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- PCI(e) Devices
+
+set_runtime_pm () { # set runtime power management
+    # $1: 0=ac mode, 1=battery mode
+
+    local address class ccontrol control device driver drv_bl
+    local pci_bl_adr pci_bl_drv type vendor
+
+    if [ "$1" = "1" ]; then
+        ccontrol=${RUNTIME_PM_ON_BAT:-}
+    else
+        ccontrol=${RUNTIME_PM_ON_AC:-}
+    fi
+
+    if [ -z "$ccontrol" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_runtime_pm($1).not_configured"
+        return 0
+    fi
+
+    # driver specific blacklist:
+    # - undefined = use internal default from $DEFAULT_PM_DRIVER_BLACKLIST
+    # - empty     = disable feature
+    drv_bl="${RUNTIME_PM_DRIVER_BLACKLIST-${DEFAULT_RUNTIME_PM_DRIVER_BLACKLIST}}"
+
+    # pci address blacklisting
+    pci_bl_adr=${RUNTIME_PM_BLACKLIST:-}
+
+    # pci driver blacklisting: corresponding pci addresses
+    pci_bl_drv=""
+
+    # cumulate pci addresses for devices with blacklisted drivers
+    for driver in $drv_bl; do # iterate list
+        if [ -n "$driver" ] && [ -d $PCIDRV/$driver ]; then
+            # driver is active --> iterate over assigned devices
+            for device in $PCIDRV/$driver/0000:*; do
+                # get short device address
+                address=${device##/*/0000:}
+
+                # add to list when not already contained
+                if ! wordinlist "$address" "$pci_bl_drv"; then
+                    pci_bl_drv="$pci_bl_drv $address"
+                fi
+            done # for device
+        fi # if driver
+    done # for driver
+
+    # iterate pci(e) devices
+    for type in $PCID; do
+        for device in $type/*; do
+            if [ -f $device/power/control ]; then
+                # get short device address, class
+                address=${device##/*/0000:}
+                class=$(read_sysf $device/class)
+
+                if wordinlist "$address" "$pci_bl_adr"; then
+                    # device is in address blacklist
+                    echo_debug "pm" "set_runtime_pm($1).bloack_address: $device [$class]"
+                elif wordinlist "$address" "$pci_bl_drv"; then
+                    # device is in driver blacklist
+                    echo_debug "pm" "set_runtime_pm($1).black_driver: $device [$class]"
+                else
+                    control=$ccontrol
+
+                    # check for Nvidia gpu's
+                    if wordinlist "nouveau" "$drv_bl" || wordinlist "nvidia" "$drv_bl"; then
+                        # driver nouveau or nvidia is in blacklist
+                        # --> blacklist depending on vendor and class
+                        vendor=$(read_sysf $device/vendor)
+
+                        if [ "$vendor" = "0x10de" ]; then
+                            # vendor nvidia
+                            # --> check for display or 3d controller class
+                            case $class in
+                                "0x030000"|"0x030200") control="black_nvgpu" ;;
+                            esac
+                        fi
+                    fi # if nouveau | nvidia blacklisted
+
+                    case $control in
+                        auto|on)
+                            write_sysf $control $device/power/control
+                            echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]; rc=$?"
+                            ;;
+
+                        *) # black_* --> do nothing
+                            echo_debug "pm" "set_runtime_pm($1).$control: $device [$class]"
+                            ;;
+                    esac
+                fi # if blacklist
+            fi # if control
+        done # for device
+    done # for type
+
+    return 0
+}
+
+set_pcie_aspm () { # set pcie active state power management
+    # $1: 0=ac mode, 1=battery mode
+
+    local pwr
+
+    if [ "$1" = "1" ]; then
+        pwr=${PCIE_ASPM_ON_BAT:-}
+    else
+        pwr=${PCIE_ASPM_ON_AC:-}
+    fi
+
+    if [ -z "$pwr" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_pcie_aspm($1).not_configured"
+        return 0
+    fi
+
+    if [ -f /sys/module/pcie_aspm/parameters/policy ]; then
+        if write_sysf "$pwr" /sys/module/pcie_aspm/parameters/policy; then
+            echo_debug "pm" "set_pcie_aspm($1): $pwr"
+        else
+            echo_debug "pm" "set_pcie_aspm($1).disabled_by_kernel"
+        fi
+    else
+        echo_debug "pm" "set_pcie_aspm($1).not_available"
+    fi
+
+    return 0
+}
+
+# -- Audio Devices
+
+set_sound_power_mode () { # set sound chip power modes
+    # $1: 0=ac mode, 1=battery mode
+
+    local pwr cpwr
+
+    # new config param
+    if [ "$1" = "1" ]; then
+        pwr=${SOUND_POWER_SAVE_ON_BAT:-}
+    else
+        pwr=${SOUND_POWER_SAVE_ON_AC:-}
+    fi
+
+    # when unconfigured consider legacy config param
+    [ -z "$pwr" ] && pwr=${SOUND_POWER_SAVE:-}
+
+    if [ -z "$pwr" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_sound_power_mode($1).not_configured"
+        return 0
+    fi
+
+    cpwr=${SOUND_POWER_SAVE_CONTROLLER:-${DEFAULT_SOUND_POWER_SAVE_CONTROLLER}}
+
+    check_sysfs "set_sound_power_mode" "/sys/module"
+
+    if [ -d /sys/module/snd_hda_intel ]; then
+        write_sysf "$pwr" /sys/module/snd_hda_intel/parameters/power_save
+        echo_debug "pm" "set_sound_power_mode($1).hda: $pwr; rc=$?"
+
+        if [ "$pwr" = "0" ]; then
+            write_sysf "N" /sys/module/snd_hda_intel/parameters/power_save_controller
+            echo_debug "pm" "set_sound_power_mode($1).hda_controller: N controller=$cpwr; rc=$?"
+        else
+            write_sysf "$cpwr" /sys/module/snd_hda_intel/parameters/power_save_controller
+            echo_debug "pm" "set_sound_power_mode($1).hda_controller: $cpwr; rc=$?"
+        fi
+    fi
+
+    if [ -d /sys/module/snd_ac97_codec ]; then
+        write_sysf "$pwr" /sys/module/snd_ac97_codec/parameters/power_save
+        echo_debug "pm" "set_sound_power_mode($1).ac97: $pwr; rc=$?"
+    fi
+
+    return 0
+}
+
+# --- LAN Devices
+
+get_ethifaces () { # get all eth devices -- retval: $_ethifaces
+    local ei eic
+    _ethifaces=""
+
+    for eic in $NETD/*/device/class; do
+        if [ "$(read_sysf $eic)" = "0x020000" ] \
+            && [ ! -d "${eic%/class}/ieee80211" ]; then
+
+            ei=${eic%/device/class}; ei=${ei##*/}
+            _ethifaces="$_ethifaces $ei"
+        fi
+    done
+
+    _ethifaces="${_ethifaces# }"
+    return 0
+}
+
+disable_wake_on_lan () {  # disable WOL
+    local ei
+
+    WOL_DISABLE=${WOL_DISABLE:-${DEFAULT_WOL_DISABLE}}
+
+    if [ "$WOL_DISABLE" = "Y" ]; then
+        get_ethifaces
+        for ei in $_ethifaces; do
+            $ETHTOOL -s "$ei" wol d > /dev/null 2>&1
+            echo_debug "pm" "disable_wake_on_lan: $ei; rc=$?"
+        done
+    else
+        echo_debug "pm" "disable_wake_on_lan.disabled"
+    fi
+
+    return 0
+}
diff -Nru tlp-1.1/func.d/10-tlp-func-cpu tlp-1.2.1/func.d/10-tlp-func-cpu
--- tlp-1.1/func.d/10-tlp-func-cpu	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/10-tlp-func-cpu	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,340 @@
+#!/bin/sh
+# tlp-func-cpu - Processor Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly ENERGYPERF=x86_energy_perf_policy
+
+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
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Scaling Governor
+
+check_intel_pstate () { # detect intel_pstate driver -- rc: 0=present/1=absent
+    #  Note: intel_pstate requires Linux 3.9 or higher
+    [ -d $INTEL_PSTATED ]
+}
+
+set_scaling_governor () { # set scaling governor -- $1: 0=ac mode, 1=battery mode
+    local gov cpu ec
+
+    if [ "$1" = "1" ]; then
+        gov=${CPU_SCALING_GOVERNOR_ON_BAT:-}
+    else
+        gov=${CPU_SCALING_GOVERNOR_ON_AC:-}
+    fi
+
+    if [ -n "$gov" ]; then
+        ec=0
+        for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
+            if [ -f "$cpu" ] && ! write_sysf "$gov" $cpu; then
+                echo_debug "pm" "set_scaling_governor($1).write_error: $cpu $gov; rc=$?"
+                ec=$((ec+1))
+            fi
+        done
+        echo_debug "pm" "set_scaling_governor($1): $gov; ec=$?"
+    else
+        echo_debug "pm" "set_scaling_governor($1).not_configured"
+    fi
+
+    return 0
+}
+
+set_scaling_min_max_freq () { # set scaling limits -- $1: 0=ac mode, 1=battery mode
+    local minfreq maxfreq cpu ec
+    local conf=0
+
+    if [ "$1" = "1" ]; then
+        minfreq=${CPU_SCALING_MIN_FREQ_ON_BAT:-}
+        maxfreq=${CPU_SCALING_MAX_FREQ_ON_BAT:-}
+    else
+        minfreq=${CPU_SCALING_MIN_FREQ_ON_AC:-}
+        maxfreq=${CPU_SCALING_MAX_FREQ_ON_AC:-}
+    fi
+
+    if [ -n "$minfreq" ] && [ "$minfreq" != "0" ]; then
+        conf=1
+        ec=0
+        for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq; do
+            if [ -f "$cpu" ] && ! write_sysf "$minfreq" $cpu; then
+                echo_debug "pm" "set_scaling_min_max_freq($1).min.write_error: $cpu $minfreq; rc=$?"
+                ec=$((ec+1))
+            fi
+        done
+        echo_debug "pm" "set_scaling_min_max_freq($1).min: $minfreq; ec=$ec"
+    fi
+
+    if [ -n "$maxfreq" ] && [ "$maxfreq" != "0" ]; then
+        conf=1
+        ec=0
+        for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do
+            if [ -f "$cpu" ] && ! write_sysf "$maxfreq" $cpu; then
+                echo_debug "pm" "set_scaling_min_max_freq($1).max.write_error: $cpu $maxfreq; rc=$?"
+                ec=$((ec+1))
+            fi
+            echo_debug "pm" "set_scaling_min_max_freq($1).max: $maxfreq; ec=$ec"
+        done
+    fi
+
+    [ $conf -eq 1 ] || echo_debug "pm" "set_scaling_min_max_freq($1).not_configured"
+    return 0
+}
+
+# --- Performance Policies
+
+set_cpu_hwp_pref () { # set HWP energy performance hints -- $1: 0=ac mode, 1=battery mode
+    local hwpp avail cpu ec
+
+    if ! check_intel_pstate; 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
+        avail=0
+        ec=0
+        for cpu in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
+            if [ -f "$cpu" ]; then
+                avail=1
+                if  ! write_sysf "$hwpp" $cpu; then
+                    echo_debug "pm" "set_cpu_hwp_pref($1).write_error: $cpu $hwpp; rc=$"
+                    ec=$((ec+1))
+                fi
+            fi
+        done
+        if [ "$avail" = "1" ]; then
+            echo_debug "pm" "set_cpu_hwp_pref($1): $hwpp; ec=$ec"
+        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
+
+
+    if ! check_intel_pstate; 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
+        write_sysf "$min" $CPU_MIN_PERF_PCT
+        echo_debug "pm" "set_cpu_perf_pct($1).min: $min; rc=$?"
+    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
+        write_sysf "$max" $CPU_MAX_PERF_PCT
+        echo_debug "pm" "set_cpu_perf_pct($1).max: $max; rc=$?"
+    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
+
+    if [ "$1" = "1" ]; then
+        val=${CPU_BOOST_ON_BAT:-}
+    else
+        val=${CPU_BOOST_ON_AC:-}
+    fi
+
+    if [ -z "$val" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_cpu_boost_all($1).not_configured"
+        return 0
+    fi
+
+    if check_intel_pstate; then
+        # use intel_pstate sysfiles, invert value
+        if write_sysf "$((val ^ 1))" $CPU_TURBO_PSTATE; then
+            echo_debug "pm" "set_cpu_boost_all($1).intel_pstate: $val"
+        else
+            echo_debug "pm" "set_cpu_boost_all($1).intel_pstate.cpu_not_supported"
+        fi
+    elif [ -f $CPU_BOOST_ALL_CTRL ]; then
+        # use acpi_cpufreq sysfiles
+        # simple test for attribute "w" doesn't work, so actually write
+        if write_sysf "$val" $CPU_BOOST_ALL_CTRL; then
+            echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq: $val"
+        else
+            echo_debug "pm" "set_cpu_boost_all($1).acpi_cpufreq.cpu_not_supported"
+        fi
+    else
+        echo_debug "pm" "set_cpu_boost_all($1).not_available"
+    fi
+
+    return 0
+}
+
+set_sched_powersave () { # set multi-core/-thread powersave policy
+    # $1: 0=ac mode, 1=battery mode
+
+    local pwr pool sdev
+    local avail=0
+
+    if [ "$1" = "1" ]; then
+        pwr=${SCHED_POWERSAVE_ON_BAT:-}
+    else
+        pwr=${SCHED_POWERSAVE_ON_AC:-}
+    fi
+
+    if [ -z "$pwr" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_sched_powersave($1).not_configured"
+        return 0
+    fi
+
+    for pool in mc smp smt; do
+        sdev="/sys/devices/system/cpu/sched_${pool}_power_savings"
+        if [ -f "$sdev" ]; then
+            write_sysf "$pwr" $sdev
+            echo_debug "pm" "set_sched_powersave($1): ${sdev##/*/} $pwr; rc=$?"
+            avail=1
+        fi
+    done
+
+    [ "$avail" = "1" ] || echo_debug "pm" "set_sched_powersave($1).not_available"
+
+    return 0
+}
+
+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
+}
+
+# --- Misc
+
+set_nmi_watchdog () { # enable/disable nmi watchdog
+    local nmiwd=${NMI_WATCHDOG:-}
+
+    if [ -z "$nmiwd" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_nmi_watchdog.not_configured"
+        return 0
+    fi
+
+    if [ -f /proc/sys/kernel/nmi_watchdog ]; then
+        if write_sysf "$nmiwd" /proc/sys/kernel/nmi_watchdog; then
+            echo_debug "pm" "set_nmi_watchdog: $nmiwd; rc=$?"
+        else
+            echo_debug "pm" "set_nmi_watchdog.disabled_by_kernel: $nmiwd"
+        fi
+    else
+        echo_debug "pm" "set_nmi_watchdog.not_available"
+    fi
+
+    return 0
+}
+
+set_phc_controls () { # set core voltages
+    local control
+    local ctrl_avail="0"
+
+    phc_controls=${PHC_CONTROLS:-}
+
+    if [ -z "$phc_controls" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_phc_controls.not_configured"
+        return 0
+    fi
+
+    for control in /sys/devices/system/cpu/cpu*/cpufreq/phc_controls; do
+        if [ -f "$control" ]; then
+            write_sysf "$phc_controls" $control
+            echo_debug "pm" "set_phc_controls: $control $phc_controls; rc=$?"
+            ctrl_avail="1"
+        fi
+    done
+
+    [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_phc_controls.not_available"
+
+    return 0
+}
diff -Nru tlp-1.1/func.d/15-tlp-func-disk tlp-1.2.1/func.d/15-tlp-func-disk
--- tlp-1.1/func.d/15-tlp-func-disk	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/15-tlp-func-disk	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,467 @@
+#!/bin/sh
+# tlp-func-disk - Storage Device and Filesystem Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly AHCID=$PCID'/*/ata*'
+readonly BLOCKD='/sys/block/nvme* /sys/block/sd*'
+
+readonly DEFAULT_DISK_DEVICES="nvme0n1 sda"
+readonly DEFAULT_DISK_IDLE_SECS_ON_AC=0
+readonly DEFAULT_DISK_IDLE_SECS_ON_BAT=2
+readonly DEFAULT_DISK_IOSCHED="keep"
+readonly DEFAULT_MAX_LOST_WORK_SECS=15
+readonly DEFAULT_AHCI_RUNTIME_PM_TIMEOUT="15"
+
+readonly DISK_TYPES_NO_APM_CHANGE="usb ieee1394"
+readonly DISK_NOP_WORDS="_ keep"
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Device Helpers
+
+get_disk_dev () { # translate disk id to device (sdX)
+    # $1: id or dev;
+    # retval: $_disk_dev, $_disk_id, $_disk_type
+    local path
+
+    if [ -h /dev/disk/by-id/$1 ]; then
+        # $1 is disk id
+        _disk_id=$1
+        _disk_dev=$(printf '%s' "$_disk_id" | sed -r 's/-part[1-9][0-9]*$//')
+        _disk_dev=$(readlink /dev/disk/by-id/$_disk_dev)
+        _disk_dev=${_disk_dev##*/}
+    else
+        # $1 is disk dev
+        _disk_dev=$1
+        _disk_id=""
+    fi
+
+    # determine device type (bus)
+    if [ -b /dev/$_disk_dev ]; then
+        path="$($UDEVADM info -n "$_disk_dev" -q property 2>/dev/null | sed -n 's/^ID_PATH=//p')"
+        case "$path" in
+            pci-*-nvme-*)     _disk_type="nvme" ;;
+            pci-*-ata-*)      _disk_type="ata"  ;;
+            pci-*-usb-*)      _disk_type="usb"  ;;
+            pci-*-ieee1394-*) _disk_type="ieee1394" ;;
+            *)                _disk_type="unknown" ;;
+        esac
+        echo_debug "disk" "get_disk_dev($1): dev=$_disk_dev; type=$_disk_type; path=$path"
+    else
+        _disk_type="none"
+        echo_debug "disk" "get_disk_dev($1).missing"
+    fi
+
+    return 0
+}
+
+check_disk_apm () { # check if disk device supports advanced pm
+    # $1: dev; rc: 0=yes/1=no
+
+    if $HDPARM -i $1 2> /dev/null | grep -q 'AdvancedPM=yes'; then
+        return 0
+    else
+        return 1
+    fi
+}
+
+show_disk_ids () { # show disk id's
+    local dev
+
+    { # iterate SATA and NVMe disks
+      # shellcheck disable=SC2010
+        for dev in $(ls /dev/disk/by-id/ | grep -E '^(ata|ieee1394|nvme|usb)' | grep -E -v '(^nvme-eui|\-part[1-9]+)'); do
+            if [ -n "$dev" ]; then
+                get_disk_dev $dev
+                echo "$_disk_dev: $_disk_id"
+            fi
+        done
+    } | sort
+
+    return 0
+}
+
+# --- Disk APM Features
+
+set_disk_apm_level () { # set disk apm level
+    # $1: 0=ac mode, 1=battery mode
+
+    local pwrmode="$1"
+    local dev log_message
+
+    # when undefined use default
+    : ${DISK_DEVICES=${DEFAULT_DISK_DEVICES}}
+
+    # exit when disabled
+    if [ -z "$DISK_DEVICES" ]; then
+        echo_debug "disk" "set_disk_apm_level($pwrmode).disabled"
+        return 0
+    fi
+
+    # set @argv := apmlist (blanks removed - relying on a sane $IFS)
+    if [ "$pwrmode" = "1" ]; then
+        set -- $DISK_APM_LEVEL_ON_BAT
+    else
+        set -- $DISK_APM_LEVEL_ON_AC
+    fi
+
+    # exit if empty apmlist
+    if [ $# -eq 0 ]; then
+        echo_debug "disk" "set_disk_apm_level($pwrmode).not_configured"
+        return 0
+    fi
+
+    echo_debug "disk" "set_disk_apm_level($pwrmode)"
+
+    # pairwise iteration DISK_DEVICES[1,n], apmlist[1,m]; m > 0
+    #  for j in [1,n]: disk_dev[j], apmlist[min(j,m)]
+    #
+    for dev in $DISK_DEVICES; do
+        : ${1:?BUG: broken DISK_APM_LEVEL list handling}
+
+        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_apm /dev/$_disk_dev || wordinlist "$_disk_type" "$DISK_TYPES_NO_APM_CHANGE"; then
+            echo_debug "disk" "${log_message} -- not supported"
+        elif wordinlist "$1" "$DISK_NOP_WORDS"; then
+            echo_debug "disk" "${log_message} -- keep as is"
+        else
+            $HDPARM -B $1 /dev/$_disk_dev > /dev/null 2>&1
+            echo_debug "disk" "${log_message}; rc=$?"
+        fi
+
+        # 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}}
+
+    # exit when disabled
+    if [ -z "$DISK_DEVICES" ]; then
+        echo_debug "disk" "set_disk_spindown_timeout($pwrmode).disabled"
+        return 0
+    fi
+
+    # set @argv := timeoutlist
+    if [ "$pwrmode" = "1" ]; then
+        set -- $DISK_SPINDOWN_TIMEOUT_ON_BAT
+    else
+        set -- $DISK_SPINDOWN_TIMEOUT_ON_AC
+    fi
+
+    # exit if empty timeoutlist
+    if [ $# -eq 0 ]; then
+        echo_debug "disk" "set_disk_spindown_timeout($pwrmode).not_configured"
+        return 0
+    fi
+
+    echo_debug "disk" "set_disk_spindown_timeout($pwrmode)"
+
+    # pairwise iteration DISK_DEVICES[1,n], timeoutlist[1,m]; m > 0
+    #  for j in [1,n]: disk_dev[j], timeoutlist[min(j,m)]
+    #
+    for dev in $DISK_DEVICES; do
+        : ${1:?BUG: broken DISK_SPINDOWN_TIMEOUT list handling}
+
+        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 wordinlist "$1" "$DISK_NOP_WORDS"; then
+            echo_debug "disk" "${log_message} -- keep as is"
+        else
+            $HDPARM -S $1 /dev/$_disk_dev > /dev/null 2>&1
+            echo_debug "disk" "${log_message}; rc=$?"
+        fi
+
+        # last entry in timeoutlist applies to all remaining disks
+        [ $# -lt 2 ] || shift
+    done
+
+    return 0
+}
+
+spindown_disk () { # stop spindle motor -- $1: dev
+    $HDPARM -y /dev/$1 > /dev/null 2>&1
+
+    return 0
+}
+
+set_disk_iosched () { # set disk io scheduler
+    local dev log_message
+
+    # when undefined use default
+    : ${DISK_DEVICES=${DEFAULT_DISK_DEVICES}}
+
+    # exit when disabled
+    if [ -z "$DISK_DEVICES" ]; then
+        echo_debug "disk" "set_disk_iosched.disabled"
+        return 0
+    fi
+
+    # set @argv := schedlist
+    set -- $DISK_IOSCHED
+
+    # exit if empty schedlist
+    if [ $# -eq 0 ]; then
+        echo_debug "disk" "set_disk_iosched.not_configured"
+        return 0
+    fi
+
+    echo_debug "disk" "set_disk_iosched"
+
+    # pairwise iteration DISK_DEVICES[1,n], schedlist[1,m]; m > 0
+    #  for j in [1,min(n,m)]   : disk_dev[j], schedlistj]
+    #  for j in [min(n,m)+1,n] : disk_dev[j], %DEFAULT_DISK_IO_SCHEDULER
+    for dev in $DISK_DEVICES; do
+        local sched schedctrl
+        get_disk_dev $dev
+
+        # get sched from argv, use default scheduler when list is too short
+        sched=${1:-${DEFAULT_DISK_IOSCHED}}
+        schedctrl="/sys/block/$_disk_dev/queue/scheduler"
+        log_message="set_disk_iosched: $_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
+            write_sysf "$sched" $schedctrl
+            echo_debug "disk" "${log_message}; rc=$?"
+        fi
+
+        # using %DEFAULT_DISK_IO_SCHEDULER when argv is empty
+        [ $# -eq 0 ] || shift
+    done
+
+    return 0
+}
+
+# --- Power Saving
+
+set_sata_link_power () { # set ahci link power management
+    # $1: 0=ac mode, 1=battery mode
+
+    local pm="$1"
+    local host host_bl hostid linkpol pwr rc
+    local pwrlist=""
+    local ctrl_avail="0"
+
+    if [ "$pm" = "1" ]; then
+        pwrlist=${SATA_LINKPWR_ON_BAT:-}
+    else
+        pwrlist=${SATA_LINKPWR_ON_AC:-}
+    fi
+
+    if [ -z "$pwrlist" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_sata_link_power($pm).not_configured"
+        return 0
+    fi
+
+    # ALPM blacklist
+    host_bl=${SATA_LINKPWR_BLACKLIST:-}
+
+    # copy configured values to args
+    set -- $pwrlist
+    # iterate SATA hosts
+    for host in /sys/class/scsi_host/host* ; do
+        linkpol=$host/link_power_management_policy
+        if [ -f $linkpol ]; then
+            hostid=${host##*/}
+            if wordinlist "$hostid" "$host_bl"; then
+                # host blacklisted --> skip
+                echo_debug "pm" "set_sata_link_power($pm).black: $host"
+                ctrl_avail="1"
+            else
+                # host not blacklisted --> iterate all configured values
+                for pwr in "$@"; do
+                    write_sysf "$pwr" $linkpol; rc=$?
+                    echo_debug "pm" "set_sata_link_power($pm).$pwr: $host; rc=$rc"
+                    if [ $rc -eq 0 ]; then
+                        # write successful --> goto next host
+                        ctrl_avail="1"
+                        break
+                    else
+                        # write failed --> don't use this value for remaining hosts
+                        # and try next value
+                        shift
+                    fi
+                done
+            fi
+        fi
+    done
+
+    [ "$ctrl_avail" = "0" ] && echo_debug "pm" "set_sata_link_power($pm).not_available"
+    return 0
+}
+
+set_ahci_runtime_pm () { # set ahci runtime power management
+    # $1: 0=ac mode, 1=battery mode
+
+    local control device ec timeout rc
+
+    if [ "$1" = "1" ]; then
+        control=${AHCI_RUNTIME_PM_ON_BAT:-}
+    else
+        control=${AHCI_RUNTIME_PM_ON_AC:-}
+    fi
+
+    # calc timeout in millisecs
+    timeout="${AHCI_RUNTIME_PM_TIMEOUT-${DEFAULT_AHCI_RUNTIME_PM_TIMEOUT}}"
+    [ -z "$timeout" ] || timeout=$((timeout * 1000))
+
+    # check values
+    case "$control" in
+        on|auto)       ;;
+        *) control="" ;; # invalid input --> unconfigured
+    esac
+
+    if [ -z "$control" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_ahci_runtime_pm($1).not_configured"
+        return 0
+    fi
+
+    # when timeout is unconfigured we're done here
+    if [ -z "$timeout" ]; then
+        echo_debug "pm" "set_ahci_runtime_pm($1).timeout_not_configured"
+        return 0
+    fi
+
+    # iterate disks
+    ec=0
+    for device in $BLOCKD; do
+        if [ -f ${device}/device/power/control ]; then
+            # write timeout first because writing "auto" with the default
+            # timeout -1 still active will lockup the machine!
+            rc=0
+            if write_sysf "$timeout" ${device}/device/power/autosuspend_delay_ms; then
+                # writing timeout was successful --> proceed with activation;
+                if ! write_sysf "$control" ${device}/device/power/control; then
+                    # writing control failed
+                    rc=2
+                    ec=$((ec+1))
+                fi
+            else
+                # writing timeout failed (or file not present)
+                rc=1
+                ec=$((ec+1))
+            fi
+            echo_debug "pm" "set_ahci_runtime_pm($1).$control: disk=$device timeout=$timeout; rc=$rc"
+        fi
+    done
+
+    if [ $ec -gt 0 ]; then
+        # quit: do not touch hosts if write failed for at least one disk
+        echo_debug "pm" "set_ahci_runtime_pm($1).quit; ec=$ec"
+        return 0
+    fi
+
+    # iterate hosts
+    for device in $AHCID; do
+        write_sysf "$control" ${device}/power/control
+        echo_debug "pm" "set_ahci_runtime_pm($1).$control: host=$device; rc=$?"
+    done
+
+    return 0
+}
+
+# --- Filesystem Parameters
+
+set_laptopmode () { # set kernel laptop mode -- $1: 0=ac mode, 1=battery mode
+    check_sysfs "set_laptopmode" "/proc/sys/vm/laptop_mode"
+
+    local isec
+
+    if [ "$1" = "1" ]; then
+        isec=${DISK_IDLE_SECS_ON_BAT:-${DEFAULT_DISK_IDLE_SECS_ON_BAT}}
+    else
+        isec=${DISK_IDLE_SECS_ON_AC:-${DEFAULT_DISK_IDLE_SECS_ON_AC}}
+    fi
+    # replace with empty string if non-numeric chars are contained
+    isec=$(printf '%s' "$isec" | grep -E '^[0-9]+$')
+
+    if [ -z "$isec" ]; then
+        # do nothing if unconfigured or non numeric value
+        echo_debug "pm" "set_laptopmode($1).not_configured"
+        return 0
+    fi
+
+    write_sysf "$isec" /proc/sys/vm/laptop_mode
+    echo_debug "pm" "set_laptopmode($1): $isec; rc=$?"
+
+    return 0
+}
+
+set_dirty_parms () { # set filesystem buffer params
+    # $1: 0=ac mode, 1=battery mode
+    # concept from laptop-mode-tools
+
+    local age cage df ec
+
+    check_sysfs "set_dirty_parms" "/proc/sys/vm"
+
+    if [ "$1" = "1" ]; then
+        age=${MAX_LOST_WORK_SECS_ON_BAT:-${DEFAULT_MAX_LOST_WORK_SECS}}
+    else
+        age=${MAX_LOST_WORK_SECS_ON_AC:-${DEFAULT_MAX_LOST_WORK_SECS}}
+    fi
+
+    # calc age in centisecs, non numeric values result in "0"
+    cage=$((age * 100))
+
+    if [ "$cage" = "0" ]; then
+        # do nothing if unconfigured or invalid age
+        echo_debug "pm" "set_dirty_parms($1).not_configured"
+        return 0
+    fi
+
+    ec=0
+    for df in /proc/sys/vm/dirty_writeback_centisecs \
+             /proc/sys/vm/dirty_expire_centisecs \
+             /proc/sys/fs/xfs/age_buffer_centisecs \
+             /proc/sys/fs/xfs/xfssyncd_centisecs; do
+        if [ -f "$df" ] && ! write_sysf "$cage" $df; then
+            echo_debug "pm" "set_dirty_parms($1).write_error: $df $cage; rc=$?"
+            ec=$((ec+1))
+        fi
+    done
+    # shellcheck disable=SC2043
+    for df in /proc/sys/fs/xfs/xfsbufd_centisecs; do
+        if [ -f "$df" ] && ! write_sysf "3000" $df; then
+            echo_debug "pm" "set_dirty_parms($1).write_error: $df 3000; rc=$?"
+            ec=$((ec+1))
+        fi
+    done
+    echo_debug "pm" "set_dirty_parms($1): $cage; ec=$ec"
+
+    return 0
+}
diff -Nru tlp-1.1/func.d/20-tlp-func-usb tlp-1.2.1/func.d/20-tlp-func-usb
--- tlp-1.1/func.d/20-tlp-func-usb	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/20-tlp-func-usb	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,277 @@
+#!/bin/sh
+# tlp-func-usb - USB Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086,SC2155
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly USBD=/sys/bus/usb/devices
+readonly USB_TIMEOUT=2
+readonly USB_TIMEOUT_MS=2000
+readonly USB_WWAN_VENDORS="0bdb 05c6 1199 2cb7"
+readonly USB_DONE=usb_done
+
+readonly DEFAULT_USB_AUTOSUSPEND=0
+readonly DEFAULT_USB_BLACKLIST_BTUSB=0
+readonly DEFAULT_USB_BLACKLIST_PHONE=0
+readonly DEFAULT_USB_BLACKLIST_PRINTER=1
+readonly DEFAULT_USB_BLACKLIST_WWAN=0
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- USB Autosuspend
+
+usb_suspend_device () { # enable/disable usb autosuspend for a single device
+    # except input and blacklisted
+    # $1: device syspath, $2: batch/udev, $3: auto=enable/on=disable
+    local usbdev=$1
+
+    if [ -f $usbdev/power/autosuspend_delay_ms ]; then
+        # device is autosuspendable
+        local vendor="$(read_sysf $usbdev/idVendor)"
+        local usbid="$vendor:$(read_sysf $usbdev/idProduct)"
+        local busdev="Bus $(read_sysf $usbdev/busnum) Dev $(read_sysf $usbdev/devnum)"
+        local dclass="$(read_sysf $usbdev/bDeviceClass)"
+        local control="${3:-auto}"
+        local caller="$2"
+        local exc=""
+        local chg=0 rc1=0 rc2=0
+        local drvlist=""
+
+        # trace only: get drivers for all subdevices
+        if [ "$X_USB_DRIVER_TRACE" = "1" ]; then
+            local dl
+            drvlist=$(for dl in $usbdev/*:*/driver; do readlink "$dl" | \
+                sed -r 's/.+\///'; done | sort -u | tr '\n' ' ')
+            drvlist="(${drvlist% })"
+        fi
+
+        if wordinlist "$usbid" "$USB_WHITELIST"; then
+            # device is in whitelist -- whitelist always wins
+            control="auto"
+            exc="_dev_white"
+        elif wordinlist "$usbid" "$USB_BLACKLIST"; then
+            # device is in blacklist
+            control="on"
+            exc="_dev_black"
+        else
+            local subdev
+
+            # udev: wait for subdevices to populate
+            [ "$caller" = "udev" ] && sleep 0.5
+
+            # check for hid subdevices
+            for subdev in $usbdev/*:*; do
+                if [ "$(read_sysf $subdev/bInterfaceClass)" = "03" ]; then
+                    control="on"
+                    exc="_hid_black"
+                    break
+                fi
+            done
+
+            if [ -z "$exc" ]; then
+                # check for bluetooth devices
+                : ${USB_BLACKLIST_BTUSB:=${DEFAULT_USB_BLACKLIST_BTUSB}}
+
+                if [ "$USB_BLACKLIST_BTUSB" = "1" ] \
+                    && [ "$dclass" = "e0" ] \
+                    && [ "$(read_sysf $usbdev/bDeviceSubClass)" = "01" ] \
+                    && [ "$(read_sysf $usbdev/bDeviceProtocol)" = "01" ]; then
+                    control="on"
+                    exc="_btusb_black"
+                fi
+            fi # bluetooth
+
+            if [ -z "$exc" ]; then
+                # check for scanners:
+                # libsane_matched envvar is set by libsane's udev rules
+                # shellcheck disable=SC2154
+                if [ "$libsane_matched" = "yes" ] || [ "$2" = "batch" ] \
+                    && $UDEVADM info -q property $usbdev 2>/dev/null | grep -q 'libsane_matched=yes'; then
+                    # do not touch this device
+                    control="black"
+                    exc="_libsane"
+                fi
+            fi
+
+            if [ -z "$exc" ]; then
+                # check for phone devices
+                : ${USB_BLACKLIST_PHONE:=${DEFAULT_USB_BLACKLIST_PHONE}}
+
+                if [ "$USB_BLACKLIST_PHONE" = "1" ]; then
+                    if [ "$vendor" = "0fca" ]; then
+                        # RIM
+                        if [ "$dclass" = "ef" ]; then
+                            # RIM / BlackBerry
+                            control="on"
+                            exc="_phone_black"
+                        elif [ "$dclass" = "00" ]; then
+                           for subdev in $usbdev/*:*; do
+                                if [ -d $subdev ]; then
+                                    if [ "$(read_sysf $subdev/interface)" = "BlackBerry" ]; then
+                                        # Blackberry
+                                        control="on"
+                                        exc="_phone_black"
+                                        break
+                                    fi
+                                fi
+                            done
+                        fi
+
+                    elif [ "$vendor" = "045e" ] && [ "$dclass" = "ef" ]; then
+                        # Windows Phone
+                        control="on"
+                        exc="_phone_black"
+
+                    elif [ "$vendor" = "05ac" ] && [ "$(read_sysf $usbdev/product)" = "iPhone" ]; then
+                        # iPhone
+                        control="on"
+                        exc="_phone_black"
+
+                    elif [ "$dclass" = "00" ]; then
+                        # class defined at interface level, iterate subdevices
+                        for subdev in $usbdev/*:*; do
+                            if [ -d $subdev ]; then
+                                if [ "$(read_sysf $subdev/interface)" = "MTP" ]; then
+                                    # MTP: mostly Android
+                                    control="on"
+                                    exc="_phone_black"
+                                    break
+                                elif [ "$(read_sysf $subdev/bInterfaceClass)" = "ff" ] \
+                                    && [ "$(read_sysf $subdev/bInterfaceSubClass)" = "42" ] \
+                                    && [ "$(read_sysf $subdev/bInterfaceProtocol)" = "01" ]; then
+                                    # ADB: Android
+                                    control="on"
+                                    exc="_phone_black"
+                                    break
+                                elif [ "$(read_sysf $subdev/bInterfaceClass)" = "06" ] \
+                                    && [ "$(read_sysf $subdev/bInterfaceSubClass)" = "01" ] \
+                                    && [ "$(read_sysf $subdev/bInterfaceProtocol)" = "01" ]; then
+                                    # PTP: iPhone, Lumia et al.
+                                    # caveat: may also be a camera
+                                    control="on"
+                                    exc="_phone_black"
+                                    break
+                                fi
+                            fi
+                        done
+
+                    fi # dclass 00
+                fi # blacklist phone
+            fi # phone
+
+            if [ -z "$exc" ]; then
+                # check for printers
+                : ${USB_BLACKLIST_PRINTER:=${DEFAULT_USB_BLACKLIST_PRINTER}}
+
+                if [ "$USB_BLACKLIST_PRINTER" = "1" ]; then
+                    if [ "$dclass" = "00" ]; then
+                        # check for printer subdevices
+                        for subdev in $usbdev/*:*; do
+                            if [ "$(read_sysf $subdev/bInterfaceClass)" = "07" ]; then
+                                control="on"
+                                exc="_printer_black"
+                                break
+                            fi
+                        done
+                    fi
+                fi
+            fi # printer
+
+            if [ -z "$exc" ]; then
+                # check for wwan devices
+                : ${USB_BLACKLIST_WWAN:=${DEFAULT_USB_BLACKLIST_WWAN}}
+
+                if [ "$USB_BLACKLIST_WWAN" = "1" ]; then
+                    if [ "$dclass" != "00" ]; then
+                        # check for cdc subdevices
+                        for subdev in $usbdev/*:*; do
+                            if [ "$(read_sysf $subdev/bInterfaceClass)" = "0a" ]; then
+                                control="on"
+                                exc="_wwan_black"
+                                break
+                            fi
+                        done
+                    fi
+
+                    if [ -z "$exc" ]; then
+                        # check for vendors
+                        if wordinlist "$vendor" "$USB_WWAN_VENDORS"; then
+                            control="on"
+                            exc="_wwan_black"
+                        fi
+                    fi
+                fi # blacklist wwan
+            fi # wwan
+        fi # !device blacklist
+
+        if [ "$(read_sysf $usbdev/power/control)" != "$control" ]; then
+            # set control, write actual changes only
+            case $control in
+                auto|on)
+                    write_sysf "$control" $usbdev/power/control; rc1=$?
+                    chg=1
+                    ;;
+
+                black) # do not touch blacklisted device
+                    ;;
+            esac
+        fi
+
+        if [ "$X_TLP_USB_SET_AUTOSUSPEND_DELAY" = "1" ]; then
+            # set autosuspend delay
+            write_sysf $USB_TIMEOUT_MS $usbdev/power/autosuspend_delay_ms; rc2=$?
+            echo_debug "usb" "usb_suspend_device.$caller.$control$exc: $busdev ID $usbid $usbdev [$drvlist]; control: rc=$rc1; delay: rc=$rc2"
+        elif [ $chg -eq 1 ]; then
+            # default: change control but not autosuspend_delay, i.e. keep kernel default setting
+            echo_debug "usb" "usb_suspend_device.$caller.$control$exc: $busdev ID $usbid $usbdev [$drvlist]; control: rc=$rc1"
+        else
+            # we didn't change anything actually
+            echo_debug "usb" "usb_suspend_device.$caller.$control$exc.no_change: $busdev ID $usbid $usbdev [$drvlist]"
+        fi
+
+    fi # autosuspendable
+
+    return 0
+}
+
+set_usb_suspend () { # enable/disable usb autosuspend for all devices
+    # $1: 0=silent/1=report result; $2: auto=enable/on=disable
+
+    local usbdev
+
+    check_sysfs "set_usb_suspend" "$USBD"
+
+    : ${USB_AUTOSUSPEND:=${DEFAULT_USB_AUTOSUSPEND}}
+
+    if [ "$USB_AUTOSUSPEND" = "1" ]; then
+        # autosuspend is configured --> iterate devices
+        for usbdev in $USBD/*; do
+            case "$usbdev" in
+                *:*) ;; # colon in device name --> do nothing
+
+                *) usb_suspend_device "$usbdev" "batch" $2 ;;
+            esac
+        done
+
+        [ "$1" = "1" ] && echo "USB autosuspend settings applied."
+        echo_debug "usb" "set_usb_suspend.done"
+    else
+        [ "$1" = "1" ] && echo "Error: USB autosuspend is disabled. Set USB_AUTOSUSPEND=1 in $CONFFILE." 1>&2
+        echo_debug "usb" "set_usb_suspend.not_configured"
+
+    fi
+
+    # set "startup completion" flag for tlp-usb-udev
+    set_run_flag $USB_DONE
+
+    return 0
+}
+
diff -Nru tlp-1.1/func.d/25-tlp-func-rf tlp-1.2.1/func.d/25-tlp-func-rf
--- tlp-1.1/func.d/25-tlp-func-rf	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/25-tlp-func-rf	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,220 @@
+#!/bin/sh
+# tlp-func-rf - Radio Device Checks and PM Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly IWC=iwconfig
+readonly IW=iw
+
+readonly BLUETOOTHD=/sys/class/bluetooth
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Wifi Device Checks
+
+get_wifi_ifaces () { # get all wifi devices -- retval: $_wifaces
+    local wi wiu
+    _wifaces=""
+
+    for wiu in $NETD/*/uevent; do
+        if grep -q -s 'DEVTYPE=wlan' $wiu ; then
+            wi=${wiu%/uevent}; wi=${wi##*/}
+            _wifaces="$_wifaces $wi"
+        fi
+    done
+
+    _wifaces="${_wifaces# }"
+    return 0
+}
+
+get_wifi_driver () { # get driver associated with interface
+                     # $1: iface; retval: $_wifidrv
+    local drvl
+
+    _wifidrv=""
+    if [ -d $NETD/$1 ]; then
+        drvl=$(readlink $NETD/$1/device/driver)
+        # shellcheck disable=SC2034
+        [ -n "$drvl" ] && _wifidrv=${drvl##*/}
+    fi
+
+    return 0
+}
+
+wireless_in_use () { # check if wifi or wwan device is in use -- $1: iface
+    if [ -f $NETD/$1/carrier ]; then
+        if [ "$(read_sysf $NETD/$1/carrier)" = "1" ]; then
+            return 0
+        fi
+    fi
+    return 1
+}
+
+any_wifi_in_use () { # check if any wifi device is in use
+    local iface
+
+    get_wifi_ifaces
+    for iface in $_wifaces; do
+        wireless_in_use $iface && return 0
+    done
+
+    return 1
+}
+
+# --- Wifi Power Management
+
+set_wifi_power_mode () { # set wifi power save mode -- $1: 0=ac mode, 1=battery mode
+    local pwr iface
+    local rc=0
+    local cmdx=0
+
+    if [ "$1" = "1" ]; then
+        pwr=${WIFI_PWR_ON_BAT:-}
+    else
+        pwr=${WIFI_PWR_ON_AC:-}
+    fi
+
+    # check values, translate obsolete syntax
+    case "$pwr" in
+        off|on)      ;;
+        0|1|N)       pwr="off" ;;
+        2|3|4|5|6|Y) pwr="on"  ;;
+        *)           pwr=""    ;; # invalid input --> unconfigured
+    esac
+
+    if [ -z "$pwr" ]; then
+        # do nothing if unconfigured
+        echo_debug "pm" "set_wifi_power_mode($1).not_configured"
+        return 0
+    fi
+
+    get_wifi_ifaces
+    if [ -z "$_wifaces" ]; then
+        echo_debug "pm" "set_wifi_power_mode($1).no_ifaces"
+        return 0
+    fi
+
+    for iface in $_wifaces; do
+        if [ -n "$iface" ]; then
+            if  [ "$X_DONT_USE_IW" != "1" ] && cmd_exists $IW; then
+                # try with iw first
+                $IW dev $iface set power_save $pwr > /dev/null 2>&1
+                rc=$?
+                echo_debug "pm" "set_wifi_power_mode($1, $iface).iw: $pwr; rc=$rc"
+                cmdx=1 # set flag: iw found and called
+            fi
+            if cmd_exists $IWC; then
+                if [ $rc -ne 0 ] || [ $cmdx -eq 0 ]; then
+                    # iw did not succeed or iw not installed -> try with iwconfig
+                    $IWC $iface power $pwr > /dev/null 2>&1
+                    rc=$?
+                    echo_debug "pm" "set_wifi_power_mode($1, $iface).iwconfig: $pwr; rc=$rc"
+                    cmdx=1 # set flag: iwconfig found and called
+                fi
+            fi
+            if [ $cmdx -eq 0 ]; then
+                # flag not set: neither iw nor iwconfig installed --> no way
+                echo_debug "pm" "set_wifi_power_mode($1, $iface).no_tool"
+            fi
+        fi
+    done
+
+    return 0
+}
+
+# --- WWAN Device Checks
+
+get_wwan_ifaces () { # get all wwan devices -- retval: $_wanifaces
+    local wi wiu
+    _wanifaces=""
+
+    for wiu in $NETD/*/uevent; do
+        if grep -q -s 'DEVTYPE=wwan' $wiu ; then
+            wi=${wiu%/uevent}; wi=${wi##*/}
+            _wanifaces="$_wanifaces $wi"
+        fi
+    done
+
+    _wanifaces="${_wanifaces# }"
+    return 0
+}
+
+any_wwan_in_use () { # check if any wwan device is in use
+    local iface
+
+    get_wwan_ifaces
+    for iface in $_wanifaces; do
+        wireless_in_use $iface && return 0
+    done
+
+    return 1
+}
+
+get_wwan_driver () { # get driver associated with interface
+                     # $1: iface; retval: $_wwandrv
+    local drvl
+
+    _wwandrv=""
+    if [ -d $NETD/$1 ]; then
+        drvl=$(readlink $NETD/$1/device/driver)
+        # shellcheck disable=SC2034
+        [ -n "$drvl" ] && _wwandrv=${drvl##*/}
+    fi
+
+    return 0
+}
+
+# --- Bluetooth Device Checks
+
+get_bluetooth_ifaces () { # get all bluetooth interfaces -- retval: $_bifaces
+    # enumerate symlinks only
+    _bifaces="$(for i in $BLUETOOTHD/*; do [ -h $i ] && echo ${i##/*/}; done | grep -v ':')"
+    return 0
+}
+
+get_bluetooth_driver () { # get driver associated with interface -- $1: iface; retval: $_btdrv
+    local drvl
+
+    # shellcheck disable=SC2034
+    _btdrv=""
+    if [ -d $BLUETOOTHD/$1 ]; then
+        drvl=$(readlink $BLUETOOTHD/$1/device/driver)
+        # shellcheck disable=SC2034
+        [ -n "$drvl" ] && _btdrv=${drvl##*/}
+    fi
+
+    return 0
+}
+
+bluetooth_in_use () { # check if bluetooth interface is in use -- $1: iface
+    local uev
+
+    # when devices are connected to an interface its sysdir is populated with
+    # subdevices like <iface>:<number> where the uevent file contains a line
+    # "DEVTYPE=link"
+    for uev in $BLUETOOTHD/$1/$1:*/uevent; do
+        grep -q -s 'DEVTYPE=link' $uev && return 0
+    done
+
+    return 1
+}
+
+any_bluetooth_in_use () { # check if any bluetooth interface is in use
+    local i
+
+    get_bluetooth_ifaces
+    for i in $_bifaces; do
+        bluetooth_in_use "$i" && return 0
+    done
+
+    return 1
+}
diff -Nru tlp-1.1/func.d/30-tlp-func-rf-sw tlp-1.2.1/func.d/30-tlp-func-rf-sw
--- tlp-1.1/func.d/30-tlp-func-rf-sw	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/30-tlp-func-rf-sw	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,497 @@
+#!/bin/sh
+# tlp-func-rf-sw - Radio Switch Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base, tlp-func-rf
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly NMCLI=nmcli
+readonly RFKILL="rfkill"
+readonly RFKD="/dev/rfkill"
+
+readonly ALLDEV="bluetooth wifi wwan"
+
+readonly RDW_NM_LOCK="rdw_nm"
+readonly RDW_DOCK_LOCK="rdw_dock"
+readonly RDW_LOCKTIME=2
+readonly RDW_KILL="rdw_kill"
+
+readonly DEFAULT_RESTORE_DEVICE_STATE_ON_STARTUP=0
+
+# ----------------------------------------------------------------------------
+# Functions
+
+get_devc () { # get control device for radio type
+              # $1: rftype bluetooth/wifi/wwan
+              # retval $_devc: sysdev, $_devs: device state
+              #     $_rfkdev: 1/0=is/is not an rfkill device,
+              #     $_devon, $_devoff: value to write directly to the sysdev
+              #     to achieve the desired switch state
+
+    local i
+
+    check_sysfs "get_devc" "/sys/class/rfkill"
+
+    # preset retvals
+    _devc=""
+    _devs=254
+    _rfkdev="1"
+    _devon="1"
+    _devoff="0"
+
+    case $1 in
+        wwan|bluetooth)
+            for i in /sys/class/rfkill/rfkill* ; do
+                if [ "$(read_sysf $i/type)" = "$1" ]; then
+                    _devc="$i/state"
+                    echo_debug "rf" "get_devc($1) = $_devc"
+                    return 0
+                fi
+            done
+            ;;
+
+        wifi)
+            for i in /sys/bus/pci/drivers/ipw2?00/*/rf_kill; do
+                if [ -f "$i" ]; then
+                    _devc="$i"
+                    _rfkdev="0"
+                    _devon="0"
+                    _devoff="1"
+                    echo_debug "rf" "get_devc($1) = $_devc"
+                    return 0
+                fi
+            done
+
+            for i in /sys/class/rfkill/rfkill* ; do
+                if [ "$(read_sysf $i/type)" = "wlan" ]; then
+                    _devc="$i/state"
+                    echo_debug "rf" "get_devc($1) = $_devc"
+                    return 0
+                fi
+            done
+            ;;
+
+        *)
+            echo "Error: unknown device type \"$1\"" 1>&2
+            echo_debug "rf" "get_devc($1).unknown_type"
+            return 0
+            ;;
+    esac
+
+    echo_debug "rf" "get_devc($1).not_present"
+
+    return 0
+}
+
+get_devs () { # get radio device state -- $1: rftype; retval $_devs: 0=off/1=on
+    if [ -n "$_devc" ]; then
+        _devs="$(read_sysf $_devc)"
+        case "$_devs" in
+            0|1) # invert state when not a rfkill device
+                [ "$_rfkdev" = "0" ] && _devs=$((_devs ^ _devoff))
+                ;;
+            2) ;; # hard blocked device
+            *) _devs=3 # invalid state
+        esac
+    fi
+
+    echo_debug "rf" "get_devs($1) = $_devs"
+
+    return 0
+}
+
+err_no_root_priv () { # check root privilege
+    echo "Error: missing root privilege." 1>&2
+    echo_debug "rf" "$1.missing_root_privilege"
+
+    return 0
+}
+
+test_rfkill_perms () { # test if either root priv or rfkill device writable
+    test_root || [ -w $RFKD ]
+}
+
+check_nm () { # test if NetworkManager is running and nmcli is installed
+
+    cmd_exists $NMCLI
+}
+
+invoke_nmcli () { # call nmcli with radio option according to the program version
+                  # $1: rftype, $2: on/off, $3: caller; rc: last nmcli rc
+    local rc
+
+    check_nm || return 0 # return if NetworkManager not running
+
+    $NMCLI radio $1 $2 > /dev/null 2>&1; rc=$?
+    echo_debug "rf" "invoke_nmcli($1, $2).radio: rc=$rc"
+
+    return $rc
+}
+
+device_state () { # get radio type state -- $1: rftype; retval $_devc, $_devs: 0=off/1=on
+    echo_debug "rf" "device_state($1)"
+
+    get_devc $1
+    get_devs $1
+}
+
+device_switch () { # switch radio type state
+                   # $1: rftype, $2: 1/on/0/off/toggle
+                   # $3: lock id, $4: lock duration
+                   # rc: 0=switched/1=invalid device or operation/
+                   #     2=hard blocked/3=invalid state/4=no change
+                   # retval $_devc, $_devs: 0=off/1=on
+
+    local curst newst devn
+
+    echo_debug "rf" "device_switch($1, $2, $3, $4)"
+
+    get_devc $1
+
+    # quit if no device
+    if [ -z "$_devc" ]; then
+        echo_debug "rf" "device_switch($1, $2).no_device: rc=1"
+        return 1
+    fi
+
+    # quit if invalid operation
+    if ! wordinlist $2 "on 1 off 0 toggle"; then
+        echo_debug "rf" "device_switch($1, $2).invalid_op: rc=1"
+        return 1
+    fi
+
+    # get current device state
+    get_devs $1
+    curst=$_devs
+
+    # quit if device state is hard blocked or invalid
+    if [ $_devs -ge 2 ]; then
+        case $_devs in
+            2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;;
+            *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;;
+        esac
+        return $_devs
+    fi
+
+    # determine desired device state
+    case $2 in
+        1|on)     newst=1 ;;
+        0|off)    newst=0 ;;
+        toggle) newst=$((curst ^ 1)) ;;
+    esac
+
+    # wifi, wwan: before rfkill (only if X_configured) -- notify NM of desired state
+    if [ "$X_USE_NMCLI" = "1" ] && [ "$1" != "bluetooth" ]; then
+        case $newst in
+            1) invoke_nmcli $1 on  ;;
+            0) invoke_nmcli $1 off ;;
+        esac
+        # record device state after nmcli
+        get_devs $1
+        # update current state
+        curst=$_devs
+
+        # quit if device state is hard blocked or invalid
+        if [ $_devs -ge 2 ]; then
+            case $_devs in
+                2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;;
+                *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;;
+            esac
+            return $_devs
+        fi
+    fi
+
+    # compare current and desired device state
+    if [ "$curst" = "$newst" ]; then
+        # desired matches current state --> do nothing
+        echo_debug "rf" "device_switch($1, $2).desired_state"
+
+    else
+        # desired does not match current state --> do switch
+
+        # set timed lock if required
+        [ -n "$3" ] && [ -n "$4" ] && [ "$1" != "bluetooth" ] && \
+            set_timed_lock $3 $4
+
+        # determine value for direct write
+        case $newst in
+            1) devn=$_devon  ;;
+            0) devn=$_devoff ;;
+        esac
+
+        # switch device state when either rfkill isn't disabled
+        # or it's a bluetooth device
+        if [ "$X_USE_RFKILL" != "0" ] || [ "$1" != "bluetooth" ]; then
+            if [ "$_rfkdev" = "1" ] && cmd_exists $RFKILL ; then
+                if test_rfkill_perms ; then
+                    # use rfkill
+                    echo_debug "rf" "device_switch($1, $2).rfkill"
+                    case $newst in
+                        1) $RFKILL unblock $1 > /dev/null 2>&1 ;;
+                        0) $RFKILL block $1   > /dev/null 2>&1 ;;
+                        *) ;;
+                    esac
+                    # record device state after rfkill
+                    get_devs $1
+                else
+                    # missing permission to rfkill
+                    err_no_root_priv "device_switch($1, $2).rfkill"
+                fi
+            else
+                # use direct write
+                if test_root ; then
+                    write_sysf "$devn" $_devc
+                    echo_debug "rf" "device_switch($1, $2).devc: rc=$?"
+                    # record device state after direct write
+                    get_devs $1
+                else
+                    err_no_root_priv "device_switch($1, $2).devc"
+                fi
+            fi
+        fi # rfkill not disabled or bluetooth
+    fi # states did not match
+
+    # wifi, wwan: after rkfill (default) -- notify NM of desired state
+    if [ "$X_USE_NMCLI" != "0" ] && [ "$1" != "bluetooth" ]; then
+        case $newst in
+            1) invoke_nmcli $1 on  ;;
+            0) invoke_nmcli $1 off ;;
+        esac
+        # record final device state
+        get_devs $1
+    fi
+
+    # quit if device state is hard blocked or invalid
+    if [ $_devs -ge 2 ]; then
+        case $_devs in
+            2) echo_debug "rf" "device_switch($1, $2).hard_blocked: rc=$_devs" ;;
+            *) echo_debug "rf" "device_switch($1, $2).invalid_state: rc=$_devs" ;;
+        esac
+        return $_devs
+    fi
+
+    # compare old and new device state
+    if [ "$curst" = "$_devs" ]; then
+        # state did not change
+        echo_debug "rf" "device_switch($1, $2).no_change: rc=4"
+        return 4
+    else
+        echo_debug "rf" "device_switch($1, $2).ok: rc=0"
+        return 0
+    fi
+}
+
+echo_device_state () { # print radio type state -- $1: rftype, $2: state
+    case $1 in
+        bluetooth)
+            devstr="bluetooth"
+            ;;
+
+        wifi)
+            devstr="wifi     "
+            ;;
+
+        wwan)
+            devstr="wwan     "
+            ;;
+
+        *)
+            devstr=$1
+            ;;
+    esac
+
+    case $2 in
+        0)
+            echo "$devstr = off (software)"
+            ;;
+
+        1)
+            echo "$devstr = on"
+            ;;
+
+        2)
+            echo "$devstr = off (hardware)"
+            ;;
+
+        254)
+            echo "$devstr = none (no device)"
+            ;;
+
+        *)
+            echo "$devstr = invalid state"
+    esac
+
+    return 0
+}
+
+# shellcheck disable=SC2120
+save_device_states () { # save radio states -- $1: list of rftypes
+    local dev
+    local devlist="${1:-$ALLDEV}" # when arg empty -> use all
+
+    echo_debug "rf" "save_device_states($devlist): $RFSTATEFILE"
+
+    # create empty state file
+    create_rundir
+    { : > $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: $RFSTATEFILE"
+
+    if [ -f $RFSTATEFILE ]; then
+        # read state file
+        # shellcheck disable=SC2162
+        while read sline; do
+            set -- $sline # read dev, state into $1, $2
+            device_switch $1 $2
+        done < $RFSTATEFILE
+
+        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:-${DEFAULT_RESTORE_DEVICE_STATE_ON_STARTUP}}
+    else
+        restore=0
+    fi
+
+    if [ "$restore" = "1" ]; then
+        # "save/restore" mode
+        echo_debug "rf" "set_radio_device_states($1).restore"
+        case $1 in
+            start)
+                if restore_device_states; then
+                    echo "Radio device states restored."
+                else
+                    echo "No saved radio device states found."
+                fi
+                ;;
+
+            stop)
+                # shellcheck disable=SC2119
+                save_device_states
+                echo "Radio device states saved."
+                ;;
+        esac
+    else
+        # "disable/enable on startup/shutdown or bat/ac" or "radiosw" mode
+        case $1 in
+            start) # system startup
+                devs2disable="$DEVICES_TO_DISABLE_ON_STARTUP"
+                devs2enable="$DEVICES_TO_ENABLE_ON_STARTUP"
+                ;;
+
+            stop) # system shutdown
+                devs2disable="$DEVICES_TO_DISABLE_ON_SHUTDOWN"
+                devs2enable="$DEVICES_TO_ENABLE_ON_SHUTDOWN"
+
+                if [ "$X_WIFI_ON_SHUTDOWN" != "0" ]; then
+                    # NM workaround: if
+                    # 1. disable wifi is configured somehow, and
+                    # 2. wifi is not explicitly configured for shutdown
+                    # then re-enable wifi on shutdown to prepare for startup
+                    if wordinlist "wifi" "$DEVICES_TO_DISABLE_ON_BAT
+                                          $DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE
+                                          $DEVICES_TO_DISABLE_ON_LAN_CONNECT
+                                          $DEVICES_TO_DISABLE_ON_WIFI_CONNECT
+                                          $DEVICES_TO_DISABLE_ON_WWAN_CONNECT" && \
+                       ! wordinlist "wifi" "$devs2disable $devs2enable"; then
+                        devs2enable="wifi $devs2enable"
+                    fi
+                fi
+                ;;
+
+            1) # battery power --> build disable list
+                quiet=1 # do not display progress
+                devs2enable=""
+                devs2disable="${DEVICES_TO_DISABLE_ON_BAT:-}"
+
+                # check configured list for connected devices
+                for dev in ${DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE:-}; do
+                     # if device is not connected and not in list yet --> add to disable list
+                    { case $dev in
+                        bluetooth) any_bluetooth_in_use ;;
+                        wifi) any_wifi_in_use ;;
+                        wwan) any_wwan_in_use ;;
+                    esac } || wordinlist $dev "$devs2disable" || devs2disable="$dev $devs2disable"
+                done
+                devs2disable="${devs2disable# }"
+                ;;
+
+            0) # AC power --> build enable list
+                quiet=1 # do not display progress
+                devs2enable="${DEVICES_TO_ENABLE_ON_AC:-}"
+                devs2disable=""
+                ;;
+
+            radiosw)
+                devs2disable=""
+                devs2enable="$DEVICES_TO_ENABLE_ON_RADIOSW"
+                ;;
+        esac
+
+        echo_debug "rf" "set_radio_device_states($1): enable=$devs2enable disable=$devs2disable"
+
+        # Disable configured radios
+        if [ -n "$devs2disable" ]; then
+            [ "$quiet" = "1" ] || echo -n "Disabling radios:"
+            for dev in $devs2disable; do
+                [ "$quiet" = "1" ] || 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
+}
diff -Nru tlp-1.1/func.d/35-tlp-func-batt tlp-1.2.1/func.d/35-tlp-func-batt
--- tlp-1.1/func.d/35-tlp-func-batt	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/35-tlp-func-batt	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,957 @@
+#!/bin/sh
+# tlp-func-batt - [ThinkPad] Battery Feature Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly MODINFO=modinfo
+
+readonly TPACPIDIR=/sys/devices/platform/thinkpad_acpi
+readonly SMAPIBATDIR=/sys/devices/platform/smapi
+readonly ACPIBATDIR=/sys/class/power_supply
+
+readonly DEFAULT_NATACPI_ENABLE=1
+readonly DEFAULT_TPACPI_ENABLE=1
+readonly DEFAULT_TPSMAPI_ENABLE=1
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Battery Feature Support
+
+check_battery_features () { # determine which battery feature APIs/tools are
+    # supported by hardware and running kernel.
+    #
+    # 1. check for native kernel acpi (Linux 4.17 or higher required)
+    #    --> retval $_natacpi:
+    #       0=thresholds and discharge/
+    #       1=thresholds only/
+    #       32=disabled/
+    #       128=no kernel support/
+    #       253=laptop not supported
+    #
+    # 2. check for acpi-call external kernel module and test with integrated
+    #    tpacpi-bat [ThinkPads only]
+    #    --> retval $_tpacpi:
+    #       0=supported/
+    #       32=disabled/
+    #       64=acpi_call module not loaded/
+    #       127=tpacpi-bat not installed/
+    #       128=acpi_call module not installed/
+    #       253=laptop not supported/
+    #       254=ThinkPad not supported/
+    #       255=superseded by natacpi
+    #
+    # 3. check for tp-smapi external kernel module [ThinkPads only]
+    #    --> retval $_tpsmapi:
+    #       0=supported/
+    #       1=readonly/
+    #       32=disabled/
+    #       64=tp_smapi module not loaded/
+    #       128=tp_smapi module not installed/
+    #       253=laptop not supported/
+    #       254=ThinkPad not supported/
+    #
+    # 4. determine best method for
+    #    reading battery data                   --> retval $_bm_read,
+    #    reading/writing charging thresholds    --> retval $_bm_thresh,
+    #    reading/writing force discharge        --> retval $_bm_dischg:
+    #       none/natacpi/tpacpi/tpsmapi
+    #
+    # prerequisite: check_thinkpad()
+    # replaces: check_tpsmapi, check_tpacpi
+
+    # preset: natacpi takes it all
+    _natacpi=128
+    _tpacpi=255
+    _tpsmapi=255
+    _bm_read="natacpi"
+    _bm_thresh="none"
+    _bm_dischg="none"
+
+    # --- 1. check for native kernel ACPI (Linux 4.17 or higher required)
+    local ps
+    for ps in $ACPIBATDIR/*; do
+        if [ "$(read_sysf $ps/present)" = "1" ]; then
+            # battery detected
+            if [ -f $ps/charge_start_threshold ]; then
+                # kernel with native acpi support detected
+                _natacpi=253
+
+                if readable_sysf $ps/charge_start_threshold; then
+                    # charge_start_threshold exists and is actually readable
+                    if [ "${NATACPI_ENABLE:-${DEFAULT_NATACPI_ENABLE}}" = "1" ]; then
+                        _natacpi=1
+                        _bm_thresh="natacpi"
+                    else
+                        _natacpi=32
+                    fi
+                fi
+                if [ $_natacpi != 32 ] && readable_sysf $ps/force_discharge; then
+                    # force_discharge exists and is actually readable
+                    _natacpi=0
+                    _bm_dischg="natacpi"
+                fi
+            fi
+            break # exit loop on first battery detected
+        fi
+    done
+    echo_debug "bat" "check_battery_features.natacpi: $_natacpi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)"
+
+    # when not a Thinkpad --> we're done here
+    if ! is_thinkpad; then
+        # laptop not supported
+        _tpacpi=253
+        _tpsmapi=253
+        return 0
+    fi
+
+    # --- 2. check for acpi-call external kernel module and test with integrated tpacpi-bat [ThinkPads only]
+    if ! supports_tpacpi; then
+        _tpacpi=254
+    elif [ $_natacpi -eq 0 ]; then
+        # tpacpi-bat superseded by natacpi: _tpacpi=255 from above
+        :
+    elif [ ! -e /proc/acpi/call ]; then
+        # call API not present
+        if $MODINFO acpi_call > /dev/null 2>&1; then
+            # module installed but not loaded
+            _tpacpi=64
+        else
+            # module neither installed nor builtin
+            _tpacpi=128
+        fi
+    else
+        # call API present --> try tpacpi-bat
+        $TPACPIBAT -g FD 1 > /dev/null 2>&1
+        _tpacpi=$?
+
+        if [ $_tpacpi -eq 0 ] && [ "${TPACPI_ENABLE:-${DEFAULT_TPACPI_ENABLE}}" = "0" ]; then
+            # tpacpi disabled by configuration
+            _tpacpi=32
+        fi
+
+        if [ $_tpacpi -eq 0 ]; then
+            # tpacpi available --> fill in methods depending on natacpi results
+            case $_natacpi in
+                1) # discharge needed
+                    _bm_dischg="tpacpi"
+                    ;;
+
+                *) # thresholds and discharge needed
+                    _bm_thresh="tpacpi"
+                    _bm_dischg="tpacpi"
+                    ;;
+            esac
+        fi
+    fi
+    echo_debug "bat" "check_battery_features.tpacpi: $_tpacpi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)"
+
+    # --- 3. check for tp-smapi external kernel module [ThinkPads only]
+    if [ -d $SMAPIBATDIR ]; then
+        # module loaded --> tp-smapi available
+        if [ "${TPSMAPI_ENABLE:-${DEFAULT_TPSMAPI_ENABLE}}" = "0" ]; then
+            # tpsmapi disabled by configuration
+            _tpsmapi=32
+        elif supports_tpsmapi_and_tpacpi; then
+            # readonly
+            _tpsmapi=1
+        else
+            # enabled (default)
+            _tpsmapi=0
+            # fill in missing methods
+            [ "$_bm_thresh" = "none" ] && _bm_thresh="tpsmapi"
+            [ "$_bm_dischg" = "none" ] && _bm_dischg="tpsmapi"
+        fi
+
+        # reading battery data via tpsmapi is preferred over natacpi
+        # because it provides cycle count and more
+        _bm_read="tpsmapi"
+    elif ! supports_tpsmapi_only && ! supports_tpsmapi_and_tpacpi || supports_no_tp_bat_funcs; then
+        # not tp-smapi capable models
+        _tpsmapi=254
+    elif $MODINFO tp_smapi > /dev/null 2>&1; then
+        # module installed but not loaded
+        _tpsmapi=64
+    else
+        # module neither installed nor builtin
+        _tpsmapi=128
+    fi
+    echo_debug "bat" "check_battery_features.tpsmapi: $_tpsmapi (read=$_bm_read; thresh=$_bm_thresh; dischg=$_bm_dischg)"
+
+    return 0
+}
+
+# --- Battery Detection
+
+battery_present () { # check battery presence and return tpacpi-bat index
+    # $1: BAT0/BAT1/DEF
+    # global param: $_bm_read
+    # rc: 0=bat exists/1=bat nonexistent/255=no method available
+    # retval: $_bat_str:   BAT0/BAT1;
+    #         $_bat_idx:   1/2;
+    #         $_bd_read:   directory with battery data sysfiles;
+    #         $_bf_start:  sysfile for start threshold;
+    #         $_bf_stop:   sysfile for stop threshold;
+    #         $_bf_dischg: sysfile for force discharge
+
+    # defaults
+    local rc=255  # no threshold API available
+    _bat_idx=0    # no index
+    _bat_str=""   # no bat
+    _bd_read=""   # no directories
+    _bf_start=""
+    _bf_stop=""
+    _bf_dischg=""
+    local blist bs bsd
+
+    # load modules and check prerequisites
+    check_thinkpad
+    check_battery_features
+
+    # validate param
+    case $1 in
+        BAT0|BAT1) blist="$1" ;;
+        DEF)       blist="BAT0 BAT1" ;;
+        *)         return 1 ;;
+    esac
+
+    case $_bm_read in
+        natacpi) # note: includes tpacpi
+            rc=1
+            for bs in $blist; do
+                bsd="$ACPIBATDIR/$bs"
+
+                # check acpi name space
+                if [ "$(read_sysf $bsd/present)" = "1" ] \
+                   && [ "$(read_sysf $bsd/type)" = "Battery" ]; then
+                    rc=0 # battery detected
+                    # determine tpacpi-bat index
+                    case $bs in
+                        BAT0)
+                            _bat_str="$bs"
+                            _bd_read="$bsd"
+                            _bat_idx=1 # BAT0 is always assumed main battery
+                            ;;
+
+                        BAT1)
+                            _bat_str="$bs"
+                            _bd_read="$bsd"
+                            if [ -d $ACPIBATDIR/BAT0 ]; then
+                                _bat_idx=2 # BAT0 exists --> BAT1 is aux
+                            else
+                                _bat_idx=1 # BAT0 nonexistent --> BAT1 is main
+                            fi
+                            ;;
+                    esac
+                    break # exit loop on first battery detected
+                fi
+            done
+            ;; # natacpi
+
+        tpsmapi)
+            rc=1
+            for bs in $blist; do
+                bsd="$SMAPIBATDIR/$bs"
+
+                # check tp-smapi name space
+                if [ "$(read_sysf $bsd/installed)" = "1" ]; then
+                    rc=0 # battery detected
+                    case $bs in
+                        BAT0) _bat_str="$bs"; _bd_read="$bsd" ; _bat_idx=1 ;;
+                        BAT1) _bat_str="$bs"; _bd_read="$bsd" ; _bat_idx=2 ;;
+                    esac
+                    break # exit loop on first battery detected
+                fi
+            done
+            ;; # tpsmapi
+    esac
+
+    if [ $_bat_idx -ne 0 ]; then
+        case $_bm_thresh in
+            natacpi)
+                _bf_start="$ACPIBATDIR/$_bat_str/charge_start_threshold"
+                _bf_stop="$ACPIBATDIR/$_bat_str/charge_stop_threshold"
+                ;;
+
+            tpsmapi)
+                _bf_start="$SMAPIBATDIR/$_bat_str/start_charge_thresh"
+                _bf_stop="$SMAPIBATDIR/$_bat_str/stop_charge_thresh"
+                ;;
+        esac
+        case $_bm_dischg in
+            natacpi) _bf_dischg="$ACPIBATDIR/$_bat_str/force_discharge" ;;
+            tpsmapi) _bf_dischg="$SMAPIBATDIR/$_bat_str/force_discharge" ;;
+        esac
+    fi
+
+    case $rc in
+        0)   echo_debug "bat" "battery_present($1): bm_read=$_bm_read; bat_str=$_bat_str; bat_idx=$_bat_idx; bd_read=$_bd_read; bf_start=$_bf_start; bf_stop=$_bf_stop; bf_dischg=$_bf_dischg; rc=$rc" ;;
+        1)   echo_debug "bat" "battery_present($1).not_detected: bm_read=$_bm_read; rc=$rc" ;;
+        255) echo_debug "bat" "battery_present($1).no_api" ;;
+    esac
+
+    return $rc
+}
+
+# --- Battery Charge Thresholds
+
+get_threshold () { # read and echo charge threshold
+    # $1: start/stop
+    # global param: $_bm_thresh, $_bat_idx, $_bf_start, $_bf_stop
+    # rc: threshold (1..100, 255=error)
+    local bsys rc tprc
+
+    case $_bm_thresh in
+        natacpi|tpsmapi)
+            case $1 in
+                start) bsys=$_bf_start ;;
+                stop)  bsys=$_bf_stop ;;
+            esac
+            # get effective threshold, 255 if not readable/non-existent
+            rc=$(read_sysf $bsys "255")
+            ;; # natacpi, tpsmapi
+
+        tpacpi) # use tpacpi-bat
+            if [ $_bat_idx -ne 0 ]; then
+                # bat index is valid
+                rc=$($TPACPIBAT -g $1 $_bat_idx 2> /dev/null | cut -f1 -d' '); tprc=$?
+
+                if [ $tprc -eq 0 ] && [ -n "$rc" ]; then
+                    if [ $rc -ge 128 ]; then
+                        # Remove offset of 128 for Edge S430 et al.
+                        rc=$((rc - 128))
+                    fi
+                else
+                    rc=255
+                fi
+            else
+                # bat index is invalid
+                rc=255
+            fi
+            ;; # tpacpi
+
+        *) # invalid threshold method
+            rc=255
+            ;;
+    esac
+
+    # replace 0 with factory default values
+    if [ $rc -eq 0 ]; then
+        case $1 in
+            start) rc=96 ;;
+            stop)  rc=100 ;;
+        esac
+    fi
+
+    echo_debug "bat" "get_threshold($1): bm_thresh=$_bm_thresh; bat_idx=$_bat_idx; thresh=$rc"
+    return $rc
+}
+
+set_thresholds () { # write both charge thresholds for a battery,
+    # use pre-determined method from global parms, set by battery_present()
+    # $1: BAT0/BAT1,
+    # $2: new start treshold, $3: new stop threshold,
+    # $4: 0=quiet/1=output progress and error messages
+    # global param: $_bm_thresh, $_bat_str, $_bat_idx, $_bf_start, $_bf_stop
+    # rc: 0=ok/1=write error/2=read error/255=no thresh api
+    local verb=${4:-0}
+
+    echo_debug "bat" "set_thresholds($1, $2, $3, $4): bm_thresh=$_bm_thresh; bat_str=$_bat_str; bat_idx=$_bat_idx"
+
+    # read old threshold values
+    local old_start old_stop
+
+    get_threshold start; old_start=$?
+    if [ $old_start -eq 255 ]; then
+        [ "$verb" = "1" ] && echo "Error: cannot read start threshold. Aborting." 1>&2
+        echo_debug "bat" "set_thresholds($1, $2, $3, $4).start.read_error"
+        return 2
+    fi
+
+    get_threshold stop; old_stop=$?
+    if [ $old_stop -eq 255 ]; then
+        [ "$verb" = "1" ] && echo "Error: cannot read stop threshold. Aborting." 1>&2
+        echo_debug "bat" "set_thresholds($1, $2, $3, $4).stop.read_error"
+        return 2
+    fi
+
+    # evaluate threshold args: replace empty string with -1, which means
+    # don't change this threshold
+    local new_start=${2:--1}
+    local new_stop=${3:--1}
+
+    # determine write sequence to enforce start <= stop - 4 because
+    # driver boundary conditions must be met in all write stages:
+    # - natacpi: start <= stop (write fails if not met)
+    # - tpacpi:  nothing (maybe BIOS enforces something)
+    # - tpsmapi: start <= stop - 4 (changes value for compliance)
+    local tseq
+
+    if [ $new_start -gt $((old_stop - 4)) ]; then
+        tseq="stop start"
+    else
+        tseq="start stop"
+    fi
+
+    # write new thresholds in determined sequence
+    local rc=0 step steprc
+
+    if [ "$verb" = "1" ]; then
+        echo "Setting temporary charge thresholds for $_bat_str:"
+    fi
+
+    for step in $tseq; do
+        local old_thresh new_thresh
+
+        case $step in
+            start)
+                old_thresh=$old_start
+                new_thresh=$new_start
+                ;;
+
+            stop)
+                old_thresh=$old_stop
+                new_thresh=$new_stop
+                ;;
+        esac
+
+        [ $new_thresh -eq -1 ] && continue # -1 means don't change this threshold
+
+        if [ $old_thresh -ne $new_thresh ]; then
+            # new threshold differs from effective one --> write it
+            case $_bm_thresh in
+                natacpi|tpsmapi)
+                    case $step in
+                        start) write_sysf "$new_thresh" $_bf_start ;;
+                        stop)  write_sysf "$new_thresh" $_bf_stop  ;;
+                    esac
+                    steprc=$?; [ $rc -eq 0 ] && rc=$steprc
+                    ;; # natacpi, tpsmapi
+
+                tpacpi)
+                    # replace factory default values with 0 for tpacpi-bat
+                    local nt ts
+
+                    case $step in
+                        start)
+                            ts="ST"
+                            if [ $new_thresh -eq  96 ]; then
+                                nt=0
+                            else
+                                nt=$new_thresh
+                            fi
+                            ;;
+                        stop)
+                            ts="SP"
+                            if [ $new_thresh -eq  100 ]; then
+                                nt=0
+                            else
+                                nt=$new_thresh
+                            fi
+                            ;;
+                    esac
+                    $TPACPIBAT -s $ts $_bat_idx $nt > /dev/null 2>&1;
+                    steprc=$?; [ $rc -eq 0 ] && rc=$steprc
+                    ;; # tpacpi
+
+                *) # invalid threshold method --> abort
+                    rc=255
+                    break
+                    ;;
+            esac
+            echo_debug "bat" "set_thresholds($1, $2, $3, $4).$step.write: old=$old_thresh; new=$new_thresh; steprc=$steprc"
+
+            if [ "$verb" = "1" ]; then
+                if [ $steprc -eq 0 ]; then
+                    printf "  %-5s = %3d\n" $step $new_thresh
+                else
+                    printf "  %-5s = %3d (Error: cannot set threshold)\n" $step $new_thresh 1>&2
+                fi
+            fi
+        else
+            echo_debug "bat" "set_thresholds($1, $2, $3, $4).$step.no_change: old=$old_thresh; new=$new_thresh"
+
+            if [ "$verb" = "1" ]; then
+                printf "  %-5s = %3d (no change)\n" $step $new_thresh
+            fi
+        fi
+    done # for step
+
+    echo_debug "bat" "set_thresholds($1, $2, $3, $4): rc=$rc"
+    return $rc
+}
+
+normalize_thresholds () { # check values and enforce start <= stop - 4
+    # $1: start threshold; $2: stop threshold
+    # 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" | grep -E '^[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 - 4
+    if [ $_start_thresh -ne -1 ] && [ $_stop_thresh -ne -1 ]; then
+        [ $_start_thresh -gt $((_stop_thresh - 4)) ] && _start_thresh=$((_stop_thresh - 4))
+    fi
+
+    # catch unconfigured thresholds
+    if [ $_start_thresh -ne -1 ] || [ $_stop_thresh -ne -1 ]; then
+        # at least one valid threshold
+        echo_debug "bat" "normalize_thresholds($1, $2): start=$_start_thresh; stop=$_stop_thresh"
+        return 0
+    else
+        # no valid threshold --> unconfigured
+        echo_debug "bat" "normalize_thresholds.not_configured"
+        return 1
+    fi
+}
+
+set_charge_thresholds () { # write all charge thresholds from configuration
+    # rc: 0
+
+    if battery_present BAT0; then
+        # validate thresholds and if ok write them (quiet mode)
+        normalize_thresholds "$START_CHARGE_THRESH_BAT0" "$STOP_CHARGE_THRESH_BAT0" \
+            && set_thresholds BAT0 $_start_thresh $_stop_thresh 0
+    fi
+
+    if battery_present BAT1; then
+        # validate thresholds and if ok write them (quiet mode)
+        normalize_thresholds "$START_CHARGE_THRESH_BAT1" "$STOP_CHARGE_THRESH_BAT1" \
+            && set_thresholds BAT1 $_start_thresh $_stop_thresh 0
+    fi
+
+    return 0
+}
+
+setcharge_battery () { # write charge thresholds (called from cmd line)
+    # $1: start charge threshold, $2: stop charge threshold, $3: battery
+    # rc: 0=ok/> 0=error
+
+    local bat rc start_thresh stop_thresh
+    local use_cfg=0
+    # $_bat_str is global for cancel_force_discharge() trap
+
+    # check params
+    case $# in
+        0) # no args
+            bat=DEF   # use default(1st) battery
+            use_cfg=1 # use configured values
+            ;;
+
+        1) # assume $1 is battery
+            bat=$1
+            use_cfg=1 # use configured values
+            ;;
+
+        2) # assume $1,$2 are thresholds
+            start_thresh=$1
+            stop_thresh=$2
+            bat=DEF # use default(1st) battery
+            ;;
+
+        3) # assume $1,$2 are thresholds, $3 is battery
+            start_thresh=$1
+            stop_thresh=$2
+            bat=$3
+            ;;
+    esac
+
+    # convert bat to uppercase
+    bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]")
+
+    # check bat presence and/or get default(1st) battery
+    battery_present $bat
+    case $? in
+        0) # battery present
+            # get configured values if requested
+            if [ $use_cfg -eq 1 ]; then
+                eval start_thresh="\$START_CHARGE_THRESH_${_bat_str}"
+                eval stop_thresh="\$STOP_CHARGE_THRESH_${_bat_str}"
+            fi
+            ;;
+
+        255) # no method
+            echo "Error: battery feature not available." 1>&2
+            echo_debug "bat" "setcharge_battery.no_method"
+            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
+    if normalize_thresholds $start_thresh $stop_thresh; then
+        # ok --> write them (verbose mode)
+        set_thresholds $_bat_str $_start_thresh $_stop_thresh 1
+        rc=$?
+    elif [ $use_cfg -eq 1 ]; then
+        # thresholds unconfigured
+        echo "Error: no battery thresholds configured." 1>&2
+        echo_debug "bat" "setcharge_battery.not_configured"
+        rc=1
+    else
+        # invalid threshold parameters
+        echo "Error: invalid threshold parameters given." 1>&2
+        echo_debug "bat" "setcharge_battery.invalid_param"
+        rc=1
+    fi
+
+    return $rc
+}
+
+chargeonce_battery () { # charge battery to upper threshold once
+    # $1: battery
+    # rc: 0=ok/1=error
+
+    local bat start_thresh stop_thresh temp_start_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
+    battery_present $bat
+    case $? in
+        0) ;; # battery present
+
+        255) # no method
+            echo "Error: battery feature not available." 1>&2
+            echo_debug "bat" "chargeonce_battery.no_method"
+            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 start_thresh="\$START_CHARGE_THRESH_${_bat_str}"
+    eval stop_thresh="\$STOP_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 %)
+    case $_bm_read in
+        natacpi|tpacpi) # use ACPI sysfiles
+            if [ -f $_bd_read/energy_full ]; then
+                efull=$(read_sysval $_bd_read/energy_full)
+                enow=$(read_sysval $_bd_read/energy_now)
+            fi
+
+            if [ $efull -ne 0 ]; then
+                ccharge=$(( 100 * enow / efull ))
+            else
+                ccharge=-1
+            fi
+            ;; # natacpi, tpacpi
+
+        tpsmapi) # use tp-smapi sysfiles
+            ccharge=$(read_sysval $_bd_read/remaining_percent)
+            ;; # tpsmapi
+
+        *) # invalid read method
+            rc=255
+            ;;
+    esac
+
+    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
+
+    set_thresholds $_bat_str $temp_start_thresh $stop_thresh 1
+    return $?
+}
+
+# --- Battery Forced Discharge
+
+echo_discharge_locked () { # print "locked" message
+    echo "Error: another discharge/recalibrate operation is pending." 1>&2
+    return 0
+}
+
+get_force_discharge () { # $1: BAT0/BAT1,
+    # global param: $_bm_dischg, $_bat_idx, $_bf_dischg
+    # rc: 0=off/1=on/2=discharge not present/255=no thresh api
+
+    local bsys rc=0
+
+    case $_bm_dischg in
+        natacpi|tpsmapi)
+            # read sysfile, 2 if non-existent
+            rc=$(read_sysf $_bf_dischg 2)
+            ;; # natacpi, tpsmapi
+
+        tpacpi) # read via tpacpi-bat
+            case $($TPACPIBAT -g FD $_bat_idx 2> /dev/null) in
+                yes) rc=1 ;;
+                no)  rc=0 ;;
+                *)   rc=2 ;;
+            esac
+            ;; # tpacpi
+
+        *) # invalid discharge method
+            rc=255
+            ;;
+    esac
+
+    echo_debug "bat" "get_force_discharge($1): bm_dischg=$_bm_dischg; bat_idx=$_bat_idx; rc=$rc"
+    return $rc
+}
+
+set_force_discharge () { # write force discharge state
+    # $1: BAT0/BAT1, $2: 0=off/1=on
+    # global param: $_bm_dischg, $_bat_idx, $_bf_dischg
+    # rc: 0=done/1=write error/2=discharge not present/255=no thresh api
+
+    local rc=0
+
+    case $_bm_dischg in
+        natacpi|tpsmapi)
+            if [ -f "$_bf_dischg" ]; then
+                # write force_discharge
+                write_sysf "$2" $_bf_dischg; rc=$?
+            else
+                # sysfile non-existent, possibly invalid bat argument
+                rc=2
+            fi
+            ;; # natacpi, tpsmapi
+
+        tpacpi) # use tpacpi-bat
+            $TPACPIBAT -s FD $_bat_idx $2 > /dev/null 2>&1; rc=$?
+            ;; # tpcpaci
+
+        *) # invalid discharge method
+            rc=255
+            ;;
+    esac
+
+    echo_debug "bat" "set_force_discharge($1, $2): bm_dischg=$_bm_dischg; bat_idx=$_bat_idx; rc=$rc"
+
+    return $rc
+}
+
+cancel_force_discharge () { # called from trap -- global param: $_bat_str
+    set_force_discharge $_bat_str 0
+    unlock_tlp tlp_discharge
+    echo_debug "bat" "force_discharge.cancelled($_bat_str)"
+    echo " Cancelled."
+
+    exit 0
+}
+
+battery_discharging () { # check if battery is discharging -- $1: BAT0/BAT1,
+    # global param: $_bm_read, $_bd_read
+    # rc: 0=discharging/1=not discharging/255=no battery api
+
+    local bsys rc=255
+
+    # determine status sysfile
+    case $_bm_read in
+        natacpi|tpacpi)
+            bsys=$_bd_read/status # use ACPI sysfile
+            ;;
+
+        tpsmapi)
+            bsys=$_bd_read/state # use tpsmapi sysfile
+            ;;
+
+        *) # invalid read method
+            bsys=""
+            rc=255
+            ;;
+    esac
+
+    # get battery state
+    if [ -f "$bsys" ]; then
+        case "$(read_sysf $bsys)" in
+            [Dd]ischarging) rc=0 ;;
+            *) rc=1 ;;
+        esac
+    fi
+
+    echo_debug "bat" "battery_discharging($1): bm_read=$_bm_read; rc=$rc"
+    return $rc
+}
+
+discharge_battery () { # discharge battery
+    # $1: battery
+    # global param: $_tpacpi, $_tpsmapi
+    # rc: 0=ok/1=error
+
+    local bat en ef pn rc wt
+    # $_bat_str is global for cancel_force_discharge() trap
+
+    # check params
+    bat=$1
+    bat=${bat:=DEF}
+    bat=$(printf '%s' "$bat" | tr "[:lower:]" "[:upper:]")
+
+    # check if selected battery is present
+    battery_present $bat
+    case $? in
+        0) ;; # battery present
+
+        255) # no method
+            echo "Error: battery feature not available." 1>&2
+            echo_debug "bat" "discharge_battery.no_method"
+            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
+    set_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 laptop." 1>&2
+        return 1
+    fi
+
+    trap cancel_force_discharge INT # enable ^C hook
+
+    # wait for start == while status not "discharging" -- 5.0 sec timeout
+    wt=10
+    while ! battery_discharging $_bat_str && [ $wt -gt 0 ] ; do sleep 0.5; wt=$((wt - 1)); done
+
+    if battery_discharging $_bat_str; then
+        # discharge initiated sucessfully --> wait for completion == while status "discharging"
+        echo_debug "bat" "discharge_battery.running($_bat_str)"
+
+        while battery_discharging $_bat_str; do
+            clear
+            echo "Currently discharging battery $_bat_str:"
+
+            # show current battery state
+            case $_bm_read in
+                natacpi|tpacpi) # use ACPI sysfiles
+                    perl -e 'printf ("voltage            = %6d [mV]\n", '"$(read_sysval $_bd_read/voltage_now)"' / 1000.0);'
+
+                    en=$(read_sysval $_bd_read/energy_now)
+                    perl -e 'printf ("remaining capacity = %6d [mWh]\n", '$en' / 1000.0);'
+
+                    ef=$(read_sysval $_bd_read/energy_full)
+                    if [ "$ef" != "0" ]; then
+                        perl -e 'printf ("remaining percent  = %6d [%%]\n", 100.0 * '$en' / '$ef' );'
+                    else
+                        printf "remaining percent  = not available [%%]\n"
+                    fi
+
+                    pn=$(read_sysval $_bd_read/power_now)
+                    if [ "$pn" != "0" ]; then
+                        perl -e 'printf ("remaining time     = %6d [min]\n", 60.0 * '$en' / '$pn');'
+                        perl -e 'printf ("power              = %6d [mW]\n", '$pn' / 1000.0);'
+                    else
+                        printf "remaining time     = not discharging [min]\n"
+                    fi
+                    printf "state              = %s\n"  "$(read_sysf $_bd_read/status)"
+                    ;; # natacpi, tpsmapi
+
+                tpsmapi) # use tp-smapi sysfiles
+                    printf "voltage            = %6s [mV]\n"  "$(read_sysf $_bd_read/voltage)"
+                    printf "remaining capacity = %6s [mWh]\n" "$(read_sysf $_bd_read/remaining_capacity)"
+                    printf "remaining percent  = %6s [%%]\n"  "$(read_sysf $_bd_read/remaining_percent)"
+                    printf "remaining time     = %6s [min]\n" "$(read_sysf $_bd_read/remaining_running_time_now)"
+                    printf "power              = %6s [mW]\n"  "$(read_sysf $_bd_read/power_avg)"
+                    printf "state              = %s\n"  "$(read_sysf $_bd_read/state)"
+                    ;; # tpsmapi
+
+            esac
+            get_force_discharge $_bat_str; printf "force discharge    = %s\n"  "$?"
+
+            echo "Press Ctrl+C to cancel."
+            sleep 5
+        done
+    else
+        # discharge malfunction --> cancel discharge and abort
+        set_force_discharge $_bat_str 0;
+        echo_debug "bat" "discharge_battery.malfunction($_bat_str)"
+        echo "Error: discharge $_bat_str malfunction." 1>&2
+        trap - INT # remove ^C hook
+        return 1
+    fi
+
+    trap - INT # remove ^C hook
+
+    # ThinkPad E-series firmware may keep force_discharge active --> cancel it
+    ! get_force_discharge $_bat_str && set_force_discharge $_bat_str 0
+
+    echo
+    echo "Done: battery $_bat_str was completely discharged."
+    echo_debug "bat" "discharge_battery.complete($_bat_str)"
+    return 0
+}
diff -Nru tlp-1.1/func.d/40-tlp-func-bay tlp-1.2.1/func.d/40-tlp-func-bay
--- tlp-1.1/func.d/40-tlp-func-bay	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/40-tlp-func-bay	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,210 @@
+#!/bin/sh
+# tlp-func-bay - Bay Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base, 15-tlp-func-disk
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly DOCKGLOB="/sys/devices/platform/dock.?"
+
+readonly RFSTATEFILE=$RUNDIR/rfkill_saved
+readonly BAYSTATEFILE=$RUNDIR/bay_saved
+
+readonly DEFAULT_BAY_POWEROFF_ON_AC=0
+readonly DEFAULT_BAY_POWEROFF_ON_BAT=0
+readonly DEFAULT_BAY_DEVICE=sr0
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Drive Bay
+
+get_drivebay_device () { # Find generic dock interface for drive bay
+                         # rc: 0; retval: $dock
+    dock=$(grep -l 'ata_bay' $DOCKGLOB/type 2> /dev/null)
+    dock=${dock%%/type}
+    if [ ! -d "$dock" ]; then
+        dock=""
+    fi
+
+    return 0
+}
+
+check_is_docked() { # check if $dock is docked;
+                    # rc: 0 if docked, else 1
+
+   local dock_status dock_info_file
+
+   # return 0 if any sysfs file indicates "docked"
+   for dock_info_file in docked firmware_node/status; do
+        if [ -f $dock/$dock_info_file ] && \
+            read -r dock_status < $dock/$dock_info_file 2>/dev/null; then
+            # catch empty $dock_status (safety check, unlikely case)
+            [ "${dock_status:-0}" != "0" ] && return 0
+        fi
+   done
+
+   # otherwise assume "not docked"
+   return 1
+}
+
+poweroff_drivebay () { # power off optical drive in drive bay
+    # $1: 0=ac mode, 1=battery mode
+    # $2: 0=conditional+quiet mode, 1=force+verbose mode
+    # Some code adapted from https://www.thinkwiki.org/wiki/How_to_hotswap_UltraBay_devices
+
+    local pwr optdrv syspath
+
+    if [ "$1" = "1" ]; then
+        pwr=${BAY_POWEROFF_ON_BAT:-${DEFAULT_BAY_POWEROFF_ON_BAT}}
+    else
+        pwr=${BAY_POWEROFF_ON_AC:-${DEFAULT_BAY_POWEROFF_ON_AC}}
+    fi
+
+    # Run only if forced or enabled
+    if [ "$2" != "1" ]; then
+        case "$pwr" in
+            1) # enabled --> proceed
+                ;;
+
+            0) # disabled
+                echo_debug "pm" "poweroff_drivebay($1).disabled"
+                return 0
+                ;;
+
+            *) # not configured or invalid parameter
+                echo_debug "pm" "poweroff_drivebay($1).not_configured"
+                return 0
+                ;;
+        esac
+    fi
+
+    get_drivebay_device
+    if [ -z "$dock" ] || [ ! -d "$dock" ]; then
+        echo_debug "pm" "poweroff_drivebay($1).no_bay_device"
+        [ "$2" = "1" ] && echo "Error: cannot locate bay device." 1>&2
+        return 1
+    fi
+    echo_debug "pm" "poweroff_drivebay($1): dock=$dock"
+
+    # Check if bay is occupied
+    if ! check_is_docked; then
+        echo_debug "pm" "poweroff_drivebay($1).drive_already_off"
+        [ "$2" = "1" ] && echo "No drive in bay (or power already off)."
+    else
+        # Check for optical drive
+        optdrv=${BAY_DEVICE-${DEFAULT_BAY_DEVICE}}
+        if [ -z "$optdrv" ]; then
+            echo_debug "pm" "poweroff_drivebay($1).opt_drive_not_configured"
+            [ "$2" = "1" ] && echo "Error: no optical drive configured (BAY_DEVICE=\"\")." 1>&2
+            return 1
+        elif [ ! -b "/dev/$optdrv" ]; then
+            echo_debug "pm" "poweroff_drivebay($1).no_opt_drive: /dev/$optdrv"
+            [ "$2" = "1" ] && echo "No optical drive in bay (/dev/$optdrv)."
+            return 0
+        else
+            echo_debug "pm" "poweroff_drivebay($1): optdrv=$optdrv"
+            [ "$2" = "1" ] && echo -n "Powering off drive bay..."
+
+            # Unmount media
+            umount -l $optdrv > /dev/null 2>&1
+
+            # Sync drive
+            sync
+            sleep 1
+
+            # Power off drive
+            $HDPARM -Y $optdrv > /dev/null 2>&1
+            sleep 5
+
+            # Unregister scsi device
+            if syspath="$($UDEVADM info --query=path --name=$optdrv)"; then
+                syspath="/sys${syspath%/block/*}"
+
+                if [ "$syspath" != "/sys" ]; then
+                    write_sysf "1" $syspath/delete
+                    echo_debug "pm" "poweroff_drivebay($1): syspath=$syspath; rc=$?"
+                else
+                    echo_debug "pm" "poweroff_drivebay($1): got empty/invalid syspath for $optdrv"
+                fi
+            else
+                echo_debug "pm" "poweroff_drivebay($1): failed to get syspath (udevadm returned $?)"
+            fi
+
+            # Turn power off
+            write_sysf "1" $dock/undock
+            echo_debug "pm" "poweroff_drivebay($1).bay_powered_off: rc=$?"
+            [ "$2" = "1" ] && echo "done."
+        fi
+    fi
+
+    return 0
+}
+
+suspend_drivebay () { # Save power state of drive bay before suspend
+                      # $1: 0=ac mode, 1=battery mode
+
+    if [ "$1" = "1" ] && [ "${BAY_POWEROFF_ON_BAT:-${DEFAULT_BAY_POWEROFF_ON_BAT}}" = "1" ] || \
+       [ "$1" = "0" ] && [ "${BAY_POWEROFF_ON_AC:-${DEFAULT_BAY_POWEROFF_ON_AC}}"  = "1" ]; then
+        # setting corresponding to mode is active -> save state
+        get_drivebay_device
+
+        if [ -n "$dock" ]; then
+            create_rundir
+            if ! check_is_docked; then
+                write_sysf "off" $BAYSTATEFILE
+                echo_debug "pm" "suspend_drivebay($1): bay=off; rc=$?"
+            else
+                write_sysf "on" $BAYSTATEFILE
+                echo_debug "pm" "suspend_drivebay($1): bay=on; rc=$?"
+            fi
+        fi
+    else
+        # setting not active -> remove state file
+        rm -f $BAYSTATEFILE 2> /dev/null
+    fi
+
+    return 0
+}
+
+resume_drivebay () { #
+    # $1: 0=ac mode, 1=battery mode
+    local cnt rc
+
+    if [ "$(read_sysf $BAYSTATEFILE)" = "off" ]; then
+        # saved state = off
+        get_drivebay_device
+
+        if [ -n "$dock" ]; then
+            if check_is_docked; then
+                # device active -> deactivate
+                if [ -e $dock/undock ]; then
+                    cnt=5
+                    rc=1
+                    until [ $rc = 0 ] || [ $cnt = 0 ]; do
+                        cnt=$((cnt - 1))
+                        { printf '%s\n' "1" > $dock/undock; } 2> /dev/null
+                        rc=$?
+                        [ $rc = 0 ] || sleep 0.5
+                    done
+                    echo_debug "pm" "resume_drivebay.bay_off: rc=$rc"
+                fi
+            else
+                echo_debug "pm" "resume_drivebay.already_off"
+            fi
+        fi
+    else
+        # No saved state or state != off --> apply settings
+        poweroff_drivebay $1 0
+    fi
+
+    rm -f $BAYSTATEFILE 2> /dev/null
+
+    return 0
+}
diff -Nru tlp-1.1/func.d/45-tlp-func-gpu tlp-1.2.1/func.d/45-tlp-func-gpu
--- tlp-1.1/func.d/45-tlp-func-gpu	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/45-tlp-func-gpu	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,172 @@
+#!/bin/sh
+# tlp-func-gpu - Intel GPU Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly BASE_MODD=/sys/module
+readonly BASE_DRMD=/sys/class/drm
+readonly BASE_DEBUGD=/sys/kernel/debug/dri
+
+readonly IGPU_MIN_FREQ=gt_min_freq_mhz
+readonly IGPU_MAX_FREQ=gt_max_freq_mhz
+readonly IGPU_BOOST_FREQ=gt_boost_freq_mhz
+readonly IGPU_FREQ_TABLE=i915_ring_freq_table
+
+readonly RADD=/sys/module/radeon
+
+readonly DEFAULT_RADEON_POWER_PROFILE_ON_AC=default
+readonly DEFAULT_RADEON_POWER_PROFILE_ON_BAT=default
+readonly DEFAULT_RADEON_DPM_PERF_LEVEL_ON_AC=auto
+readonly DEFAULT_RADEON_DPM_PERF_LEVEL_ON_BAT=auto
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Intel GPU
+
+check_intel_gpu () { # detect Intel GPU presence and determine sysdirs
+    # rc: 0=present/1=absent
+    # retval: $_intel_gpu_parm: card parameter sysdir;
+    #         $_intel_gpu_drm:  card drm sysdir;
+    #         $_intel_gpu_dbg:  card debug sysdir
+    local cardno driver gpu
+
+    _intel_gpu_parm=""
+    _intel_gpu_drm=""
+    _intel_gpu_dbg=""
+
+    for gpu in ${BASE_DRMD}/card?; do
+        driver=$(readlink ${gpu}/device/driver)
+        driver=${driver##*/}
+        case $driver in
+            i915*) # Intel GPU found
+                cardno=${gpu##${BASE_DRMD}/card}
+                _intel_gpu_parm=${BASE_MODD}/${driver}/parameters
+                _intel_gpu_drm=${gpu}
+                _intel_gpu_dbg=${BASE_DEBUGD}/${cardno}
+                echo_debug "pm" "check_intel_gpu.present: parm=$_intel_gpu_parm; drm=$_intel_gpu_drm; dbg=$_intel_gpu_dbg"
+                return 0
+                ;;
+        esac
+    done
+
+    # no Intel GPU found
+    echo_debug "pm" "check_intel_gpu.no_card"
+    return 1
+}
+
+set_intel_gpu_min_max_boost_freq () { # set gpu frequency limits -- $1: 0=ac mode, 1=battery mode
+    local minfreq maxfreq boostfreq
+    local conf=0
+
+    check_intel_gpu || return 0
+
+    if [ "$1" = "1" ]; then
+        minfreq=${INTEL_GPU_MIN_FREQ_ON_BAT:-}
+        maxfreq=${INTEL_GPU_MAX_FREQ_ON_BAT:-}
+        boostfreq=${INTEL_GPU_BOOST_FREQ_ON_BAT:-}
+    else
+        minfreq=${INTEL_GPU_MIN_FREQ_ON_AC:-}
+        maxfreq=${INTEL_GPU_MAX_FREQ_ON_AC:-}
+        boostfreq=${INTEL_GPU_BOOST_FREQ_ON_AC:-}
+    fi
+
+    if [ -n "$minfreq" ] && [ "$minfreq" != "0" ]; then
+        write_sysf "$minfreq" $_intel_gpu_drm/$IGPU_MIN_FREQ
+        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).min: $minfreq; rc=$?"
+        conf=1
+    fi
+
+    if [ -n "$maxfreq" ] && [ "$maxfreq" != "0" ]; then
+        write_sysf "$maxfreq" $_intel_gpu_drm/$IGPU_MAX_FREQ
+        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).max: $maxfreq; rc=$?"
+        conf=1
+    fi
+
+    if [ -n "$boostfreq" ] && [ "$boostfreq" != "0" ]; then
+        write_sysf "$boostfreq" $_intel_gpu_drm/$IGPU_BOOST_FREQ
+        echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).boost: $boostfreq; rc=$?"
+        conf=1
+    fi
+
+    [ $conf -eq 1 ] ||  echo_debug "pm" "set_intel_gpu_min_max_boost_freq($1).not_configured"
+    return 0
+}
+
+# --- AMD Radeon GPU
+
+set_radeon_profile () { # set radeon power profile
+    # $1: 0=ac mode, 1=battery mode
+
+    local card level pwr rc1 rc2
+    local sdone=0 # 1=radeon present
+
+    if [ ! -d $RADD ]; then
+        # No card present --> 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-${DEFAULT_RADEON_DPM_PERF_LEVEL_ON_BAT}}
+            else
+                pwr=${RADEON_DPM_STATE_ON_AC:-}
+                level=${RADEON_DPM_PERF_LEVEL_ON_AC-${DEFAULT_RADEON_DPM_PERF_LEVEL_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
+                write_sysf "$pwr" $card/power_dpm_state; rc1=$?
+                write_sysf "$level" $card/power_dpm_force_performance_level; rc2=$?
+                echo_debug "pm" "set_radeon_profile($1): $card: state=${pwr}: rc=$rc1; perf=${level}: rc=$rc2"
+            fi
+
+            sdone=1
+
+        elif [ -f $card/power_method ] && [ -f $card/power_profile ]; then
+            # Use old radeon power profile method
+            if [ "$1" = "1" ]; then
+                pwr=${RADEON_POWER_PROFILE_ON_BAT-${DEFAULT_RADEON_POWER_PROFILE_ON_BAT}}
+            else
+                pwr=${RADEON_POWER_PROFILE_ON_AC-${DEFAULT_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
+                write_sysf "profile" $card/power_method; rc1=$?
+                write_sysf "$pwr" $card/power_profile; rc2=$?
+                echo_debug "pm" "set_radeon_profile($1): $card; method=profile: rc=$rc1; profile=${pwr}: rc=$rc2"
+            fi
+
+            sdone=1
+        fi
+    done
+
+    if [ $sdone -eq 0 ]; then
+        echo_debug "pm" "set_radeon_profile($1).not_available"
+    fi
+
+    return 0
+}
diff -Nru tlp-1.1/func.d/tlp-func-stat tlp-1.2.1/func.d/tlp-func-stat
--- tlp-1.1/func.d/tlp-func-stat	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/func.d/tlp-func-stat	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,607 @@
+#!/bin/sh
+# tlp-func-stat - tlp-stat Helper Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# Needs: tlp-func-base, 15-tlp-func-disk, 35-tlp-func-batt
+
+# shellcheck disable=SC1004,SC2059,SC2086,SC2154
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly INITCTL=initctl
+readonly SMARTCTL=smartctl
+readonly SYSTEMCTL=systemctl
+readonly SYSTEMD=systemd
+
+readonly RE_AC_QUIRK='^UNDEFINED$'
+readonly RE_ATA_ERROR='ata[0-9]+: SError: {.*CommWake }'
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Checks
+
+check_systemd () { # check if systemd is the active init system (PID 1) and systemctl is installed
+                   # rc: 0=yes, 1=no
+    [ -d /run/systemd/system ] && cmd_exists $SYSTEMCTL
+}
+
+check_upstart () { # check if upstart is active init system (PID 1)
+                   # rc: 0=yes, 1=no
+    cmd_exists $INITCTL && $INITCTL --version | grep -q upstart
+}
+
+check_openrc () { # check if openrc is the active init system (PID 1)
+                  # rc: 0=yes, 1=no
+    [ -e /run/openrc/softlevel ]
+}
+
+check_ac_quirk () { # check for hardware known not to expose AC device
+                    # $1: model string; rc: 0=yes, 1=no
+    printf '%s' "$1" | grep -E -q "${RE_AC_QUIRK}"
+}
+
+# --- Formatted Output
+
+printparm () { # formatted output of sysfile - general
+    # $1: format, $2: sysfile, $3: n/a message, $4: cutoff
+    local format="$1"
+    local sysf="$2"
+    local namsg="$3"
+    local cutoff="$4"
+    local val=""
+
+    if val=$(read_sysf $sysf); then
+        # sysfile read successful
+        if [ -n "$cutoff" ]; then
+            val=${val%$cutoff}
+        fi
+    fi
+
+    if [ -z "$val" ]; then
+        # replace empty value with n/a text
+        if [ -n "$namsg" ]; then
+            if [ "$namsg" != "_" ]; then
+                # use specific n/a text
+                format=$(echo $format | sed -r -e "s/##(.*)##/($namsg)/" -e "s/\[.*\]//")
+            else
+                # _ = skip
+                sysf=""
+            fi
+        else
+            # empty n/a text, use default text
+            format=$(echo $format | sed -r -e "s/##(.*)##/(not available)/" -e "s/\[.*\]//")
+        fi
+        # output n/a text or skip
+        [ -n "$sysf" ] && printf "$format\n" "$sysf"
+    else
+        # non empty value: strip delimiters from format str
+        format=$(echo $format | sed -r "s/##(.*)##/\1/")
+        printf "$format\n" "$sysf" "$val"
+    fi
+
+    return 0
+}
+
+printparm_i915 () { # formatted output of sysfile - i915 kernel module variant
+    # $1: sysfile; $2: alternative; $3: 1=psr/0=other
+    local sysf val
+
+    # Check if sysfile or alternative exist
+    if [ -f "$1" ]; then
+        sysf=$1
+    elif [ -f "$2" ]; then
+        sysf=$2
+    else
+        # neither file exists --> quit silently
+        return 0
+    fi
+
+    if val=$(read_sysf $sysf); then
+        # sysfile exists and is actually readable, output content
+        printf "%-44s = %2d " "$sysf" "$val"
+        # Explain content
+        if [ "$val" = "-1" ]; then
+            echo "(use per-chip default)"
+        else
+            echo -n "("
+            if [ "$3" = "1" ]; then
+                # enable_psr
+                case $val in
+                    0) echo -n "disabled" ;;
+                    1) echo -n "enabled" ;;
+                    2) echo -n "force link-standby mode" ;;
+                    3) echo -n "force link-off mode" ;;
+                    *) echo -n "unknown" ;;
+                esac
+            else
+                # other parms
+                if [ $((val & 1)) -ne 0 ]; then
+                    echo -n "enabled"
+                else
+                    echo -n "disabled"
+                fi
+                [ $((val & 2)) -ne 0 ] && echo -n " + deep"
+                [ $((val & 4)) -ne 0 ] && echo -n " + deepest"
+            fi
+            echo ")"
+        fi
+    else
+        # sysfile was not readable
+        printf "%-44s = (not available)\n" "$sysf"
+    fi
+
+    return 0
+}
+
+print_sysf () { # formatted output of a sysfile
+    # $1: format; $2: sysfile
+    local val
+
+    if val=$(read_sysf $2); then
+         # sysfile readable
+        printf "$1" "$val"
+    else
+        # sysfile not readable
+        printf "$1" "(not available)"
+    fi
+
+    return 0
+}
+
+print_sysf_trim () { # formatted output of a sysfile, trim leading and trailing
+    # blanks -- $1: format; $2: sysfile
+    local val
+
+    if val=$(read_sysf $2); then
+         # sysfile readable
+        printf "$1" "$(printf "%s" "$val" | sed -r 's/^[[:blank:]]*//;s/[[:blank:]]*$//')"
+    else
+        # sysfile not readable
+        printf "$1" "(not available)"
+    fi
+
+    return 0
+}
+
+print_file_modtime_and_age () { # show a file's last modification time
+    #  and age in secs -- $1: file
+    local mtime age
+
+    if [ -f "$1" ]; then
+        mtime=$(date +%X -r $1)
+        age=$(( $(date +%s) - $(date +%s -r $1) ))
+        printf '%s, %6d sec(s) ago' "$mtime" "$age"
+    else
+        printf "unknown"
+    fi
+}
+
+print_saved_powerstate () { # read and print saved state
+    case "$(read_sysf $PWRRUNFILE)" in
+        0) printf "AC" ;;
+        1) printf "battery" ;;
+        *) printf "unknown" ;;
+    esac
+
+    # add manual mode
+    get_manual_mode
+    case "$_manual_mode" in
+        0|1) printf " (manual)\n" ;;
+        *)   printf "\n" ;;
+    esac
+
+    return 0
+}
+
+# --- Storage Devices
+
+print_disk_model () { # print disk model -- $1: dev
+    local model vendor
+
+    model=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Model Number' | \
+      cut -f2 -d: | sed -r 's/^ *//' )
+
+    if [ -z "$model" ]; then
+        # hdparm -I not supported --> try udevadm approach
+        vendor="$($UDEVADM info -n /dev/$1 -q property 2>/dev/null | sed -n 's/^ID_VENDOR=//p')"
+        model="$( $UDEVADM info -n /dev/$1 -q property 2>/dev/null | sed -n 's/^ID_MODEL=//p' )"
+        model=$(printf "%s %s" "$vendor" "$model" | sed -r 's/_/ /g; s/-//g; s/[[:space:]]+$//')
+    fi
+
+    printf '%s\n' "${model:-unknown}"
+
+    return 0
+}
+
+print_disk_firmware () { # print firmware version --- $1: dev
+    local firmware
+
+    firmware=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Firmware Revision' | \
+      cut -f2 -d: | sed -r 's/^ *//' )
+    printf '%s\n' "${firmware:-unknown}"
+
+    return 0
+}
+
+get_disk_state () { # get disk power state -- $1: dev; retval: $_disk_state
+    _disk_state=$($HDPARM -C /dev/$1 2> /dev/null | awk -F ':' '/drive state is/ { gsub(/ /,"",$2); print $2; }')
+    [ -z "$_disk_state" ] && _disk_state="(not available)"
+
+    return 0
+}
+
+get_disk_apm_level () { # get disk apm level -- $1: dev; rc: apm
+    local apm
+
+    apm=$($HDPARM -I /dev/$1 2> /dev/null | grep 'Advanced power management level' | \
+          cut -f2 -d: | grep -E '^ *[0-9]+ *$')
+    if [ -n "$apm" ]; then
+        return $apm
+    else
+        return 0
+    fi
+
+}
+
+get_disk_trim_capability () { # check for trim capability
+    # $1: dev; rc: 0=no, 1=yes, 254=no ssd device
+
+    local trim
+
+    if $HDPARM -I /dev/$1 2> /dev/null | grep -q 'Solid State Device'; then
+        if $HDPARM -I /dev/$1 2> /dev/null | grep -q 'TRIM supported'; then
+            trim=1
+        else
+            trim=0
+        fi
+    else
+        trim=255
+    fi
+
+    return $trim
+}
+
+check_ata_errors () { # check kernel log for ata errors
+    # (possibly) caused by SATA_LINKPWR_ON_AC/BAT != max_performance
+    # stdout: error count
+
+    if wordinlist $SATA_LINKPWR_ON_BAT "min_power medium_power" || \
+       wordinlist $SATA_LINKPWR_ON_AC "min_power medium_power"; then
+        # config values != max_performance exist --> check kernel log
+
+        # count matching error lines
+        dmesg | grep -E -c "${RE_ATA_ERROR}" 2> /dev/null
+    else
+        # no values in question configured
+        echo "0"
+    fi
+
+    return 0
+}
+
+show_disk_data () { # formatted output of NVMe / SATA disk data
+    # $1: disk device
+
+    # translate disk name
+    get_disk_dev $1
+
+    # check if block device
+    if [ ! -b /dev/$_disk_dev ]; then
+        # no block device for disk name --> we're done
+        printf     "\n%s: not present.\n" /dev/$_disk_dev
+        return 1
+    fi
+
+    # --- show general data
+    case "$_disk_type" in
+        nvme) # NVMe disk
+            printf     "\n%s:\n" /dev/$_disk_dev
+            printf     "  Type      = NVMe\n"
+            [ -n "$_disk_id" ] && printf "  Disk ID   = %s\n" $_disk_id
+            print_sysf "  Model     = %s\n" /sys/block/$_disk_dev/device/model
+            print_sysf "  Firmware  = %s\n" /sys/block/$_disk_dev/device/firmware_rev
+            # TODO: more features?
+            ;;
+
+        ata|usb|ieee1394)
+            # ATA/USB/IEEE1394 disk
+            printf     "\n%s:\n" /dev/$_disk_dev
+            [ -n "$_disk_id" ] && printf "  Disk ID   = %s\n" $_disk_id
+
+            printf     "  Type      = %s\n" "$(toupper $_disk_type)"
+
+            # save spindle state
+            get_disk_state $_disk_dev
+
+            printf "  Model     = "
+            print_disk_model $_disk_dev
+
+            printf "  Firmware  = "
+            print_disk_firmware $_disk_dev
+
+            get_disk_apm_level $_disk_dev; local apm=$?
+            printf "  APM Level = "
+            case $apm in
+                0|255)
+                    printf "none/disabled\n"
+                    ;;
+
+                *)
+                    printf "%s" $apm
+                    if wordinlist "$_disk_type" "$DISK_TYPES_NO_APM_CHANGE"; then
+                        printf " (changes not supported)\n"
+                    else
+                        printf "\n"
+                    fi
+                    ;;
+            esac
+
+            printf "  Status    = %s\n" $_disk_state
+
+            get_disk_trim_capability $_disk_dev; local trim=$?
+            case $trim in
+                0) printf "  TRIM      = not supported\n" ;;
+                1) printf "  TRIM      = supported\n" ;;
+            esac
+
+            # restore standby state
+            [ "$_disk_state" = "standby" ] && spindown_disk $_disk_dev
+            ;;
+
+        *)
+            printf     "\n%s: Device type \"%s\" ignored.\n" /dev/$_disk_dev $_disk_type
+            return 1
+            ;;
+    esac
+
+    if [ -f /sys/block/$_disk_dev/queue/scheduler ]; then
+        if [ -d /sys/block/$_disk_dev/mq ]; then
+            print_sysf_trim "  Scheduler = %s (multi queue)\n" /sys/block/$_disk_dev/queue/scheduler
+        else
+            print_sysf_trim "  Scheduler = %s (single queue)\n" /sys/block/$_disk_dev/queue/scheduler
+        fi
+    fi
+
+    local pdev=/sys/block/$_disk_dev/device/power
+    if [ -f $pdev/control ]; then
+        echo
+        print_sysf "  Runtime PM: control = %s, " $pdev/control
+        print_sysf "autosuspend_delay_ms = %4s\n"    $pdev/autosuspend_delay_ms
+    fi
+
+    # --- show SMART data
+    # skip if smartctl not installed or disk not SMART capable
+    cmd_exists $SMARTCTL && $SMARTCTL /dev/$_disk_dev > /dev/null 2>&1 || return 0
+
+    case "$_disk_type" in
+        nvme)
+            # NVMe disk
+            printf "\n  SMART info:\n"
+            $SMARTCTL -A /dev/$_disk_dev | \
+                grep -E -e '^(Critical Warning|Temperature:|Available Spare)' \
+                        -e '^(Percentage Used:|Data Units Written:|Power|Unsafe)' \
+                        -e 'Integrity Errors' | \
+                    sed 's/^/    /'
+            ;;
+
+        ata|usb)
+            printf "\n  SMART info:\n"
+            $SMARTCTL -A /dev/$_disk_dev | grep -v '<==' | \
+                awk -F ' ' '$2 ~ /Power_Cycle_Count|Start_Stop_Count|Load_Cycle_Count|Reallocated_Sector_Ct/ \
+                                { printf "    %3d %-25s = %8d \n", $1, $2, $10 } ; \
+                          $2 ~ /Used_Rsvd_Blk_Cnt_Chip|Used_Rsvd_Blk_Cnt_Tot|Unused_Rsvd_Blk_Cnt_Tot/ \
+                                { printf "    %3d %-25s = %8d \n", $1, $2, $10 } ; \
+                          $2 ~ /Power_On_Hours/ \
+                                { printf "    %3d %-25s = %8d %s\n", $1, $2, $10, "[h]" } ; \
+                          $2 ~ /Temperature_Celsius/ \
+                                { printf "    %3d %-25s = %8d %s %s %s %s\n", $1, $2, $10, $11, $12, $13, "[°C]" } ; \
+                          $2 ~ /Airflow_Temperature_Cel/ \
+                                { printf "    %3d %-25s = %8d %s\n", $1, $2, $10, "[°C]" } ; \
+                          $2 ~ /G-Sense_Error_Rate/ \
+                                { printf "    %3d %-25s = %8d \n", $1, $2, $10 } ; \
+                          $2 ~ /Host_Writes/ \
+                                { printf "    %3d %-25s = %8.3f %s\n", $1, $2, $10 / 32768.0, "[TB]" } ; \
+                          $2 ~ /Total_LBAs_Written/ \
+                                { printf "    %3d %-25s = %8.3f %s\n", $1, $2, $10 / 2147483648.0, "[TB]" } ; \
+                          $2 ~ /NAND_Writes_1GiB/ \
+                                { printf "    %3d %-25s = %8d %s\n", $1, $2, $10, "[GB]" } ; \
+                          $2 ~ /Available_Reservd_Space|Media_Wearout_Indicator|Wear_Leveling_Count/ \
+                                { printf "    %3d %-25s = %8d %s\n", $1, $2, $4, "[%]" }'
+            ;;
+
+        *) # unkown disk type
+            ;;
+    esac
+
+    return 0
+}
+
+# --- Graphics
+
+show_intel_gpu_data () { # show Intel GPU data
+
+    check_intel_gpu || return 0
+
+    # power managment data
+    echo "+++ Intel Graphics"
+    printparm_i915 $_intel_gpu_parm/powersave
+    printparm_i915 $_intel_gpu_parm/enable_rc6 $_intel_gpu_parm/i915_enable_rc6
+    printparm_i915 $_intel_gpu_parm/enable_dc
+    printparm_i915 $_intel_gpu_parm/enable_fbc $_intel_gpu_parm/i915_enable_fbc
+    printparm_i915 $_intel_gpu_parm/enable_psr "" 1
+    printparm_i915 $_intel_gpu_parm/lvds_downclock
+    printparm_i915 $_intel_gpu_parm/modeset
+    printparm_i915 $_intel_gpu_parm/semaphores
+    echo
+
+    # frequency parameters
+    if readable_sysf $_intel_gpu_drm/$IGPU_MIN_FREQ; then
+        printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_MIN_FREQ
+        printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_MAX_FREQ
+        printparm "%-44s = ##%5d## [MHz]" $_intel_gpu_drm/$IGPU_BOOST_FREQ
+        if readable_sysf $_intel_gpu_dbg/$IGPU_FREQ_TABLE; then
+            # available frequencies
+            printf "%s: " $_intel_gpu_dbg/$IGPU_FREQ_TABLE
+            awk -F ' ' '{ if (NR >= 2) { printf "%d ", $1 } }; ' $_intel_gpu_dbg/$IGPU_FREQ_TABLE
+            printf "[MHz] \n"
+        fi
+        echo
+    fi
+
+    return 0
+}
+
+# --- Battery Features
+
+print_methods_per_driver () { # show features provided by a battery driver
+    # $1: driver = natacpi, tpacpi, tpsmapi
+    local bm m mlist=""
+
+    for bm in _bm_read _bm_thresh _bm_dischg; do
+        if [ "$(eval echo \$$bm)" = "$1" ]; then
+            # method matches driver
+            case $bm in
+                _bm_read)   m="data" ;;
+                _bm_thresh) m="thresholds" ;;
+                _bm_dischg) m="discharge" ;;
+            esac
+            # concat method to output
+            if [ -n "$mlist" ]; then
+                mlist="$mlist, $m"
+            else
+                mlist="$m"
+            fi
+        fi
+    done
+
+    if [ -n "$mlist" ]; then
+        printf "(%s)\n" "$mlist"
+    else
+        printf "(none)\n"
+    fi
+
+    return 0
+}
+
+print_batstate () { # print battery charging state with
+    # an explanation when a threshold inhibits charging
+    # $1: sysfile
+    local sysf val
+
+    # check if bat state sysfile exists
+    if [ -f "$1" ]; then
+        sysf=$1
+    else
+        # sysfile non-existent
+        printf "%-59s = (not available)\n" "$1"
+        return 0
+    fi
+
+    if val=$(read_sysf $sysf); then
+        # sysfile was readable, output content
+        printf "%-59s = %s" "$sysf" "$val"
+        # Explain content if necessary
+        case $val in
+            "Unknown"|"Not charging") # a threshold forbids charging
+                printf " (threshold effective)\n"
+                ;;
+
+            *) # Nothing to do
+                printf "\n"
+                ;;
+        esac
+    else
+        # sysfile was not readable
+        printf "%-59s = (not available)\n" "$sysf"
+    fi
+
+    return 0
+}
+
+print_thresholds () { # formatted output of ThinkPad charging thresholds
+    # $1: BAT0/BAT1
+    # global param: $_bm_thresh, $_bat_idx, $_bf_start, $_bf_stop
+    local bsys sp thresh
+
+    for sp in start stop; do
+        get_threshold $sp; thresh=$?
+
+        case $sp in
+            start) bsys=$_bf_start ;;
+            stop)  bsys=$_bf_stop ;;
+        esac
+
+        if [ $thresh -ne 255 ]; then
+            # valid threshold read
+            case $_bm_thresh in
+                natacpi|tpsmapi)
+                    printf "%-59s = %6d [%%]\n" "$bsys" "$thresh"
+                    ;;
+
+                tpacpi)
+                    printf "%-59s = %6d [%%]\n" "tpacpi-bat.${1}.${sp}Threshold" "$thresh"
+                    ;;
+
+                none) ;; # nothing to show
+            esac
+        else
+            # threshold read failed
+            case $_bm_thresh in
+                natacpi|tpsmapi)
+                    printf "%-59s = (not available)\n" "$bsys"
+                    ;;
+
+                tpacpi)
+                    printf "%-59s = (not available)\n" "tpacpi-bat.${1}.${sp}Threshold"
+                    ;;
+
+                none) ;; # nothing to show
+            esac
+        fi
+    done # for sp
+
+    return 0
+}
+
+print_discharge () { # formatted output of ThinkPad force_discharge
+    # $1: BAT0/BAT1
+    # global param: $_bm_dischg, $_bat_idx, $_bf_dischg
+    local force
+
+    get_force_discharge $1; force=$?
+
+    if [ $force -lt 2 ]; then
+        # valid force_discharge read
+        case $_bm_dischg in
+            natacpi|tpsmapi)
+                printf "%-59s = %6d\n" "$_bf_dischg" "$force"
+                ;;
+
+            tpacpi)
+                printf "%-59s = %6d\n" "tpacpi-bat.${1}.forceDischarge" "$force"
+                ;;
+
+            none) ;; # nothing to show
+        esac
+    else
+        # force_discharge read failed
+        case $_bm_dischg in
+            natacpi|tpsmapi)
+                printf "%-59s = (not available)\n" "$_bf_dischg"
+                ;;
+
+            tpacpi)
+                printf "%-59s = %6d\n" "tpacpi-bat.${1}.forceDischarge" "(not available)"
+                ;;
+
+            none) ;; # nothing to show
+        esac
+    fi
+
+    return 0
+}
+
diff -Nru tlp-1.1/.github/Bug_Reporting_Howto.md tlp-1.2.1/.github/Bug_Reporting_Howto.md
--- tlp-1.1/.github/Bug_Reporting_Howto.md	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/.github/Bug_Reporting_Howto.md	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,24 @@
+## How to submit a bug report
+
+### Before you report a bug
+Make shure you have:
+
+* Followed the appropiate [Installation instructions](https://linrunner.de/en/tlp/docs/tlp-linux-advanced-power-management.html#installation)
+* Read the [Settings guide](https://linrunner.de/en/tlp/docs/tlp-configuration.html)
+* Carefully checked the [FAQ](https://linrunner.de/en/tlp/docs/tlp-faq.html)
+* Tried to isolate the cause as described in [Troubleshooting](https://linrunner.de/en/tlp/docs/tlp-troubleshooting.html)
+
+### What not to report
+* Missing packages (including tp-smapi and acpi_call)
+* Asking for help about installation, configuration and usage
+* Deviations from powertop's recommendations
+* Hardware issues e.g. worn out or malfunctioning batteries
+
+Please use adequate Linux forums for help and support questions.
+
+### Reporting a bug
+This project uses [GitHub issues](https://github.com/linrunner/TLP/issues) for bug reports and feature requests.
+
+When opening an issue, provide all the information requested by the template.
+Bug reports not providing the necessary information get flagged *incomplete* and
+may be closed without further notice.
diff -Nru tlp-1.1/.github/CONTRIBUTING.md tlp-1.2.1/.github/CONTRIBUTING.md
--- tlp-1.1/.github/CONTRIBUTING.md	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/.github/CONTRIBUTING.md	2019-03-18 18:08:25.000000000 +0100
@@ -1,14 +1,29 @@
-### Issues
+## How can I contribute to TLP?
 
-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.
+Contributing is not only about coding and pull requests. Volunteers helping
+with testing and support are always welcome!
 
-*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).
+### Testing
+You may install and test the master branch at any time. Feel free to
+[file bug reports](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md)
+against it.
 
-**When opening an issue, provide all the information requested by the template.**
+Beta packages are [published here](https://download.linrunner.de/packages/) once available.
+
+Please refer to the [developer docs](https://linrunner.de/en/tlp/docs/tlp-developer-documentation.html)
+for more information.
+
+### Support
+Experienced users are welcome to help others with TLP
+in their Linux community:
+
+* Reddit
+* StackExchange
+* Forums
+* User groups
+* or else
 
 ### Code
 
-Please read the [developer docs](http://linrunner.de/en/tlp/docs/tlp-developer-documentation.html) before contributing code.
+Read the [developer docs](https://linrunner.de/en/tlp/docs/tlp-developer-documentation.html)
+before developing code. Please base your pull requests on the master branch.
diff -Nru tlp-1.1/.github/ISSUE_TEMPLATE/bug_report.md tlp-1.2.1/.github/ISSUE_TEMPLATE/bug_report.md
--- tlp-1.1/.github/ISSUE_TEMPLATE/bug_report.md	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/.github/ISSUE_TEMPLATE/bug_report.md	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+[x] I've read and accepted the [Bug Reporting Howto](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md)
+
+**Describe the bug**
+
+A clear and concise description of what the bug is.
+
+**Expected behavior**
+
+A clear and concise description of what you expected to happen.
+
+**To Reproduce**
+
+Steps to reproduce the unexpected behavior:
+
+1. Does the problem occur on battery or AC or both?
+2. Actions to reproduce the behaviour
+3. Shell commands entered and their output
+4. **Full output of `tlp-stat` via https://gist.github.com/ for *all* cases of 1**
+
+**Additional context**
+
+Add any other context about the problem here.
diff -Nru tlp-1.1/.github/ISSUE_TEMPLATE/feature_request.md tlp-1.2.1/.github/ISSUE_TEMPLATE/feature_request.md
--- tlp-1.1/.github/ISSUE_TEMPLATE/feature_request.md	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/.github/ISSUE_TEMPLATE/feature_request.md	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,30 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature request
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+
+A clear and concise description of what the problem is. Ex. I repeatedly ran into [...]
+
+**Describe the solution you'd like**
+
+A clear and concise description of:
+
+* Your use case(s)
+* What you want to happen
+
+**Describe alternatives you've considered**
+
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+
+Add any other context and references about the feature request here:
+
+* New kernel interfaces: please provide links to the description and usage examples
+* Sample shell code
diff -Nru tlp-1.1/.github/ISSUE_TEMPLATE.md tlp-1.2.1/.github/ISSUE_TEMPLATE.md
--- tlp-1.1/.github/ISSUE_TEMPLATE.md	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/.github/ISSUE_TEMPLATE.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-### 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
diff -Nru tlp-1.1/Makefile tlp-1.2.1/Makefile
--- tlp-1.1/Makefile	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/Makefile	2019-03-18 18:08:25.000000000 +0100
@@ -4,12 +4,13 @@
 TLP_SBIN   ?= /usr/sbin
 TLP_BIN    ?= /usr/bin
 TLP_TLIB   ?= /usr/share/tlp
-TLP_PLIB   ?= /usr/lib/pm-utils
+TLP_FLIB   ?= /usr/share/tlp/func.d
 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_ELOD   ?= /lib/elogind/system-sleep
 TLP_SHCPL  ?= /usr/share/bash-completion/completions
 TLP_MAN    ?= /usr/share/man
 TLP_META   ?= /usr/share/metainfo
@@ -19,12 +20,13 @@
 _SBIN  = $(DESTDIR)$(TLP_SBIN)
 _BIN   = $(DESTDIR)$(TLP_BIN)
 _TLIB  = $(DESTDIR)$(TLP_TLIB)
-_PLIB  = $(DESTDIR)$(TLP_PLIB)
+_FLIB  = $(DESTDIR)$(TLP_FLIB)
 _ULIB  = $(DESTDIR)$(TLP_ULIB)
 _NMDSP = $(DESTDIR)$(TLP_NMDSP)
 _CONF  = $(DESTDIR)$(TLP_CONF)
 _SYSD  = $(DESTDIR)$(TLP_SYSD)
 _SYSV  = $(DESTDIR)$(TLP_SYSV)
+_ELOD  = $(DESTDIR)$(TLP_ELOD)
 _SHCPL = $(DESTDIR)$(TLP_SHCPL)
 _MAN   = $(DESTDIR)$(TLP_MAN)
 _META  = $(DESTDIR)$(TLP_META)
@@ -33,18 +35,18 @@
 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_FLIB@|$(TLP_FLIB)|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-func-base \
 	tlp-rdw-nm \
 	tlp-rdw.rules \
 	tlp-rdw-udev \
+	tlp-rdw \
 	tlp-rf \
 	tlp.rules \
 	tlp-run-on \
@@ -69,13 +71,16 @@
 	tlp.service.8 \
 	tlp-sleep.service.8
 
+MANFILESRDW8 = \
+	tlp-rdw.8
+
 SHFILES = \
 	tlp.in \
-	tlp-functions.in \
-	tlp-nop.in \
+	tlp-func-base.in \
+	func.d/* \
+	tlp-rdw.in \
 	tlp-rdw-nm.in \
 	tlp-rdw-udev.in \
-	tlp-rf-func \
 	tlp-rf.in \
 	tlp-run-on.in \
 	tlp-stat.in \
@@ -104,8 +109,8 @@
 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-func-base $(_TLIB)/tlp-func-base
+	install -D -m 755 --target-directory $(_TLIB)/func.d func.d/*
 	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)
@@ -116,9 +121,8 @@
 	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
+ifeq ($(TLP_WITH_ELOGIND),1)
+	install -D -m 755 tlp-sleep.elogind $(_ELOD)/49-tlp-sleep
 endif
 ifneq ($(TLP_NO_BASHCOMP),1)
 	install -D -m 644 tlp.bash_completion $(_SHCPL)/tlp
@@ -131,9 +135,13 @@
 
 install-rdw: all
 	# Package tlp-rdw
+	install -D -m 755 tlp-rdw $(_BIN)/tlp-rdw
 	install -D -m 644 tlp-rdw.rules $(_ULIB)/rules.d/85-tlp-rdw.rules
 	install -D -m 755 tlp-rdw-udev $(_ULIB)/tlp-rdw-udev
 	install -D -m 755 tlp-rdw-nm $(_NMDSP)/99tlp-rdw-nm
+ifneq ($(TLP_NO_BASHCOMP),1)
+	install -D -m 644 tlp-rdw.bash_completion $(_SHCPL)/tlp-rdw
+endif
 
 install-man:
 	# manpages
@@ -142,6 +150,11 @@
 	install -d 755 $(_MAN)/man8
 	cd man && install -m 644 $(MANFILES8) $(_MAN)/man8/
 
+install-man-rdw:
+	# manpages
+	install -d 755 $(_MAN)/man8
+	cd man-rdw && install -m 644 $(MANFILESRDW8) $(_MAN)/man8/
+
 install: install-tlp install-rdw
 
 uninstall-tlp:
@@ -155,17 +168,13 @@
 	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 -r $(_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 $(_ELOD)/49-tlp-sleep
 	rm -f $(_SHCPL)/tlp-stat
 	rm -f $(_SHCPL)/bluetooth
 	rm -f $(_SHCPL)/wifi
@@ -175,16 +184,30 @@
 
 uninstall-rdw:
 	# Package tlp-rdw
+	rm $(_BIN)/tlp-rdw
 	rm $(_ULIB)/rules.d/85-tlp-rdw.rules
 	rm $(_ULIB)/tlp-rdw-udev
 	rm $(_NMDSP)/99tlp-rdw-nm
+	rm -f $(_SHCPL)/tlp-rdw
 
 uninstall-man:
 	# manpages
 	cd $(_MAN)/man1 && rm -f $(MANFILES1)
 	cd $(_MAN)/man8 && rm -f $(MANFILES8)
 
+uninstall-man-rdw:
+	# manpages
+	cd $(_MAN)/man8 && rm -f $(MANFILESRDW8)
+
 uninstall: uninstall-tlp uninstall-rdw
 
+checkall: checkbashisms shellcheck checkdupconst
+
 checkbashisms:
 	checkbashisms $(SHFILES) || true
+
+shellcheck:
+	shellcheck -s dash $(SHFILES) || true
+
+checkdupconst:
+	{ sed -n -r -e 's,^.*readonly\s+([A-Za-z_][A-Za-z_0-9]*)=.*$$,\1,p' $(SHFILES) | sort | uniq -d; } || true
diff -Nru tlp-1.1/man/tlp.8 tlp-1.2.1/man/tlp.8
--- tlp-1.1/man/tlp.8	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/man/tlp.8	2019-03-18 18:08:25.000000000 +0100
@@ -1,10 +1,10 @@
-.TH tlp 8 2017-01-29 "TLP 1.0" "Power Management"
+.TH tlp 8 2019-03-03 "TLP 1.2" "Power Management"
 .
 .SH NAME
 tlp \- apply laptop power saving settings
 .
 .SH SYNOPSIS
-.B tlp \fIcommand\fR \fR[\fIparameter\fR] "..."
+.B tlp \fIcommand\fR \fR[\fIparameters\fR] [-- <config> "..."]
 .
 .SH DESCRIPTION
 Apply power saving settings manually and control ThinkPad battery features.
@@ -14,7 +14,7 @@
 .TP
 .B start
 Initialize \fBtlp\fR and apply power saving settings according to the actual
-power source.
+power source. Also use to apply a changed configuration.
 .
 .TP
 .B bat
@@ -22,7 +22,7 @@
 .
 .TP
 .B true
-Same as \fBbat\fR (this command is called when power source changes to battery).
+Same as \fBbat\fR.
 .
 .TP
 .B ac
@@ -30,8 +30,7 @@
 .
 .TP
 .B false
-Same as \fBac\fR (this command is called when power source changes
-to ac).
+Same as \fBac\fR.
 .
 .TP
 .B usb
@@ -79,6 +78,11 @@
 .B diskid
 Show disk ids for configuration.
 .
+.TP
+.B \-- PARAM=value "..."
+Add parameters to temporarily overwrite the system configuration
+(for this program invocation only).
+.
 .SH NOTES
 For ThinkPads with more than one battery the selection works as follows:
 .IP
@@ -118,4 +122,4 @@
 .URL "http://linrunner.de"; "Project homepage: "
 .
 .SH AUTHOR
-(c) 2017 Thomas Koch <linrunner at gmx.net>
+(c) 2018 Thomas Koch <linrunner at gmx.net>
diff -Nru tlp-1.1/man/tlp-stat.8 tlp-1.2.1/man/tlp-stat.8
--- tlp-1.1/man/tlp-stat.8	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/man/tlp-stat.8	2019-03-18 18:08:25.000000000 +0100
@@ -1,10 +1,10 @@
-.TH tlp\-stat 8 2017-01-29 "TLP 1.0" "Power Management"
+.TH tlp\-stat 8 2018-12-16 "TLP 1.2" "Power Management"
 .
 .SH NAME
 tlp\-stat \- show power saving settings
 .
 .SH SYNOPSIS
-.B tlp\-stat \fI[option]\fR "..."
+.B tlp\-stat \fI[options]\fR [-- <config> "..."]
 .
 .SH DESCRIPTION
 Show configuration, system information, active power saving settings and battery
@@ -72,6 +72,11 @@
 .B \-v, \-\-verbose
 Be more verbose.
 .
+.TP
+.B \-- PARAM=value "..."
+Add parameters to temporarily overwrite the system configuration
+(for this program invocation only).
+.
 .SH FILES
 .I /etc/default/tlp
 .RS
@@ -81,4 +86,4 @@
 .BR tlp (8).
 .
 .SH AUTHOR
-(c) 2017 Thomas Koch <linrunner at gmx.net>
+(c) 2018 Thomas Koch <linrunner at gmx.net>
diff -Nru tlp-1.1/man-rdw/tlp-rdw.8 tlp-1.2.1/man-rdw/tlp-rdw.8
--- tlp-1.1/man-rdw/tlp-rdw.8	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/man-rdw/tlp-rdw.8	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,24 @@
+.TH tlp-rdw 8 2019-01-16 "TLP 1.2" "Power Management"
+.
+.SH NAME
+tlp-rdw \- disable Radio Device Wizard temporarily (until reboot).
+.
+.SH SYNOPSIS
+.B tlp-rdw \fR[\fIcommand\fR]
+.
+.SH COMMANDS
+.
+.TP
+.B disable
+Disable RDW actions.
+.
+.TP
+.B enable
+Enable RDW actions.
+.
+.TP
+<none>
+Show RDW state.
+.
+.SH AUTHOR
+(c) 2019 Thomas Koch <linrunner at gmx.net>
diff -Nru tlp-1.1/README tlp-1.2.1/README
--- tlp-1.1/README	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/README	1970-01-01 01:00:00.000000000 +0100
@@ -1,52 +0,0 @@
-TLP 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
diff -Nru tlp-1.1/README.md tlp-1.2.1/README.md
--- tlp-1.1/README.md	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/README.md	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,78 @@
+# TLP - Linux Advanced Power Management
+
+TLP saves laptop battery power on Linux without the need to understand every
+technical detail.
+
+TLP comes with a default configuration already optimized for battery life, so
+you may just install and forget it. Nevertheless TLP is highly customizable to
+fulfil your specific requirements.
+
+TLP is a pure command line tool with automated background tasks. It does not
+contain a GUI.
+
+## Features
+### Power profiles
+Depending on the power source (AC or battery) the following settings are applied:
+
+- Kernel laptop mode and dirty buffer params
+- Processor frequency scaling including "turbo boost" / "turbo core"
+- Limit max/min P-state to control power dissipation of the CPU - intel_pstate only
+- HWP energy performance hints
+- Processor performance versus energy savings policy (x86_energy_perf_policy)
+- Disk drive advanced power management level (APM) and spin down timeout
+- AHCI link power management (ALPM) with device blacklist
+- AHCI runtime power management for host controllers and disks (EXPERIMENTAL)
+- PCIe active state power management (PCIe ASPM)
+- Runtime power management for PCIe bus devices
+- Intel GPU frequency limits
+- Radeon graphics power management (KMS and DPM)
+- Wifi power saving mode
+- Enable/disable integrated radio devices (excluding connected devices)
+- Power off optical drive in UltraBay/MediaBay
+- Audio power saving mode
+
+### Additional
+- I/O scheduler (per disk)
+- USB autosuspend with device blacklist/whitelist (input devices excluded automatically)
+- Enable or disable integrated radio devices upon system startup and shutdown
+- Restore radio device state on system startup (from previous shutdown)
+- Radio device wizard: switch radios upon network connect/disconnect and dock/undock
+- Disable Wake On LAN
+- Integrated WWAN and bluetooth state is restored after suspend/hibernate
+- Battery charge thresholds and recalibration - ThinkPads only
+
+## Installation
+TLP packages are available for all major Linux distributions; see
+[Installation](https://linrunner.de/en/tlp/docs/tlp-linux-advanced-power-management.html#installation).
+
+## Configuration
+The default configuration provides optimized power saving out of the box.
+
+Settings are stored in `/etc/default/tlp`;
+see [Configuration](https://linrunner.de/en/tlp/docs/tlp-configuration.html) for
+details.
+
+## Documentation
+Read the the full documentation at the website:
+
+- <https://linrunner.de/tlp>
+
+Or take a look at the manpages:
+
+- tlp (apply settings)
+- tlp-rdw (control the radio device wizard)
+- tlp-stat (display tlp status and active settings)
+- wifi, bluetooth, wwan (switch wireless devices on/off)
+- run-on-ac, run-on-bat
+
+## Support
+Please use adequate Linux forums for help and support questions.
+
+## Bug reports
+Refer to the [Bug Reporting Howto](https://github.com/linrunner/TLP/blob/master/.github/Bug_Reporting_Howto.md).
+
+## Contributing
+Contributing is not only about coding and pull requests. Volunteers helping
+with testing and support are always welcome!
+
+See [Contributing](https://github.com/linrunner/TLP/blob/master/.github/CONTRIBUTING.md).
diff -Nru tlp-1.1/tlp.bash_completion tlp-1.2.1/tlp.bash_completion
--- tlp-1.1/tlp.bash_completion	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.bash_completion	2019-03-18 18:08:25.000000000 +0100
@@ -20,7 +20,7 @@
     local cur prev words cword opts bats
     _init_completion || return
 
-    opts="start ac bat usb bayoff discharge setcharge fullcharge chargeonce recalibrate stat diskid"
+    opts="start ac bat usb bayoff discharge setcharge fullcharge chargeonce recalibrate diskid"
 
     case $cword in
         1)
diff -Nru tlp-1.1/tlp-func-base.in tlp-1.2.1/tlp-func-base.in
--- tlp-1.1/tlp-func-base.in	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/tlp-func-base.in	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,746 @@
+#!/bin/sh
+# tlp - Base Functions
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# shellcheck disable=SC2086
+
+# ----------------------------------------------------------------------------
+# Constants
+
+readonly TLPVER="1.2.1"
+
+readonly CONFFILE=@TLP_CONF@
+readonly RUNDIR=@TLP_RUN@
+
+readonly FLOCK=flock
+readonly HDPARM=hdparm
+readonly LAPMODE=laptop_mode
+readonly LOGGER=logger
+readonly MODPRO=modprobe
+readonly TPACPIBAT=@TLP_TLIB@/tpacpi-bat
+readonly UDEVADM=udevadm
+
+readonly LOCKFILE=$RUNDIR/lock
+readonly LOCKTIMEOUT=2
+
+readonly PWRRUNFILE=$RUNDIR/last_pwr
+readonly MANUALMODEFILE=$RUNDIR/manual_mode
+
+readonly MOD_MSR="msr"
+readonly MOD_TEMP="coretemp"
+readonly MOD_TPSMAPI="tp_smapi"
+readonly MOD_TPACPI="acpi_call"
+
+readonly DMID=/sys/class/dmi/id/
+readonly NETD=/sys/class/net
+
+readonly RE_TPSMAPI_ONLY='^(Edge( 13.*)?|G41|R[56][012][eip]?|R[45]00|SL[45]10|T23|T[346][0123][p]?|T[45][01]0[s]?|W[57]0[01]|X[346][012][s]?( Tablet)?|X1[02]0e|X[23]0[01][s]?( Tablet)?|Z6[01][mpt])$'
+readonly RE_TPSMAPI_AND_TPACPI='^(X1|X220[s]?( Tablet)?|T[45]20[s]?|W520)$'
+readonly RE_TP_NONE='^(L[45]20|SL[345]00|X121e)$'
+
+readonly RE_PARAM='^[A-Z_]+[0-9]*\=[0-9a-zA-Z _-]*$'
+
+# power supplies: ignore MacBook Pro 2017 sbs-charger and hid devices
+readonly RE_PS_IGNORE='sbs-charger|hidpp_battery'
+
+readonly DEBUG_TAGS_ALL="arg bat disk lock nm path pm ps rf run sysfs udev usb"
+
+readonly DEFAULT_TLP_ENABLE=0
+readonly DEFAULT_TLP_PERSISTENT_DEFAULT=0
+
+# ----------------------------------------------------------------------------
+# Control
+
+_nodebug=0
+
+# ----------------------------------------------------------------------------
+# Functions
+
+# --- Debug
+
+echo_debug () { # write debug msg if tag matches -- $1: tag; $2: msg;
+    [ "$_nodebug" = "1" ] && return 0
+
+    if wordinlist "$1" "$TLP_DEBUG"; then
+        $LOGGER -p debug -t "tlp" --id=$$ "$2"
+    fi
+}
+
+# --- Strings
+
+tolower () { # echo string in lowercase -- $1: string
+    printf "%s" "$1" | tr "[:upper:]" "[:lower:]"
+}
+
+toupper () { # echo string in lowercase -- $1: string
+    printf "%s" "$1" | tr "[:lower:]" "[:upper:]"
+}
+
+wordinlist () { # test if word in list
+                # $1: word, $2: whitespace-separated list of words
+    local word
+
+    if [ -n "${1-}" ]; then
+        for word in ${2-}; do
+            [ "${word}" != "${1}" ] || return 0 # exact match
+        done
+    fi
+
+    return 1 # no match
+}
+
+# --- Sysfiles
+
+check_sysfs () { # debug: check if sysfile exists -- $1: routine; $2: sysfs path
+    if wordinlist "sysfs" "$TLP_DEBUG"; then
+        if [ ! -e $2 ]; then
+            $LOGGER -p debug -t "tlp[$$,$PPID]" "$1: $2 nonexistent"
+        fi
+    fi
+}
+
+read_sysf () { # read and echo contents of sysfile
+    # return 1 and echo default if read fails
+    # $1: sysfile, $2: default; rc: 0=ok/1=error
+    if ! cat "$1" 2> /dev/null; then
+        printf "%s" "$2"
+        return 1
+    else
+        return 0
+    fi
+}
+
+readable_sysf () { # check if file is actually readable -- $1: file
+    # rc: 0=readable/1=read error
+    cat "$1" > /dev/null 2>&1
+}
+
+read_sysval () { # read and echo contents of sysfile
+    # echo '0' if file is non-existent, read fails or is non-numeric
+    # $1: sysfile; rc: 0=ok/1=error
+    printf "%d" "$(read_sysf $1)" 2> /dev/null
+}
+
+write_sysf () { # write string to a sysfile
+    # $1: string, $2: sysfile
+    # rc: 0=ok/1=error
+    { printf '%s\n' "$1" > "$2"; } 2> /dev/null
+}
+
+# --- Tests
+
+cmd_exists () { # test if command exists -- $1: command
+    command -v $1 > /dev/null 2>&1
+}
+
+test_root () { # test root privilege -- rc: 0=root, 1=not root
+    [ "$(id -u)" = "0" ]
+}
+
+check_root () { # show error message and 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 --
+    # $1: 1=verbose
+    # rc: 0=disabled/1=enabled
+
+    : ${TLP_ENABLE:=${DEFAULT_TLP_ENABLE}}
+
+    if [ "$TLP_ENABLE" = "1" ]; then
+        return 0
+    else
+        [ "$1" = "1" ] && echo "Error: TLP power save is disabled. Set TLP_ENABLE=1 in $CONFFILE." 1>&2
+        return 1
+    fi
+}
+
+# --- Locking and Semaphores
+
+set_run_flag () { # set flag -- $1: flag name
+                  # rc: 0=success/1,2=failed
+    local rc
+
+    create_rundir
+    touch $RUNDIR/$1; rc=$?
+    echo_debug "lock" "set_run_flag.touch: $1; rc=$rc"
+
+    return $rc
+}
+
+reset_run_flag () { # reset flag -- $1: flag name
+    if rm $RUNDIR/$1 2> /dev/null 1>&2 ; then
+        echo_debug "lock" "reset_run_flag($1).remove"
+    else
+        echo_debug "lock" "reset_run_flag($1).not_found"
+    fi
+
+    return 0
+}
+
+check_run_flag () { # check flag -- $1: flag name
+                    # rc: 0=flag set/1=flag not set
+    local rc
+
+    [ -f $RUNDIR/$1 ]; rc=$?
+    echo_debug "lock" "check_run_flag($1): rc=$rc"
+
+    return $rc
+}
+
+lock_tlp () { # get exclusive lock: blocking with timeout
+              # $1: lock id (default: tlp)
+              # rc: 0=success/1=failed
+
+    create_rundir
+    # open file for writing and attach fd 9
+    # when successful lock fd 9 exclusive and blocking
+    # wait $LOCKTIMEOUT secs to obtain the lock
+    if { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null && $FLOCK -x -w $LOCKTIMEOUT 9 ; then
+        echo_debug "lock" "lock_tlp($1).success"
+        return 0
+    else
+        echo_debug "lock" "lock_tlp($1).failed"
+        return 1
+    fi
+}
+
+lock_tlp_nb () { # get exclusive lock: non-blocking
+                 # $1: lock id (default: tlp)
+                 # rc: 0=success/1=failed
+
+    create_rundir
+    # open file for writing and attach fd 9
+    # when successful lock fd 9 exclusive and non-blocking
+    if { exec 9> ${LOCKFILE}_${1:-tlp} ; } 2> /dev/null && $FLOCK -x -n 9 ; then
+        echo_debug "lock" "lock_tlp_nb($1).success"
+        return 0
+    else
+        echo_debug "lock" "lock_tlp_nb($1).failed"
+        return 1
+    fi
+}
+
+unlock_tlp () { # free exclusive lock
+                # $1: lock id (default: tlp)
+
+    # defer unlock for $X_DEFER_UNLOCK seconds -- debugging only
+    [ -n "$X_DEFER_UNLOCK" ] && sleep $X_DEFER_UNLOCK
+
+    # free fd 9 and scrap lockfile
+    { exec 9>&- ; } 2> /dev/null
+    rm -f ${LOCKFILE}_${1:-tlp}
+    echo_debug "lock" "unlock_tlp($1)"
+
+    return 0
+}
+
+lockpeek_tlp () { # check for pending lock (by looking for the lockfile)
+                  # $1: lock id (default: tlp)
+    if [ -f ${LOCKFILE}_${1:-tlp} ]; then
+        echo_debug "lock" "lockpeek_tlp($1).locked"
+        return 0
+    else
+        echo_debug "lock" "lockpeek_tlp($1).not_locked"
+        return 1
+    fi
+}
+
+echo_tlp_locked () { # print "locked" message
+    echo "Error: TLP is locked by another operation." 1>&2
+    return 0
+}
+
+set_timed_lock () { # create timestamp n seconds in the future
+    # $1: lock id, $2: lock duration [s]
+    local lock rc time
+
+    lock=${1}_timed_lock_$(date +%s -d "+${2} seconds")
+    set_run_flag $lock; rc=$?
+    echo_debug "lock" "set_timed_lock($1, $2): $lock; rc=$rc"
+
+    # cleanup obsolete locks
+    time=$(date +%s)
+    for lockfile in $RUNDIR/${1}_timed_lock_*; do
+        if [ -f "$lockfile" ]; then
+            locktime=${lockfile#${RUNDIR}/${1}_timed_lock_}
+            if [ $time -ge $locktime ]; then
+                rm -f $lockfile
+                echo_debug "lock" "set_timed_lock($1, $2).remove_obsolete: ${lockfile#${RUNDIR}/}"
+            fi
+        fi
+    done
+
+    return $rc
+}
+
+check_timed_lock () { # check if active timestamp exists
+    # $1: lock id; rc: 0=locked/1=not locked
+    local lockfile locktime time
+
+    time=$(date +%s)
+    for lockfile in $RUNDIR/${1}_timed_lock_*; do
+        if [ -f "$lockfile" ]; then
+            locktime=${lockfile#${RUNDIR}/${1}_timed_lock_}
+            if [ $time -lt $(( locktime - 120 )) ]; then
+                # timestamp is more than 120 secs in the future,
+                # something weird has happened -> remove it
+                rm -f $lockfile
+                echo_debug "lock" "check_timed_lock($1).remove_invalid: ${lockfile#${RUNDIR}/}"
+            elif [ $time -lt $locktime ]; then
+                # timestamp in the future -> we're locked
+                echo_debug "lock" "check_timed_lock($1).locked: $time, $locktime"
+                return 0
+            else
+                # obsolete timestamp -> remove it
+                rm -f $lockfile
+                echo_debug "lock" "check_timed_lock($1).remove_obsolete: ${lockfile#${RUNDIR}/}"
+            fi
+        fi
+    done
+
+    echo_debug "lock" "check_timed_lock($1).not_locked: $time"
+    return 1
+}
+
+# --- PATH
+add_sbin2path () { # check if /sbin /usr/sbin in $PATH, otherwise add them
+                   # retval: $PATH, $_oldpath, $_addpath
+    local sp
+
+    # shellcheck disable=SC2034
+    _oldpath="$PATH"
+    _addpath=""
+
+    for sp in /usr/sbin /sbin; do
+        if [ -d $sp ] && [ ! -h $sp ]; then
+            # dir exists and is not a symlink
+            case ":$PATH:" in
+                *":$sp:"*) # $sp already in $PATH
+                    ;;
+
+                *) # $sp not in $PATH, add it
+                    _addpath="$_addpath:$sp"
+                    ;;
+            esac
+        fi
+    done
+
+    if [ -n "$_addpath" ]; then
+      export PATH="${PATH}${_addpath}"
+    fi
+
+    return 0
+}
+
+create_rundir () { # make sure $RUNDIR exists
+    [ -d $RUNDIR ] || mkdir -p $RUNDIR 2> /dev/null 1>&2
+}
+
+# --- Configuration
+
+read_defaults () { # read config file
+    if [ -f $CONFFILE ]; then
+        # shellcheck disable=SC1090
+        . $CONFFILE
+        return 0
+    else
+        return 1
+    fi
+}
+
+parse_args4config () { # parse command-line arguments: everything after the
+                       # delimiter '--' is interpreted as a config parameter
+    # retval: config parameters
+    local dflag=0 param value
+
+    echo_debug "arg" "parse_args4config: ${0##/*/} $*"
+    # iterate arguments
+    while [ $# -gt 0 ]; do
+        if [ $dflag -eq 1 ]; then
+            # delimiter was passed --> sanitize and parse argument:
+            #   format is PARAMETER=value (quotes stripped by the shell calling tlp)
+            #   parameter name allows 'A'..'Z' and '_' only, may end in a number (_BAT0)
+            #   parameter value allows  'A'..'Z', 'a'..'z', '0'..'9', ' ', '_', '-'
+            if printf "%s" "$1" | grep -E -q "$RE_PARAM"; then
+                param="${1%%=*}"
+                value="${1#*=}"
+                if [ -n "$param" ] && [ -n "$value" ]; then
+                    echo_debug "arg" "parse_args4config.param: $param=$value"
+                    eval "$param='$value'" 2> /dev/null
+                fi
+            fi
+        elif [ "$1" = "--" ]; then
+            # delimiter reached --> begin interpretation
+            dflag=1
+        fi
+        shift # next argument
+    done # while arguments
+
+    return 0
+}
+
+# --- 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
+    # shellcheck disable=SC2048
+    for mod in $*; do
+        $MODPRO $mod > /dev/null 2>&1
+    done
+
+    return 0
+}
+
+# --- DMI
+
+read_dmi () { # read DMI data -- $*: keywords; stdout: dmi strings
+    local ds key outr
+
+    outr=""
+    # shellcheck disable=SC2048
+    for key in $*; do
+        ds="$(read_sysf ${DMID}/$key | \
+                grep -E -v -i 'not available|to be filled|DMI table is broken')"
+        if [ -n "$outr" ]; then
+            [ -n "$ds" ] && outr="$outr $ds"
+        else
+            outr="$ds"
+        fi
+    done
+
+    printf '%s' "$outr"
+    return 0
+}
+
+# --- ThinkPad Checks
+
+supports_tpsmapi_only () {
+    # rc: 0=ThinkPad supports tpsmapi only/1=false
+    # prerequisite: check_thinkpad()
+    printf '%s' "$_tpmodel" | grep -E -q "${RE_TPSMAPI_ONLY}"
+}
+
+supports_tpsmapi_and_tpacpi () {
+    # rc: 0=ThinkPad supports tpsmapi, tpacpi-bat, natacpi/1=false
+    # prerequisite: check_thinkpad()
+    printf '%s' "$_tpmodel" | grep -E -q "${RE_TPSMAPI_AND_TPACPI}"
+}
+
+supports_no_tp_bat_funcs () {
+    # rc: 0=ThinkPad doesn't support battery features/1=false
+    # prerequisite: check_thinkpad()
+    printf '%s' "$_tpmodel" | grep -E -q "${RE_TP_NONE}"
+}
+
+supports_tpacpi () {
+    # rc: 0=ThinkPad does support tpacpi-bat, natacpi/1=false
+    # prerequisite: check_thinkpad()
+    # assumption: all newer models support tpapaci-bat/natacapi except
+    # explicitly unsupported or older tpsmapi only models
+    ! supports_no_tp_bat_funcs && ! supports_tpsmapi_only
+}
+
+check_thinkpad () { # check for ThinkPad hardware and save model string,
+                 # load ThinkPad specific kernel modules
+                 # rc: 0=ThinkPad, 1=other hardware
+                 # retval: $_tpmodel
+    local pv
+
+    _tpmodel=""
+
+    if [ -d $TPACPIDIR ]; then
+        # kernel module thinkpad_acpi is loaded
+
+        if [ -z "$X_SIMULATE_MODEL" ]; then
+            # get DMI product string and sanitize it
+            pv="$(read_dmi product_version | tr -C -d 'a-zA-Z0-9 ')"
+        else
+            # simulate arbitrary model
+            pv="$X_SIMULATE_MODEL"
+        fi
+
+        # check DMI product string for occurrence of "ThinkPad"
+        if printf '%s' "$pv" | grep -E -q 'Think[Pp]ad'; then
+            # it's a real ThinkPad --> save model substring
+            _tpmodel=$(echo $pv | sed -r 's/^Think[Pp]ad //')
+        fi
+    fi
+
+    if [ -n "$_tpmodel" ]; then
+        # load tp-smapi for supported models only; prevents kernel messages
+        if supports_tpsmapi_only || supports_tpsmapi_and_tpacpi; then
+            load_modules $MOD_TPSMAPI
+        fi
+
+        # load acpi-call for supported models only; prevents kernel messages
+        if supports_tpacpi; then
+            load_modules $MOD_TPACPI
+        fi
+
+        echo_debug "bat" "check_thinkpad: tpmodel=$_tpmodel"
+        return 0
+    fi
+
+    # not a ThinkPad
+    echo_debug "bat" "check_thinkpad.not_a_thinkpad: model=$pv"
+    return 1
+}
+
+is_thinkpad () { # check for ThinkPad by saved model string
+                 # rc: 0=ThinkPad, 1=other hardware
+    [ -n "$_tpmodel" ]
+}
+
+# --- Power Source
+
+get_sys_power_supply () { # get current power supply
+                          # retval/rc: $_syspwr (0=ac, 1=battery, 2=unknown)
+
+    # _syspwr is determined as follows:
+    #
+    #             AC online:  | none | 1    | 0    |
+    #           +------------ +------+------+------+
+    # battery   | none        | 2    | 0    | 1    |
+    # status:   | discharging | 1    | 0    | 1    |
+    #           | idle        | 0    | 0    | 1    |
+    #
+    # Note: existing AC online status has precendence over any battery status
+
+    local psrc
+    _syspwr=
+
+    for psrc in /sys/class/power_supply/*; do
+        # -f $psrc/type not necessary - read_sysf() handles this
+
+        # ignore atypical power supplies
+        echo "$psrc" | grep -E -q "$RE_PS_IGNORE" && continue
+
+        case "$(read_sysf $psrc/type)" in
+            Mains|USB)
+                # AC detected
+                # skip device to simulate broken AC detection
+                [ "$X_SIMULATE_AC_QUIRK" = "1" ] && continue
+
+                # check if online
+                if [ "$(read_sysf $psrc/online)" = "1" ]; then
+                    # AC online
+                    _syspwr=0
+                    echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).ac_online: syspwr=$_syspwr"
+                else
+                    # AC offline means battery
+                    _syspwr=1
+                    echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).ac_offline: syspwr=$_syspwr"
+                fi
+                break # AC found --> end search
+                ;;
+
+            Battery)
+                # battery detected
+                # --> inspect unless discharging battery has been found beforehand
+                [ "$_syspwr" = "1" ] && continue
+
+                case "$(read_sysf $psrc/status)" in
+                    Discharging)
+                        if ! lockpeek_tlp tlp_discharge; then
+                            # discharging means battery ...
+                            _syspwr=1
+                            echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).bat_discharging: syspwr=$_syspwr"
+                        else
+                            # ... unless forced discharge is in progress,
+                            # which means AC and end of iteration
+                            _syspwr=0
+                            echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).forced_discharge: syspwr=$_syspwr"
+                            break
+                        fi
+                        ;;
+
+                    *) # everything else including "Unknown" could mean AC,
+                       # so re-check after a short delay to be sure to catch
+                       # lagging battery status updates
+                        sleep 0.5
+                        if [ "$(read_sysf $psrc/status)" = "Discharging" ]; then
+                            _syspwr=1
+                            echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).bat_discharging2: syspwr=$_syspwr"
+                        else
+                            _syspwr=0
+                            echo_debug "ps" "get_sys_power_supply(${psrc##/*/}).bat_idle: syspwr=$_syspwr"
+                        fi
+                        ;;
+                esac
+                # battery found: continue looking for AC and more batteries
+                ;;
+
+            *) # unknown
+                ;;
+        esac
+    done
+
+    # set _syspwr to unknown if we haven't seen any AC/battery power source so far
+    if [ -z "$_syspwr" ]; then
+        _syspwr=2
+        echo_debug "ps" "get_sys_power_supply.unknown: syspwr=$_syspwr"
+    fi
+
+    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=$?
+
+    : ${TLP_DEFAULT_MODE:=}
+    : ${TLP_PERSISTENT_DEFAULT:=${DEFAULT_TLP_PERSISTENT_DEFAULT}}
+
+    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
+
+    # intercept invalid states
+    case $1 in
+        0|1) ;; # valid state
+        *) # invalid new state --> return "different"
+            echo_debug "pm" "compare_and_save_power_state($1).invalid"
+            return 0
+            ;;
+    esac
+
+    # read saved state
+    lp=$(read_sysf $PWRRUNFILE)
+
+    # compare
+    if [ -z "$lp" ] || [ "$lp" != "$1" ]; then
+        # saved state is nonexistent/empty or is different --> save new state
+        create_rundir
+        write_sysf "$1" $PWRRUNFILE
+        echo_debug "pm" "compare_and_save_power_state($1).different: old=$lp"
+        return 0
+    else
+        # touch file for last run
+        touch $PWRRUNFILE
+        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
+}
+
+check_ac_power () { # check if ac power connected -- $1: function
+
+    if ! get_sys_power_supply ; then
+        echo_debug "bat" "check_ac_power($1).no_ac_power"
+        echo "Error: $1 is possible on AC power only." 1>&2
+        return 1
+    fi
+
+    return 0
+}
+
+echo_started_mode () { # print operation mode -- $1: 0=ac mode, 1=battery mode
+    if [ "$1" = "0" ]; then
+        echo "TLP started in AC mode."
+    else
+        echo "TLP started in battery mode."
+    fi
+
+    return 0
+}
+
+set_manual_mode () { # set manual operation mode
+    # $1: 0=ac mode, 1=battery mode
+    # retval: $_manual_mode (0=ac, 1=battery, none=not found)
+
+    create_rundir
+    write_sysf "$1" $MANUALMODEFILE
+    _manual_mode="$1"
+
+    echo_debug "pm" "set_manual_mode($1)"
+    return 0
+}
+
+clear_manual_mode () { # remove manual operation mode
+    # retval: $_manual_mode (none)
+
+    rm -f $MANUALMODEFILE 2> /dev/null
+    _manual_mode="none"
+
+    echo_debug "pm" "clear_manual_mode"
+    return 0
+}
+
+get_manual_mode () { # get manual operation mode
+    # rc: 0=ok/1=not found
+    # retval: $_manual_mode (0=ac, 1=battery, none=not found)
+    local rc=1
+    _manual_mode="none"
+
+    if [ -f $MANUALMODEFILE ]; then
+        # read mode file
+        _manual_mode=$(read_sysf $MANUALMODEFILE)
+        case $_manual_mode in
+            0|1) rc=0 ;;
+            *) _manual_mode="none" ;;
+        esac
+    fi
+    return $rc
+}
+
+# --- Misc Checks
+
+check_laptop_mode_tools () { # check if lmt installed -- rc: 0=not installed, 1=installed
+    if cmd_exists $LAPMODE; then
+        echo 1>&2
+        echo "***Warning: laptop-mode-tools detected, this may cause conflicts with TLP." 1>&2
+        echo "            Please uninstall laptop-mode-tools." 1>&2
+        echo 1>&2
+        echo_debug "pm" "check_laptop_mode_tools: yes"
+        return 1
+    else
+        return 0
+    fi
+}
+
diff -Nru tlp-1.1/tlp-functions.in tlp-1.2.1/tlp-functions.in
--- tlp-1.1/tlp-functions.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-functions.in	1970-01-01 01:00:00.000000000 +0100
@@ -1,2863 +0,0 @@
-#!/bin/sh
-# tlp - power management functions
-#
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
-# 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 <iface>:<number> 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
-}
diff -Nru tlp-1.1/tlp.in tlp-1.2.1/tlp.in
--- tlp-1.1/tlp.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,23 +1,25 @@
 #!/bin/sh
 # tlp - adjust power settings
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
-# --- Constants
-readonly LIBDIR="@TLP_TLIB@"
-readonly LIBS="tlp-functions tlp-rf-func"
+# shellcheck disable=SC2086
 
 # --- 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
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/[0-9][0-9]*; do
+    # shellcheck disable=SC1090
+    . $lib || exit 70
 done
 
+# --- Constants
+
+readonly DEFAULT_USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN=0
+readonly DEFAULT_RESTORE_THRESHOLDS_ON_BAT=0
+
 # --- Subroutines
+
 apply_common_settings () { # apply settings common to all modes
                            # $1: 0=ac mode, 1=battery mode
     set_laptopmode $1
@@ -33,10 +35,11 @@
     set_energy_perf_policy $1
     set_disk_apm_level $1
     set_disk_spindown_timeout $1
-    set_disk_io_sched
+    set_disk_iosched
     set_sata_link_power $1
     set_ahci_runtime_pm $1
     set_pcie_aspm $1
+    set_intel_gpu_min_max_boost_freq $1
     set_radeon_profile $1
     set_wifi_power_mode $1
     disable_wake_on_lan
@@ -46,53 +49,203 @@
     return 0
 }
 
+show_usage () {
+    echo "Usage: tlp start|true|bat|false|ac|usb|bayoff|chargeonce|discharge|setcharge|fullcharge|recalibrate|diskid" 1>&2
+}
+
+parse_args () { # parse command-line arguments
+    # $@:       arguments to parse
+    # retval:   $_cmd:  command;
+    #           $_cmd2: subcommand;
+    #           $_carg1,
+    #           $_carg2,
+    #           $_carg3: command arguments
+
+    # parsing control: 'nil' means that the element is still expected
+    _cmd="nil"
+    _cmd2="nil"
+    _carg1="nil"
+    _carg2="nil"
+    _carg3="nil"
+
+    # iterate arguments until exhausted or delimiter '--' reached
+    while [ $# -gt 0 ]; do
+        if [ "$1" = "--" ]; then
+            break;
+
+        elif [ "$_cmd" = "nil" ]; then
+            # command
+            case $1 in
+                ac|auto|bat|bayoff|false|diskid|resume|suspend|start|true|usb)
+                    # commands without further arguments
+                    _cmd="$1"
+                    _cmd2=""
+                    _carg1=""
+                    _carg2=""
+                    _carg3=""
+                    ;;
+
+                chargeonce|discharge|fullcharge|recalibrate)
+                    # commands with one or no arguments
+                    _cmd="$1"
+                    _cmd2=""
+                    _carg2=""
+                    _carg3=""
+                    ;;
+
+                setcharge)
+                    # command with up to three arguments
+                    _cmd="$1"
+                    _cmd2=""
+                    ;;
+
+                init)
+                    # command with subcommand and no arguments
+                    _cmd="$1"
+                    _carg1=""
+                    _carg2=""
+                    _carg3=""
+                    ;;
+
+                stat)
+                    # unsupported command
+                    echo "Error: 'tlp stat' no longer supported, use 'tlp-stat' instead."  1>&2
+                    exit 3
+                    ;;
+
+                noop)
+                    # no operation
+                    _cmd="$1"
+                    _cmd2=""
+                    ;;
+
+                *)
+                    # unknown command
+                    echo "Error: unknown command \"$1\"."  1>&2
+                    show_usage
+                    exit 3
+                    ;;
+            esac
+
+        elif [ "$_cmd2" = "nil" ]; then
+            # subcommand
+            case $1 in
+                start|stop|restart|force-reload)
+                    _cmd2="$1"
+                    ;;
+
+                *) # unknown subcommand
+                    echo "Usage: tlp init {start|stop|restart|force-reload}" >&2
+                    exit 3
+                    ;;
+            esac
+
+        elif [ "$_carg1" = "nil" ]; then
+            # first command argument
+            _carg1="$1"
+
+        elif [ "$_carg2" = "nil" ]; then
+            # second command argument
+            _carg2="$1"
+
+        elif [ "$_carg3" = "nil" ]; then
+            # third command argument
+            _carg3="$1"
+
+        fi
+
+        shift # next argument
+    done # while arguments
+
+    if  [ "$_cmd" = "nil" ]; then
+        # no command parsed
+        show_usage
+        exit 3
+    fi
+
+    # clear missing arguments
+    [ "$_carg1" = "nil" ] && _carg1=""
+    [ "$_carg2" = "nil" ] && _carg2=""
+    [ "$_carg3" = "nil" ] && _carg3=""
+
+    return 0
+}
+
 # --- MAIN
+
 read_defaults
-check_tlp_enabled || exit 1
+parse_args "$@"
+parse_args4config "$@"
+check_tlp_enabled 1 || 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=$?
+get_manual_mode
 
-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]"
+if [ -z "$_cmd2" ]; then
+    echo_debug "run" "+++ $_cmd ($TLPVER) ++++++++++++++++++++++++++++++++++++++++"
 else
-    echo_debug "path" "PATH=$oldpath"
+    echo_debug "run" "+++ $_cmd $_cmd2 ($TLPVER) ++++++++++++++++++++++++++++++++++++++++"
 fi
 
-if [ $pwrmode -eq 1 ]; then
-    echo_debug "run" "power_mode=bat"
+# shellcheck disable=SC2154
+if [ -n "$_addpath" ]; then
+    # shellcheck disable=SC2154
+    echo_debug "path" "PATH=$_oldpath[$_addpath]"
 else
-    echo_debug "run" "power_mode=ac"
+    # shellcheck disable=SC2154
+    echo_debug "path" "PATH=$_oldpath"
 fi
 
-case "$syspwr" in
+# determine new power state
+case "$_cmd" in
+    init|start)
+        # discard manual mode
+        clear_manual_mode
+        ;;
+
+    auto|resume)
+        # if manual mode is set, use instead of current power state
+        # shellcheck disable=SC2154
+        [ "$_manual_mode" != "none" ] && pwrmode=$_manual_mode
+        ;;
+
+    true|bat)
+        pwrmode=1
+        set_manual_mode 1
+        ;;
+
+    false|ac)
+        pwrmode=0
+        set_manual_mode 0
+        ;;
+esac
+
+# shellcheck disable=SC2154
+case "$_syspwr" in
     0) echo_debug "run" "power_source=ac" ;;
     1) echo_debug "run" "power_source=bat" ;;
-    *) echo_debug "run" "power_source=unknown" ;;
+    *) echo_debug "run" "power_source=unknown ($_syspwr)" ;;
+esac
+
+case "$_manual_mode" in
+    0) echo_debug "run" "manual_mode=ac" ;;
+    1) echo_debug "run" "manual_mode=bat" ;;
+    *) echo_debug "run" "manual_mode=none" ;;
+esac
+
+case "$pwrmode" in
+    0) echo_debug "run" "power_mode=ac" ;;
+    1) echo_debug "run" "power_mode=bat" ;;
+    *) echo_debug "run" "power_mode=unknown ($pwrmode)" ;;
 esac
 
 exitcode=0
 
-case "$mode" in
+case "$_cmd" in
     init) # system initialization/shutdown: sysv, upstart, systemd, ...
         check_root
 
@@ -105,7 +258,7 @@
         fi
 
         # do init business ...
-        case $mode2 in
+        case $_cmd2 in
             start)
                 # apply power save settings
                 compare_and_save_power_state $pwrmode
@@ -153,7 +306,7 @@
                 echo "done."
 
                 # disable usb autosuspend if configured
-                if [ "$USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN" = "1" ]; then
+                if [ "${USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN:-${DEFAULT_USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN}}" = "1" ]; then
                     echo -n "Disabling usb autosuspend..."
                     set_usb_suspend 0 on
                     echo "done."
@@ -178,17 +331,17 @@
           # 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
+                if [ "${RESTORE_THRESHOLDS_ON_BAT:-${DEFAULT_RESTORE_THRESHOLDS_ON_BAT}}" = "1" ] \
+                    && [ "$pwrmode" = "1" ]; then
                     set_charge_thresholds
                 fi
             fi
             unlock_tlp
-        else
-            exitcode=4
         fi
         ;;
 
@@ -206,7 +359,6 @@
             echo_started_mode $pwrmode
         else
             echo_tlp_locked
-            exitcode=4
         fi
         ;;
 
@@ -223,7 +375,6 @@
             echo_started_mode 1
         else
             echo_tlp_locked
-            exitcode=4
         fi
         ;;
 
@@ -240,7 +391,6 @@
             echo_started_mode 0
         else
             echo_tlp_locked
-            exitcode=4
         fi
         ;;
 
@@ -252,8 +402,6 @@
             apply_common_settings 0
             suspend_drivebay $pwrmode
             unlock_tlp
-        else
-            exitcode=4
         fi
         ;;
 
@@ -266,8 +414,6 @@
             apply_common_settings $pwrmode
             resume_drivebay $pwrmode
             unlock_tlp
-        else
-            exitcode=4
         fi
         ;;
 
@@ -283,25 +429,31 @@
 
     setcharge) # set charge thresholds (temporarily)
         check_root
-        setcharge_battery $2 $3 $4
+        setcharge_battery $_carg1 $_carg2 $_carg3
         exitcode=$?
         ;;
 
-    fullcharge) # charge battery up to 100% (temporarily)
+    fullcharge) # charge battery to 100% (temporarily)
         if check_ac_power fullcharge; then
             check_root
-            setcharge_battery 96 100 $2
+            setcharge_battery 96 100 $_carg1
             exitcode=$?
+            if [ $exitcode -eq 0 ]; then
+                echo "Charging starts now, keep AC connected."
+            fi
         else
             exitcode=2
         fi
         ;;
 
-    chargeonce) # charge battery to upper threshold once
+    chargeonce) # charge battery to stop threshold once
         if check_ac_power chargeonce; then
             check_root
-            chargeonce_battery $2
+            chargeonce_battery $_carg1
             exitcode=$?
+            if [ $exitcode -eq 0 ]; then
+                echo "Charging starts now, keep AC connected."
+            fi
         else
             exitcode=2
         fi
@@ -310,8 +462,13 @@
     discharge) # discharge battery completely (to recalibrate)
         if check_ac_power discharge; then
             check_root
-            discharge_battery $2
-            exitcode=$?
+            if lock_tlp_nb tlp_discharge; then
+                discharge_battery $_carg1
+                exitcode=$?
+                unlock_tlp tlp_discharge
+            else
+                echo_discharge_locked
+            fi
         else
             exitcode=2
         fi
@@ -320,38 +477,32 @@
     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."
+            if lock_tlp_nb tlp_discharge; then
+                setcharge_battery 96 100 $_carg1
+                sleep 1
+                discharge_battery $_carg1
+                exitcode=$?
+                if [ $exitcode -eq 0 ]; then
+                    echo "Charging starts now, for a complete recalibration"
+                    echo "keep AC connected until the battery is fully charged."
+                fi
+                unlock_tlp tlp_discharge
+            else
+                echo_discharge_locked
             fi
         else
             exitcode=2
         fi
         ;;
 
-    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
+    diskid) # show disk id's
+        show_disk_ids
         ;;
 
-    *)
-        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
+    noop) # Debug: no operation
+        check_root
+        battery_present DEF
+        echo "Debug: no operation performed."
         ;;
 esac
 
diff -Nru tlp-1.1/tlp.init tlp-1.2.1/tlp.init
--- tlp-1.1/tlp.init	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.init	2019-03-18 18:08:25.000000000 +0100
@@ -2,7 +2,7 @@
 
 # tlp - system startup/shutdown
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 #
 # chkconfig: 2345 98 01
diff -Nru tlp-1.1/tlp-nop.in tlp-1.2.1/tlp-nop.in
--- tlp-1.1/tlp-nop.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-nop.in	1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-#!/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
diff -Nru tlp-1.1/tlp-pcilist tlp-1.2.1/tlp-pcilist
--- tlp-1.1/tlp-pcilist	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-pcilist	2019-03-18 18:08:25.000000000 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # tlp-pcilist - list pci devices with runtime pm mode and device class
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 package tlp_pcilist;
diff -Nru tlp-1.1/tlp-rdw.bash_completion tlp-1.2.1/tlp-rdw.bash_completion
--- tlp-1.1/tlp-rdw.bash_completion	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/tlp-rdw.bash_completion	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,14 @@
+# bash completion for TLP-RDW
+
+_tlp_rdw() {
+    local cur prev words cword opts
+    _init_completion || return
+
+    opts="enable disable"
+
+    if [ $cword -eq 1 ]; then
+        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+        return 0
+    fi
+} &&
+complete -F _tlp_rdw tlp-rdw
diff -Nru tlp-1.1/tlp-rdw.in tlp-1.2.1/tlp-rdw.in
--- tlp-1.1/tlp-rdw.in	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/tlp-rdw.in	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,44 @@
+#!/bin/sh
+# tlp-rdw - enable/disable RDW
+#
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
+# This software is licensed under the GPL v2 or later.
+
+# shellcheck disable=SC2086
+
+# --- Source libraries
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do
+    # shellcheck disable=SC1090
+    . $lib || exit 70
+done
+
+# --- MAIN
+
+read_defaults
+carg1=$1
+parse_args4config "$@"
+
+case $carg1 in
+    enable)
+        check_root
+        reset_run_flag $RDW_KILL
+        echo "tlp-rdw: enabled."
+        ;;
+
+    disable)
+        check_root
+        set_run_flag $RDW_KILL
+        echo "tlp-rdw: disabled."
+        ;;
+
+    *)
+        if check_run_flag $RDW_KILL; then
+            echo "tlp-rdw: disabled."
+        else
+            echo "tlp-rdw: enabled."
+        fi
+        ;;
+esac
+
+exit 0
diff -Nru tlp-1.1/tlp-rdw-nm.in tlp-1.2.1/tlp-rdw-nm.in
--- tlp-1.1/tlp-rdw-nm.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-rdw-nm.in	2019-03-18 18:08:25.000000000 +0100
@@ -2,20 +2,16 @@
 # tlp-rdw - network manager dispatcher hook:
 #           enable/disable radios on ifup/ifdown
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
-# --- Constants
-readonly LIBDIR="@TLP_TLIB@"
-readonly LIBS="tlp-functions tlp-rf-func"
+# shellcheck disable=SC2086
 
 # --- 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
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do
+    # shellcheck disable=SC1090
+    . $lib || exit 70
 done
 
 # --- Functions
@@ -52,14 +48,16 @@
     # retval: $itype
     local rc
 
-    itype=$(cat $RUNDIR/${1}.itype 2> /dev/null); rc=$?
+    itype=$(read_sysf $RUNDIR/${1}.itype); rc=$?
     rm -f $RUNDIR/${1}.itype
     return $rc
 }
 
 # --- MAIN
+
 read_defaults
-check_tlp_enabled || exit 0
+check_tlp_enabled 0 || exit 0
+check_run_flag $RDW_KILL && exit 0
 add_sbin2path
 
 # Get args
@@ -96,10 +94,13 @@
 fi
 
 echo_debug "nm" "+++ rdw_nm($iface).$action"
-if [ -n "$addpath" ]; then
-    echo_debug "path" "PATH=$oldpath[$addpath]"
+# shellcheck disable=SC2154
+if [ -n "$_addpath" ]; then
+    # shellcheck disable=SC2154
+    echo_debug "path" "PATH=$_oldpath[$_addpath]"
 else
-    echo_debug "path" "PATH=$oldpath"
+    # shellcheck disable=SC2154
+    echo_debug "path" "PATH=$_oldpath"
 fi
 
 # Determine interface type
@@ -117,7 +118,8 @@
         # different devices for control and the actual network connection
         # --> check if interface matches a WWAN device
         get_wwan_ifaces
-        if wordinlist "$iface" "$wanifaces"; then
+        # shellcheck disable=SC2154
+        if wordinlist "$iface" "$_wanifaces"; then
             itype="wwan"
         else
             # fallback:
diff -Nru tlp-1.1/tlp-rdw.rules.in tlp-1.2.1/tlp-rdw.rules.in
--- tlp-1.1/tlp-rdw.rules.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-rdw.rules.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,6 +1,6 @@
 # tlp-rdw - udev rules
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 # --- Dock/undock events
@@ -17,9 +17,6 @@
 # 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"
@@ -29,3 +26,6 @@
 
 # ThinkPad OneLink Dock Plus
 ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/3054/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock"
+
+# ThinkPad Pro Dock (CS18)
+ACTION=="add|remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="17ef/306f/*", RUN+="@TLP_ULIB@/tlp-rdw-udev %p usb_dock"
diff -Nru tlp-1.1/tlp-rdw-udev.in tlp-1.2.1/tlp-rdw-udev.in
--- tlp-1.1/tlp-rdw-udev.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-rdw-udev.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,25 +1,23 @@
 #!/bin/sh
 # tlp-rdw - handle dock/undock events
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
-# --- Constants
-readonly LIBDIR="@TLP_TLIB@"
-readonly LIBS="tlp-functions tlp-rf-func"
+# shellcheck disable=SC2086
 
 # --- 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
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do
+        # shellcheck disable=SC1090
+    . $lib || exit 70
 done
 
 # --- MAIN
+
 read_defaults
-check_tlp_enabled || exit 0
+check_tlp_enabled 0 || exit 0
+check_run_flag $RDW_KILL && exit 0
 add_sbin2path
 
 # get power source
@@ -32,22 +30,24 @@
 case $devtype in
     dock)
         # check if type is "dock_station", exit if not
-        type=$(cat $ddev/type 2> /dev/null)
+        type=$(read_sysf $ddev/type)
         [ "$type" = "dock_station" ] || exit 0
 
-        docked=$(cat $ddev/docked 2> /dev/null)
+        docked=$(read_sysf $ddev/docked)
         action=$EVENT
 
-        echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev type=$type docked=$docked syspwr=$syspwr"
+        # shellcheck disable=SC2154
+        echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev type=$type docked=$docked syspwr=$_syspwr"
         ;;
 
     usb_dock)
+        # shellcheck disable=SC2153
         case $ACTION in
             add)    action="dock"  ;;
             remove) action="undock"  ;;
         esac
 
-        echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev syspwr=$syspwr"
+        echo_debug "udev" "+++ rdw_udev($devtype).$action dev=$ddev syspwr=$_syspwr"
         ;;
 
     *) exit 0 ;; # unknown device type
diff -Nru tlp-1.1/tlp-rf-func tlp-1.2.1/tlp-rf-func
--- tlp-1.1/tlp-rf-func	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-rf-func	1970-01-01 01:00:00.000000000 +0100
@@ -1,489 +0,0 @@
-#!/bin/sh
-# tlp - rf switch functions
-#
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
-# 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
-}
diff -Nru tlp-1.1/tlp-rf.in tlp-1.2.1/tlp-rf.in
--- tlp-1.1/tlp-rf.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-rf.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,48 +1,47 @@
 #!/bin/sh
 # tlp - switch bluetooth/wifi/wwan on/off
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
-# --- Constants
-readonly LIBDIR="@TLP_TLIB@"
-readonly LIBS="tlp-functions tlp-rf-func"
+# shellcheck disable=SC2086,SC2154
 
 # --- 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
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/25-tlp-func-rf @TLP_FLIB@/30-tlp-func-rf-sw; do
+    # shellcheck disable=SC1090
+    . $lib
 done
 
 # --- MAIN
+
 read_defaults
+carg1=$1
+parse_args4config "$@"
 add_sbin2path
 self=${0##*/}
 
 case $self in
     bluetooth|wifi|wwan)
-        case $1 in
+        case $carg1 in
             on)
                 device_switch $self on
-                echo_device_state $self $devs
+                echo_device_state $self $_devs
                 ;;
 
             off)
                 device_switch $self off
-                echo_device_state $self $devs
+                echo_device_state $self $_devs
                 ;;
 
             toggle)
                 device_switch $self toggle
-                echo_device_state $self $devs
+                echo_device_state $self $_devs
                 ;;
 
             *)
                 device_state $self
-                echo_device_state $self $devs
+                echo_device_state $self $_devs
                 ;;
         esac
         ;;
diff -Nru tlp-1.1/tlp.rules.in tlp-1.2.1/tlp.rules.in
--- tlp-1.1/tlp.rules.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.rules.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,6 +1,6 @@
 # tlp - udev rules
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 # handle change of power source ac/bat
diff -Nru tlp-1.1/tlp-run-on.in tlp-1.2.1/tlp-run-on.in
--- tlp-1.1/tlp-run-on.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-run-on.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,20 +1,14 @@
 #!/bin/sh
 # tlp - run commands depending on power source
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # 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
+
+for lib in @TLP_TLIB@/tlp-func-base; do
+    # shellcheck disable=SC1090
+    . $lib
 done
 
 # --- MAIN
@@ -25,7 +19,7 @@
     echo "Usage: $self command [arg(s)]" 1>&2
     exit 1
 fi
-if ! cmd_exists $cmd; then
+if ! cmd_exists "$cmd"; then
     echo "Error: \"$cmd\" not found." 1>&2
     exit 2
 fi
@@ -34,13 +28,13 @@
 case $self in
     run-on-ac)
         if get_power_state; then
-            $cmd $@
+            $cmd "$@"
         fi
         ;;
 
     run-on-bat)
         if ! get_power_state; then
-            $cmd $@
+            $cmd "$@"
         fi
         ;;
 
diff -Nru tlp-1.1/tlp.service.in tlp-1.2.1/tlp.service.in
--- tlp-1.1/tlp.service.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.service.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,14 +1,13 @@
 # tlp - systemd startup/shutdown service
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # 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
+Documentation=https://linrunner.de/tlp
 
 [Service]
 Type=oneshot
diff -Nru tlp-1.1/tlp-sleep.elogind tlp-1.2.1/tlp-sleep.elogind
--- tlp-1.1/tlp-sleep.elogind	1970-01-01 01:00:00.000000000 +0100
+++ tlp-1.2.1/tlp-sleep.elogind	2019-03-18 18:08:25.000000000 +0100
@@ -0,0 +1,14 @@
+#!/bin/sh
+case "${1-}" in
+    'pre')
+        exec tlp suspend
+	;;
+
+    'post')
+        exec tlp resume
+	;;
+
+    *)
+        exit 64
+	;;
+esac
diff -Nru tlp-1.1/tlp-sleep.service.in tlp-1.2.1/tlp-sleep.service.in
--- tlp-1.1/tlp-sleep.service.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-sleep.service.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,13 +1,13 @@
 # tlp - systemd suspend/resume service
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # 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
+Documentation=https://linrunner.de/tlp
 
 [Service]
 Type=oneshot
diff -Nru tlp-1.1/tlp-stat.in tlp-1.2.1/tlp-stat.in
--- tlp-1.1/tlp-stat.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-stat.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,44 +1,48 @@
 #!/bin/sh
-# tlp - display power save and usb autosuspend status
+# tlp-stat - display power saving details
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
-# --- Constants
+# shellcheck disable=SC2086,SC2154
+
+# --- Source libraries
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/[0-9][0-9]* @TLP_FLIB@/tlp-func-stat; do
+    # shellcheck disable=SC1090
+    . $lib || exit 70
+done
 
-readonly LIBDIR="@TLP_TLIB@"
-readonly LIBS="tlp-functions tlp-rf-func"
+# --- Constants
 
 readonly TLPUSB=tlp-usblist
 readonly TLPPCI=tlp-pcilist
+readonly TLPRDW=tlp-rdw
 
-readonly SMARTCTL=smartctl
 readonly LSBREL=lsb_release
+readonly JOURNALCTL=journalctl
 
 readonly ASPM=/sys/module/pcie_aspm/parameters/policy
+readonly EFID=/sys/firmware/efi
 readonly NMIWD=/proc/sys/kernel/nmi_watchdog
 readonly WQPE=/sys/module/workqueue/parameters/power_efficient
 
-readonly 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 IBMFAN=/proc/acpi/ibm/fan
+readonly IBMTHERMAL=/proc/acpi/ibm/thermal
 
-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 }'
+readonly RFKILL_SERVICES="systemd-rfkill.service systemd-rfkill.socket"
 
 # --- Variables
+
 needs_root_priv=
 show_all=1
 show_bat=0
@@ -61,234 +65,6 @@
 
 # --- 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
@@ -307,9 +83,11 @@
     done
 }
 
-read_args () { # read command line arguments
-    for a in $*; do
-        case $a in
+parse_args () { # parse command-line -- $@: arguments to parse
+
+    # iterate arguments until delimiter '--' reached
+    while [ $# -gt 0 ]; do
+        case "$1" in
             "-b"|"--battery")
                 show_all=0
                 show_bat=1
@@ -337,7 +115,7 @@
             "-g"|"--graphics")
                 show_all=0
                 show_graf=1
-                : ${needs_root_priv:=0}
+                needs_root_priv=1
                 ;;
 
             "-p"|"--processor")
@@ -398,6 +176,10 @@
                 needs_root_priv=1
                 ;;
 
+            "--") # config values follow --> quit loop
+                break
+                ;;
+
             *)
                 echo "Usage: tlp-stat [ -b | --battery   | -c | --config    |"
                 echo "                  -d | --disk      | -e | --pcie      |"
@@ -410,38 +192,34 @@
                 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
+        shift # next argument
+    done # while arguments
+
+    return 0
+}
 
 # --- MAIN
-add_sbin2path
 
-read_args $*
-: ${needs_root_priv:=1}
+add_sbin2path
 
-# inhibit trace output
-nodebug=1
 
 # check for and read conffile
-read_defaults
-conf_present=$?
+read_defaults; conf_present=$?
+parse_args "$@"
+parse_args4config "$@"
+: ${needs_root_priv:=1}
+
+# inhibit trace output (unless forced)
+# shellcheck disable=SC2034
+[ "$X_TRACE_TLP_STAT" = "1" ] || _nodebug=1
 
 # check prerequisites
 if [ "$needs_root_priv" = "1" ]; then
     check_root
     load_modules $MOD_MSR $MOD_TEMP
     check_thinkpad
-    check_tpacpi
-    check_tpsmapi
+    check_battery_features
 fi
 
 echo "--- TLP $TLPVER --------------------------------------------"
@@ -451,7 +229,7 @@
 if [ "$show_conf" = "1" ] || [ "$show_all" = "1" ]; then
     if [ $conf_present -eq 0 ]; then
         echo "+++ Configured Settings: $CONFFILE"
-        egrep -v '^#|^\s*$' $CONFFILE
+        grep -E -v '^#|^\s*$' $CONFFILE
         echo
     else
         echo "Error: config file $CONFFILE not present." 1>&2
@@ -461,10 +239,15 @@
 
 if [ "$show_system" = "1" ] || [ "$show_all" = "1" ] ; then
     # --- show system info
+    # simulate arbitrary model
+    if [ -z "$X_SIMULATE_MODEL" ]; then
+        model="$(read_dmi product_version)"
+    else
+        model="$X_SIMULATE_MODEL"
+    fi
     echo "+++ System Info"
-
-    echo "System         = $( read_dmi sys_vendor product_version product_name )"
-    echo "BIOS           = $( read_dmi bios_version )"
+    echo "System         = $(read_dmi sys_vendor) $model $(read_dmi product_name)"
+    echo "BIOS           = $(read_dmi bios_version)"
 
     # --- show release & kernel info
     cmd_exists $LSBREL && echo "Release        = $($LSBREL -d -s)"
@@ -496,8 +279,19 @@
         printf "State          = disabled\n"
     fi
 
+    # --- show RDW status
+    if cmd_exists $TLPRDW; then
+        if check_run_flag $RDWKILL; then
+            printf "RDW state      = disabled\n"
+        else
+            printf "RDW state      = enabled\n"
+        fi
+    else
+        printf "RDW state      = not installed\n"
+    fi
+
     # --- show last invocation time
-    printf "Last run       = %s\n" "$(print_file_modtime_and_age ${LOCKFILE}_tlp)"
+    printf "Last run       = %s\n" "$(print_file_modtime_and_age $PWRRUNFILE)"
 
     # --- show actual power mode
     printf "Mode           = %s\n" "$(print_saved_powerstate)"
@@ -509,6 +303,9 @@
         1) printf "Power source   = battery\n" ;;
         *) printf "Power source   = unknown\n" ;;
     esac
+    if check_ac_quirk "$model"; then
+        echo "Notice: system may not detect AC/charger -- see: https://linrunner.de/en/tlp/docs/tlp-faq.html#acquirk";
+    fi
     echo
 
     # -- check systemd service units status
@@ -521,8 +318,7 @@
             fi
         done
         for su in $RFKILL_SERVICES; do
-            ise=$($SYSTEMCTL is-enabled $su 2> /dev/null)
-            if [ -n "$ise" ] && [ "$ise" != "masked" ]; then
+            if $SYSTEMCTL is-enabled $su 2> /dev/null | grep -q -v 'masked'; then
                 echo "Notice: $su is not masked -- invoke \"systemctl mask $su\" to correct this!"
                 cnt=$((cnt+1))
             fi
@@ -549,16 +345,16 @@
             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)"
+                printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_min_freq" "$(read_sysf $cpuf/scaling_min_freq)"
             fi
 
             if [ -f $cpuf/scaling_max_freq ]; then
-                printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_max_freq" "$(cat $cpuf/scaling_max_freq 2> /dev/null)"
+                printf "%-54s = %8d [kHz]\n" "$cpuf/scaling_max_freq" "$(read_sysf $cpuf/scaling_max_freq)"
             fi
 
             if [ -f $cpuf/scaling_available_frequencies ]; then
                 printf "%s = " "$cpuf/scaling_available_frequencies"
-                for freq in $(cat $cpuf/scaling_available_frequencies 2> /dev/null); do
+                for freq in $(read_sysf $cpuf/scaling_available_frequencies); do
                     printf "%s " "$freq"
                 done
                 printf "[kHz]\n"
@@ -575,9 +371,7 @@
         fi
     done
 
-    check_intel_pstate
-
-    if [ $intel_pstate -eq 1 ]; then
+    if check_intel_pstate; then
         # show Intel P-state info
         printparm "%-54s = ##%3d## [%%]" $CPU_MIN_PERF_PCT
         printparm "%-54s = ##%3d## [%%]" $CPU_MAX_PERF_PCT
@@ -590,7 +384,7 @@
         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
+        if write_sysf "$boost" $CPU_BOOST_ALL_CTRL; then
             printparm "%-54s = ##%d##" $CPU_BOOST_ALL_CTRL
         else
             printparm "%-54s = ##%d## (cpu not supported)" $CPU_BOOST_ALL_CTRL
@@ -615,7 +409,7 @@
                 # - replace numbers with descriptive strings
                 # - remove ":"
                 # - indent and align
-                $ENERGYPERF -r 2>/dev/null | \
+                $ENERGYPERF -r 2>/dev/null | grep -v 'HWP_' | \
                     sed -r 's/://;
                             s/(0x0000000000000000|EPB 0)/performance/;
                             s/(0x0000000000000004|EPB 4)/balance-performance/;
@@ -664,24 +458,24 @@
     echo "+++ Temperatures"
     if [ -f $IBMTHERMAL ]; then
         # use thinkpad-specific sysfile
-        echo "$IBMTHERMAL = $(cat $IBMTHERMAL 2> /dev/null | cut -f2  ) [°C]"
+        echo "$IBMTHERMAL = $(read_sysf $IBMTHERMAL | cut -f2  ) [°C]"
     else
         # use sensors
         cmax=0
         for sens in $(glob_files '/temp?*_input' $CORETEMP_DIRS); do
             if grep -q -- 'Physical' ${sens%input}label 2>/dev/null; then
                 # package info is available -> ignore remaining sensors
-                read -r cmax < $sens
+                cmax=$(read_sysval $sens)
                 break
             else
                 # core info -> find max value
-                read -r ctemp < $sens && [ $ctemp -gt $cmax ] && cmax=$ctemp
+                ctemp=$(read_sysval $sens)
+                [ $ctemp -gt $cmax ] && cmax=$ctemp
             fi
         done
         if [ $cmax -gt 0 ]; then
             perl -e 'printf ("CPU temp               = %5d [°C]\n", '$cmax' / 1000.0);'
         fi
-
     fi
 
     # --- show fan speed
@@ -692,7 +486,7 @@
         # use hwmon
         have_any_fan=
         for fan in $(glob_files '/fan?*_input' $HWMONFAN_DIRS); do
-            if read -r fan_speed < $fan; then
+            if fan_speed=$(read_sysval $fan); then
                 fan_name="${fan##*/}"; fan_name="${fan_name%_input}"
                 have_any_fan=y
 
@@ -722,93 +516,23 @@
 fi # show_all
 
 if [ "$show_disk" = "1" ] || [ "$show_all" = "1" ]; then
-    # --- show disk info form hdparm
+    # --- show storage device info
     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
+    # list for storage device iteration: use default when undefined
+    disklist="${DISK_DEVICES-${DEFAULT_DISK_DEVICES}}"
+    # list for output: add "(default)" when unset, "(disabled)" when empty
+    diskstat="${DISK_DEVICES-${DEFAULT_DISK_DEVICES} (default)}"
+    diskstat="${diskstat:-(disabled)}"
+    printf "Devices = %s\n" "$diskstat"
+
+    # iterate over list
+    if [ -n "$disklist" ]; then
+        # add "(default)" when unset, "(disabled)" when empty
+        cnt=0
+        for dev in $disklist; do # iterate all devices
+            show_disk_data $dev && cnt=$((cnt+1))
+        done
+    fi
     echo
 
     # --- show sata alpm mode
@@ -874,39 +598,9 @@
     [ $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 Intel GPU power mgmt and frequencies
+    show_intel_gpu_data
 
     # --- show radeon power profile or dpm state
     if [ -d $RADD ]; then
@@ -937,7 +631,7 @@
     for i in bluetooth wifi wwan; do
         get_devc $i
         get_devs $i
-        echo_device_state $i $devs
+        echo_device_state $i $_devs
     done
     echo
 
@@ -945,13 +639,13 @@
 
     # --- show bluetooth
     get_bluetooth_ifaces
-    for iface in $bifaces; do
+    for iface in $_bifaces; do
         if [ -n "$iface" ]; then
             ifshown=1
 
             # get bluetooth driver
             get_bluetooth_driver $iface
-            printf "%-30s: bluetooth, " "$iface($bluetoothdrv)"
+            printf "%-30s: bluetooth, " "$iface($_btdrv)"
             if bluetooth_in_use $iface; then
                 echo "connected"
             else
@@ -962,7 +656,7 @@
 
     # --- show wifi data
     get_wifi_ifaces
-    for iface in $wifaces; do
+    for iface in $_wifaces; do
         if [ -n "$iface" ]; then
             ifshown=1
 
@@ -971,21 +665,21 @@
             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" | \
+                    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" | \
+                        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)"
+            printf "%-30s: wifi, " "$iface($_wifidrv)"
             if wireless_in_use $iface; then
                 printf "connected, "
             else
@@ -993,7 +687,7 @@
             fi
             printf "power management = "
             case $wifipm in
-                on|off) printf "$wifipm" ;;
+                on|off) printf "%s" "$wifipm" ;;
                 *)      printf "unknown" ;;
             esac
             printf "\n"
@@ -1002,14 +696,14 @@
 
     # --- show wwan data
     get_wwan_ifaces
-    for iface in $wanifaces; do
+    for iface in $_wanifaces; do
         if [ -n "$iface" ]; then
             ifshown=1
 
             # get wwan driver
             get_wwan_driver $iface
 
-            printf "%-30s: wwan, " "$iface($wwandrv)"
+            printf "%-30s: wwan, " "$iface($_wwandrv)"
             if wireless_in_use $iface; then
                 printf "connected"
             else
@@ -1036,10 +730,25 @@
 fi # show_all
 
 if [ "$show_pcie" = "1" ] || [ "$show_all" = "1" ]; then
+    # --- show pcie aspm state
+    echo "+++ PCIe Active State Power Management"
+    if [ -f $ASPM ]; then
+        pol=$(read_sysf $ASPM | sed -r 's/.*\[(.*)\].*/\1/')
+        if write_sysf "$pol" $ASPM; then
+            echo "$ASPM = $pol"
+        else
+            echo "$ASPM = $pol (using bios preferences)"
+        fi
+    else
+        echo "$ASPM = (not available)"
+    fi
+    echo
+
     # -- show runtime pm
     echo "+++ Runtime Power Management"
     echo "Device blacklist = ${RUNTIME_PM_BLACKLIST:=(not configured)}"
-    pmdbl="${RUNTIME_PM_DRIVER_BLACKLIST-${DEFAULT_PM_DRIVER_BLACKLIST} (default)}"
+    # add "(default)" when unset, "(disabled)" when empty
+    pmdbl="${RUNTIME_PM_DRIVER_BLACKLIST-${DEFAULT_RUNTIME_PM_DRIVER_BLACKLIST} (default)}"
     echo "Driver blacklist = ${pmdbl:-(disabled)}"
     echo
 
@@ -1050,7 +759,6 @@
         echo "Error: missing subcommand $TLPPCI." 1>&2
     fi
     echo
-
 fi # show_pcie
 
 if [ "$show_usb" = "1" ] || [ "$show_all" = "1" ]; then
@@ -1099,144 +807,73 @@
     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
+    # --- show availability of (ThinkPad) battery features
+    echo "+++ Battery Features"
 
-    if [ $tpsmapi -eq 0 ]; then
-        # it's a ThinkPad with tp-smapi
+    # native ACPI kernel battery API
+    case $_natacpi in
+        0|1) printf "natacpi    = active "; print_methods_per_driver "natacpi" ;;
+        32)  echo "natacpi    = inactive (disabled by configuration)" ;;
+        128) echo "natacpi    = inactive (no kernel support)" ;;
+        253) echo "natacpi    = inactive (laptop not supported)" ;;
+        *)   echo "natacpi    = unknown status" ;;
+    esac
 
-        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
+    # ThinkPad-specific
+    case $_tpacpi in
+        0)   printf "tpacpi-bat = active "; print_methods_per_driver "tpacpi" ;;
+        32)  echo "tpacpi-bat = inactive (disabled by configuration)" ;;
+        64)  echo "tpacpi-bat = inactive (kernel module 'acpi_call' load error)" ;;
+        127) echo "tpacpi-bat = inactive (program 'tpacpi-bat' not installed)" ;;
+        128) echo "tpacpi-bat = inactive (kernel module 'acpi_call' not installed)" ;;
+        253) echo "tpacpi-bat = inactive (laptop not supported)" ;;
+        254) echo "tpacpi-bat = inactive (ThinkPad not supported)" ;;
+        255) echo "tpacpi-bat = inactive (superseded by natacpi)" ;;
+        *)   echo "tpacpi-bat = unknown status" ;;
+    esac
+    case $_tpsmapi in
+        0)   printf "tp-smapi   = active "; print_methods_per_driver "tpsmapi" ;;
+        1)   printf "tp-smapi   = readonly "; print_methods_per_driver "tpsmapi" ;;
+        32)  echo "tp-smapi   = inactive (disabled by configuration)" ;;
+        64)  echo "tp-smapi   = inactive (kernel module 'tp_smapi' load error)" ;;
+        128) echo "tp-smapi   = inactive (kernel module 'tp_smapi' not installed)" ;;
+        253) echo "tp-smapi   = inactive (laptop not supported)" ;;
+        254) echo "tp-smapi   = inactive (ThinkPad not supported)" ;;
+        255) echo "tp-smapi   = inactive (superseded by natacpi)" ;;
+        *)   echo "tp-smapi   = unknown status" ;;
+    esac
+    echo
 
-                    # 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
+    case $_bm_read in
+        natacpi) batglob="$ACPIBATDIR/*" ;;
+        tpsmapi) batglob="$SMAPIBATDIR/BAT[01]" ;;
+    esac
 
-                    bcnt=$(($bcnt+1))
-                fi
-            fi
-        done
-    elif [ -d $ACPIBATDIR ]; then
-        # --- show ACPI data
+    # -- show battery data
+    for batd in $batglob; do # iterate all batteries
 
-        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
+        [ -d "$batd" ] || continue # skip if battery directory does not exist
+        batt=${batd##/*/}
+        battery_present $batt || continue # skip if battery is not present
+
+        if is_thinkpad && [ "$_bm_thresh" != 'none' ]; then
+            # ThinkPad with battery feature support
+            case $_bat_idx in
+                1) echo "+++ ThinkPad Battery Status: $batt (Main / Internal)" ;;
+                2) echo "+++ ThinkPad Battery Status: $batt (Ultrabay / Slice / Replaceable)" ;;
+                0) echo "+++ ThinkPad Battery Status: $batt" ;;
+            esac
+        else
+            # laptop or ThinkPad without feature support
+            echo "+++ Battery Status"
+        fi
 
-            if [ -n "$batt" ]; then
+        case $_bm_read in
+            natacpi) # use ACPI data
                 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
+                if cc=$(read_sysval $batd/cycle_count) && [ $cc -gt 0 ]; then
                     printf "%-59s = %6d\n" "$batd/cycle_count" "$cc"
                 else
                     printf "%-59s = (not supported)\n" "$batd/cycle_count"
@@ -1249,11 +886,11 @@
                     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))
+                    ed=$(read_sysval $batd/energy_full_design)
+                    ef=$(read_sysval $batd/energy_full)
+                    en=$(read_sysval $batd/energy_now)
+                    efsum=$((efsum + ef))
+                    ensum=$((ensum + en))
 
                 elif [ -f $batd/charge_full ]; then
                     printparm "%-59s = ##%6d## [mAh]" $batd/charge_full_design "" 000
@@ -1262,23 +899,19 @@
                     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))
+                    ed=$(read_sysval $batd/charge_full_design)
+                    ef=$(read_sysval $batd/charge_full)
+                    en=$(read_sysval $batd/charge_now)
+                    efsum=$((efsum + ef))
+                    ensum=$((ensum + en))
 
                 else
                     ed=0
                     ef=0
                     en=0
-
-                fi
-                if [ $tpbat -eq 1 ]; then
-                    print_tp_batstate $batd/status
-                else
-                    printparm "%-59s = ##%s##" $batd/status
                 fi
+
+                print_batstate $batd/status
                 echo
 
                 if [ $show_verbose -eq 1 ]; then
@@ -1286,30 +919,72 @@
                     printparm "%-59s = ##%6s## [mV]" $batd/voltage_now "" 000
                     echo
                 fi
+                ;; # natacpi
 
-                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))
+            tpsmapi) # ThinkPad with active tp-smapi
+                printparm "%-59s = ##%s##" $batd/manufacturer
+                printparm "%-59s = ##%s##" $batd/model
+                printparm "%-59s = ##%s##" $batd/manufacture_date
+                printparm "%-59s = ##%s##" $batd/first_use_date
+                printparm "%-59s = ##%6d##" $batd/cycle_count
+
+                if [ -f $batd/temperature ]; then
+                    # shellcheck disable=SC2046
+                    perl -e 'printf ("%-59s = %6d [°C]\n", "'$batd/temperature'", '$(read_sysval $batd/temperature)' / 1000.0);'
                 fi
-                if [ $ed -ne 0 ]; then
-                    perl -e 'printf ("%-59s = %6.1f [%%]\n", "Capacity", 100.0 * '$ef' / '$ed');'
-                    lcnt=$(($lcnt+1))
+
+                printparm "%-59s = ##%6d## [mWh]" $batd/design_capacity
+                printparm "%-59s = ##%6d## [mWh]" $batd/last_full_capacity
+                printparm "%-59s = ##%6d## [mWh]" $batd/remaining_capacity
+                printparm "%-59s = ##%6d## [%%]" $batd/remaining_percent
+                printparm "%-59s = ##%6s## [min]" $batd/remaining_running_time_now
+                printparm "%-59s = ##%6s## [min]" $batd/remaining_charging_time
+                printparm "%-59s = ##%6d## [mW]" $batd/power_now
+                printparm "%-59s = ##%6d## [mW]" $batd/power_avg
+                print_batstate $batd/state
+                echo
+                if [ $show_verbose -eq 1 ]; then
+                    printparm "%-59s = ##%6s## [mV]" $batd/design_voltage
+                    printparm "%-59s = ##%6s## [mV]" $batd/voltage
+                    printparm "%-59s = ##%6s## [mV]" $batd/group0_voltage
+                    printparm "%-59s = ##%6s## [mV]" $batd/group1_voltage
+                    printparm "%-59s = ##%6s## [mV]" $batd/group2_voltage
+                    printparm "%-59s = ##%6s## [mV]" $batd/group3_voltage
+                    echo
                 fi
-                [ $lcnt -gt 0 ] && echo
 
-                bcnt=$(($bcnt+1))
-            fi # if $batt
-        done # $batd
+                # store values for charge / capacity calculation below
+                ed=$(read_sysval $batd/design_capacity)
+                ef=$(read_sysval $batd/last_full_capacity)
+                en=$(read_sysval $batd/remaining_capacity)
+                efsum=$((efsum + ef))
+                ensum=$((ensum + en))
+                ;; # tp-smapi
+
+        esac # $_bm_read
+
+        # --- show thresholds
+        print_thresholds $batt
+
+        # --- show force_discharge
+        print_discharge $batt
+
+        # show charge + capacity
+        echo
+        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 /sys/class/power_supply
+    done # for batd
 
     if [ $bcnt -eq 0 ]; then
         # no battery detected
@@ -1326,15 +1001,15 @@
 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 )
+    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 "  See the FAQ: https://linrunner.de/en/tlp/docs/tlp-faq.html#warnings\n";
         printf "  Details:\n"
-        dmesg | egrep -A 5 "${RE_ATA_ERROR}"
+        dmesg | grep -E -A 5 "${RE_ATA_ERROR}"
         echo
     elif [ "$show_warn" = "1" ]; then
         echo "No warnings detected."
@@ -1343,46 +1018,42 @@
 
 fi # show_warn
 
-if [ "$show_all" = "1" ]; then
-    # -- show suggestions
-    suout=""
+if [ "$show_bat" = "1" ] || [ "$show_all" = "1" ]; then
+    # -- show recommendations
+    reout=""
 
-    if [ "$no_runtimepm" = "1" ]; then
-        suout="${suout}Reconfigure your Linux kernel with PM_RUNTIME=y to reduce your laptop's power consumption.\n"
+    if [ "$show_all" = "1" ] && [ "$no_runtimepm" = "1" ]; then
+        reout="${reout}Reconfigure your Linux kernel with PM_RUNTIME=y to reduce your laptop's power consumption.\n"
     fi
 
     if is_thinkpad; then
-        # add ThinkPad specific 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"
-
+        # add ThinkPad specific recommendations
+        [ $_tpsmapi -eq 128 ] \
+            && reout="${reout}Install tp-smapi kernel modules for ThinkPad battery thresholds and recalibration\n"
+        case $_tpacpi in
+            127) missing="tpacpi-bat program" ;;
+            128) missing="acpi_call kernel module" ;;
+            *)   missing="" ;;
+        esac
+        if [ -n "$missing" ]; then
+            case $_natacpi in
+                0) ;; # natacpi covers it all
+                1) reout="${reout}Install $missing for ThinkPad battery recalibration\n" ;;
+                *) reout="${reout}Install $missing for ThinkPad battery thresholds and recalibration\n" ;;
+            esac
         fi
     fi # if ThinkPad
 
-    # 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/^/\* /'
+    if [ "$show_all" = "1" ]; then
+        # add other recommendations
+        cmd_exists ethtool  || reout="${reout}Install ethtool to disable Wake On LAN (WOL)\n"
+        cmd_exists smartctl || reout="${reout}Install smartmontools for disk drive health info\n"
+    fi
+
+    if [ -n "$reout" ]; then
+        echo "+++ Recommendations"
+        # shellcheck disable=SC2059
+        printf "$reout" | sed -r 's/^/\* /'
         echo
     fi
 
@@ -1403,7 +1074,8 @@
     # --- 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
+        # shellcheck disable=SC2063
+        grep -s '.*' $ps/type $ps/online $ps/present $ps/status $ps/device/path
     done
 fi # show_sup
 
@@ -1413,16 +1085,14 @@
     # 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
+        # retrieve trace output from journal, rc=1 if journald has no data available
+        $JOURNALCTL -p debug --no-pager SYSLOG_IDENTIFIER=tlp 2> /dev/null && jdone=1
     fi
 
     if [ "$jdone" = "0"  ]; then
         # no journald data available --> retrieve trace output from logfile
         if [ -f $DEBUGLOG ]; then
-            grep "tlp\[" $DEBUGLOG
+            grep 'tlp\[' $DEBUGLOG
         else
             echo "Error: $DEBUGLOG does not exist." 1>&2
             echo 1>&2
diff -Nru tlp-1.1/tlp.upstart.in tlp-1.2.1/tlp.upstart.in
--- tlp-1.1/tlp.upstart.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp.upstart.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,6 +1,6 @@
 # tlp - system startup/shutdown
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 description "tlp"
diff -Nru tlp-1.1/tlp-usblist tlp-1.2.1/tlp-usblist
--- tlp-1.1/tlp-usblist	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-usblist	2019-03-18 18:08:25.000000000 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # tlp-usblist - list usb device info with autosuspend attributes
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 package tlp_usblist;
@@ -81,29 +81,15 @@
 
 # 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
-        }
+    # get attributes
+    if ( length (my $asv = catsysf ("$udev/power/autosuspend_delay_ms"))
+        && length (my $cnv = catsysf ("$udev/power/control")) ) {
+        $usbv = sprintf ("control = %-5s autosuspend_delay_ms = %4d", $cnv . ",", $asv);
     }
 
     # save formatted result in hash
diff -Nru tlp-1.1/tlp-usb-udev.in tlp-1.2.1/tlp-usb-udev.in
--- tlp-1.1/tlp-usb-udev.in	2018-01-24 06:50:43.000000000 +0100
+++ tlp-1.2.1/tlp-usb-udev.in	2019-03-18 18:08:25.000000000 +0100
@@ -1,53 +1,29 @@
 #!/bin/sh
 # tlp - handle added usb devices
 #
-# Copyright (c) 2018 Thomas Koch <linrunner at gmx.net>
+# Copyright (c) 2019 Thomas Koch <linrunner at gmx.net> and others.
 # This software is licensed under the GPL v2 or later.
 
 # Remark: the calling udev rule is triggered for "base" devices only,
 #         not for the corresponding subdevices.
 
-# --- Constants
-readonly LOGGER=logger
+# shellcheck disable=SC2086
 
-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
-}
+# --- Source libraries
+
+for lib in @TLP_TLIB@/tlp-func-base @TLP_FLIB@/20-tlp-func-usb; do
+    # shellcheck disable=SC1090
+    . $lib || exit 70
+done
 
 # --- MAIN
 
 # read config
-[ -f $CONFFILE ] || exit 0
-. $CONFFILE
+read_defaults
 
 # exit if TLP or autosuspend disabled
-[ "$TLP_ENABLE" = "1" ] && [ "$USB_AUTOSUSPEND" = "1" ] || exit 0
+: ${USB_AUTOSUSPEND:=${DEFAULT_USB_AUTOSUSPEND}}
+check_tlp_enabled 0 && [ "$USB_AUTOSUSPEND" = "1" ] || exit 0
 
 # USB autosuspend has two principal operation modes:
 #
@@ -61,212 +37,13 @@
 # - 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
+[ "$X_TLP_USB_MODE" = "1" ] && ! check_run_flag $USB_DONE && exit 0
 
-# get args
-usbdev=/sys$1
+if [ "$X_USB_ENV_TRACE" = "1" ]; then
+    echo_debug "usb" "tlp_usb_udev.env = $(printenv)"
+fi
 
 # 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
+usb_suspend_device "/sys$1" "udev"
 
 exit 0

Attachment: signature.asc
Description: OpenPGP digital signature


--- End Message ---
--- Begin Message ---
Hi Raphaël

I'm not unblocking your package.

On Sat, 13 Apr 2019 13:04:55 +0200 =?UTF-8?Q?Rapha=c3=abl_Halimi?=
<raphael.halimi@gmail.com> wrote:
> I would perfectly understand why this request would be rejected, since a
> new version [...]

That. This new version is not in line with the freeze policy at this
part of the freeze. Sorry.

Paul

Attachment: signature.asc
Description: OpenPGP digital signature


--- End Message ---

Reply to: