User Tools

Site Tools


laptop_hacks

Laptop Hacks

Ho boy, this got difficult really quickly - 4 years ago I had everything working on my laptop with the LXDE spin of fedora plus fluxbox. After a quick spurt of travel using all the laptoppy things like conserving battery life, suspend/resume and lid up and down, I left it alone, using it like a desktop on AC power but updating through various fedora's. Quite unbeknownst to me, systemd came in and the laptop got broken - I had to re-discover how to do everything again. Now I know and understand some of it and hopefully recording it here will help me next time.

Maybe it'll help you too - but you will almost certainly need to some make tweaks for your particular setup.

Why go through all this? Why not just use a full DE like KDE or gnome to take care of it? With KDE the best I could get (without digging just as deep as I have done here) was about 4 hours of battery life. By doing the hard work and understanding what's going on I can get up to 6.5 hours! Not bad.

Who's doing the work?

It turns out that there are several players that respond to laptop events like lid opening & closing, ac power connection, suspend etc even without the distraction of gnome and KDE. Notably acpid and systemd. I got _really_ distracted thinking that systemd was the new, definitive way that had taken over the world - turns out, I was wrong, my machine was still using acpid.

How to tell? AFAICS you look at the logs - /var/log/messages or journalctl -r

How to change it? No idea - possibly installing acpid switches over to that.

Power Disconnect/Connect events

If using acpid then I put the following scripts into /etc/pm/power.d and I run this in my .xsession.

# pm-util no longer executes /etc/pm/power.d/* things so if there's a
# battery then listen on dbus for a/c connect/disconnect:
upower -e |grep -q battery && power-monitor &

Where, power-monitor is this bit of python:

#!/bin/env python

import gobject, os
gobject.threads_init()

from dbus import glib
glib.init_threads()
import dbus
bus = dbus.SystemBus()

def backtick(command):
    """
    Equivalent of Bourne shell's backtick
    See http://www.python.org/doc/2.5.1/lib/node534.html
    """
    from subprocess import Popen, PIPE
    #print "backtick: command='%s'\n" % command
    value = Popen(["sh", "-c", command], stdout=PIPE).communicate()[0].rstrip()
    #print "returning '%s'\n" % value
    return(value)

def ac(*args, **kwargs):
    if args[1]['Online'] == 0:
        print("unplugged")
        os.system("sudo /home/bhepple/bin/low-power true")
    else:
        print("plugged in")
        os.system("sudo /home/bhepple/bin/low-power false")
        
def check_battery(*args, **kwargs):
    battery_status = backtick("acpi -b")
    print(battery_status)
    if "Discharging" in battery_status:
        percent = int( backtick("acpi -b | awk '{print $4}' | tr -d '%,'"))
        print percent
        if percent < 10: backtick("battery-alarm&")
    gobject.timeout_add(60 * 1000, check_battery)

# get path from 'upower -e':
bus.add_signal_receiver(ac, signal_name="PropertiesChanged", dbus_interface="org.freedesktop.DBus.Properties", path="/org/freedesktop/UPower/devices/line_power_ADP0")

check_battery()

l = gobject.MainLoop()
l.run()

/etc/pm/power.d/01-pm-power-display to dim/brighten the display - requires xbacklight:

#!/bin/sh

logger -t $0 "$0 $@"
case "$1" in
true)
    # called by pm-powersave on power disconnect
                
    xbacklight -display :0 -set 1
    logger -t $0 "power disconnect: xbacklight -set 10"
    ;;
false)
    xbacklight -display :0 -set 40
    logger -t $0 "power connect: xbacklight -set 40"
    ALARM_PID_FILE="/var/run/battery-alarm"
    [[ -f $ALARM_PID_FILE ]] && {
        logger -t $0 "alarm pids = $( cat $ALARM_PID_FILE )"
        for PID in $(cat $ALARM_PID_FILE); do
            [[ $PID > 0 ]] && kill $PID
        done
        rm $ALARM_PID_FILE
    }
    ;;
esac
exit 0

Put this in /etc/pm/power.d/03-pm-power-hda to maximise battery or performance on the hard disc:

#!/bin/sh
logger -t $0 "$0 $@"

case "$1" in
        true)
                # called by pm-powersave on power disconnect
                logger -t $0 "power disconnect: hdparm -B1 -S5 /dev/sda"
                hdparm -B1 -S5 /dev/sda
                ;;
        false)
                # called by pm-powersave on power connect
                logger -t $0 "power connect: hdparm -B128 -S60 /dev/sda"
                hdparm -B128 -S60 /dev/sda
                ;;
esac
exit 0

Put this in /etc/pm/power.d/02-pm-power-tweaks for general tweaking - most of this originates from powertop:

#!/bin/sh
# BH

logger -t $0 "$0 $@"
case "$1" in
true)
    # called by pm-powersave on power disconnect

    echo 5 > /proc/sys/vm/laptop_mode
    echo 0 > /proc/sys/kernel/nmi_watchdog
    echo 1 > /sys/devices/system/cpu/sched_smt_power_savings
    echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
    echo 1500 > /proc/sys/vm/dirty_writeback_centisecs
    for i in /sys/bus/usb/devices/*/power/autosuspend; do echo 1 > $i; done
    for i in /sys/bus/usb/devices/*/power/level; do echo auto > $i; done
    echo min_power > /sys/class/scsi_host/host0/link_power_management_policy
    echo min_power > /sys/class/scsi_host/host1/link_power_management_policy
    echo Y > /sys/module/snd_hda_intel/parameters/power_save_controller
    echo 1 > /sys/module/snd_hda_intel/parameters/power_save
    for i in /sys/bus/{pci,i2c}/devices/*/power/control; do echo auto > $i; done
    ;;
false)
    echo 0 > /proc/sys/vm/laptop_mode
    echo 1 > /proc/sys/kernel/nmi_watchdog
    echo 0 > /sys/devices/system/cpu/sched_smt_power_savings
    echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
    echo 500 > /proc/sys/vm/dirty_writeback_centisecs
    for i in /sys/bus/usb/devices/*/power/autosuspend; do echo 2 > $i; done
    # for i in /sys/bus/usb/devices/*/power/level; do echo auto > $i; done
    echo max_performance > /sys/class/scsi_host/host0/link_power_management_policy
    echo max_performance > /sys/class/scsi_host/host1/link_power_management_policy
    #echo Y > /sys/module/snd_hda_intel/parameters/power_save_controller
    #echo 1 > /sys/module/snd_hda_intel/parameters/power_save
    #for i in /sys/bus/{pci,i2c}/devices/*/power/control; do echo auto > $i; done
    ;;
esac

exit 0

On battery script

For one reason or another a bunch of things got put into low-power:

#!/bin/sh

POWER="OFF"
[[ "${1:-}" && "$1" == "false" ]] && POWER="ON"

(( $(id -u) == 0 )) || {
    echo "$0: needs to run as root"
    exit 1
}

LOG=/tmp/low-power
echo "stdout and stderr will be sent to $LOG"
exec >$LOG 2>&1

case "$POWER" in
    OFF)
        battery-status
        cat /proc/acpi/bbswitch

        logger -t $0 "power disconnect: killing bluetooth"
        systemctl stop bluetooth.target
        BT_PID=$(ps -ef |grep '[/]usr/libexec/bluetooth/bluetoothd' | awk '{print $2}')
        [[ "$BT_PID" ]] && kill -9 $BT_PID
        rmmod bnep btbcm btrtl btusb btintel bluetooth

        logger -t $0 "power disconnect: turning off NVidia Opti with bbswitch"
        echo OFF >/proc/acpi/bbswitch

        sudo /home/bhepple/bin/01-pm-power-display true
        sudo /home/bhepple/bin/02-pm-power-tweaks true
        sudo /home/bhepple/bin/03-pm-power-hda true

        powertop --auto-tune
        sleep 3

        cat /proc/acpi/bbswitch
        battery-status

        echo "consider turning off wifi"
        echo "consider turning off camera: rmmod uvcvideo"
        echo "consider turning off ethernet: rmmod r8169 mii"
        ;;
    ON)
        sudo /home/bhepple/bin/01-pm-power-display false
        sudo /home/bhepple/bin/02-pm-power-tweaks false
        sudo /home/bhepple/bin/03-pm-power-hda false

        logger -t $0 "power connect: starting bluetooth"
        modprobe bluetooth
        systemctl start bluetooth.target

        modprobe uvcvideo r8169 mii
        ;;
esac

pkill -0 i3blocks && pkill  -RTMIN+9 i3blocks

cat $LOG

hibernate/suspend events

Put this in /etc/pm/sleep.d/01-pm-sleep-lockscreen:

#!/bin/sh

logger -t $0 "$@"

. "${PM_FUNCTIONS}"

case $1 in
    suspend|hibernate)
        : run whatever you want to run at suspend
        ;;
    resume|thaw)
        : run whatever you want to run at resume
        ;;
    *) 
        exit $NA 
        ;;
esac
exit 0

… the trouble is, it's running as root without X's $DISPLAY - so you'd need to jump through hoops to run user X programs like xscreensaver or syndaemon. Maybe try xss-lock for that? Fortunately, if the suspend has been long enough, then xscreensaver will lock the display on resume anyway.

This is /usr/local/bin/set-synaptics:

#!/bin/sh

[ "$DISPLAY" ] || {
    echo $0': $DISPLAY not set' >&2
    exit 1
}

r pkill syndaemon
r syndaemon -i 1 -d -K
synclient VertEdgeScroll=1 HorizEdgeScroll=1 VertTwoFingerScroll=1 HorizTwoFingerScroll=1 PalmDetect=1 TapButton1=1 TapButton2=2 # RTCornerButton=2

If using systemd then you would put this into /etc/systemd/system/resume@.service:

[Unit]
Description=User resume actions
After=suspend.target

[Service]
User=%I
Type=simple
ExecStart=/usr/local/bin/set-synaptics

[Install]
WantedBy=suspend.target

then run systemctl enable resume@bhepple.service

Disable nouveau

I find it slow & buggy so /etc/modprobe.d/blacklist.conf gets:

blacklist nouveau

and /etc/default/grub gets:

rdblacklist=nouveau

Run this to get grub updated:

grub2-mkconfig >/boot/grub2/grub.cfg
laptop_hacks.txt · Last modified: 2016/12/05 01:13 by admin