Slackware 10.2 on a Thinkpad T30

Note: This document is a work in progress and sections will be added as I figure out additional things. Last updated 11/15/05.

Introduction
Suspend to Disk aka Hibernate
The hibernate.conf File
General Power Saving Ideas
Automatically Hibernate when the Battery is Critical
Drive Bay Hot swapping
Video
Networking (Wireless and Wired)
Modem

Introduction

I really didn't know it until after I got Suspend to Disk working but it seems that it's a hard thing to get working on Linux. Or at least that's what I hear. I guess I just got lucky to get it working. Since it was work intensive on Slackware I figured I'd write about it for both my own notes and to benefit anyone else who may be interested.

My laptop is a Thinkpad T30 that I use from work. My primary goal is to show work that Linux can be used for day to day work and effectively interact in a predominately Windows environment. To do this in my work environment I chose to use KDE as the desktop mostly because it is in my opinion the easiest desktop environment in Linux for Windows folks to adjust to. I chose Slackware 10.2 as the distro to do this little project on mostly because I know it better than any other distribution but also because I wanted to learn as much as I could and have a little hand holding as possible. I haven't tried it but it is my understanding that Suse and Ubuntu deal with laptops and especially suspend to disk fairly well depending on the hardware. Some of the other goals I will attempt to meet are:

  • Synchronize file and PIM data with an Ipaq
  • Full E-mail and calendaring in an Exchange environment
  • Suspend to disk support along with decent general power management
  • Be able to hot swap the removable drive bay with out rebooting
  • Have the ability to run multiple Windows Operating Systems for experimentation and to help in client support calls
  • As much as possible to have configurations not be window manager specific to allow easy changing to another window manger in the future

Suspend to Disk aka Hibernate

Since this is a laptop that I use out on the road, often away from a convenient power source the first thing I chose to tackle was power management. In Windows, in order to maximize battery life I often hibernate (aka Suspend to Disk) my system instead of just going into standby. The obvious advantage of hibernate over standby is that in hibernation the system is totally turned off. When in standby the system still uses power just at a lower rate. Before I go to far though, let me say I've heard that the drivers provided by Nvidia and ATI can have issues with being suspended. Like I said, I've only heard this, not seen it. But I can say that so far the generic Radeon drivers in Xorg work perfectly.

First off Slackware 10.2 installs with the 2.4.31 kernel and I wanted the latest ACPI support so I had to upgrade to the 2.6.13 kernel on CD 2. There is only one catch with this though. The default file system for Slack is reiserfs but the 2.6.13 kernel only has it compiled in as a module forcing you to use an initrd file or to recompile the kernel. In this case I chose to recompile to kernel since I had to patch the kernel for hibernation to work. I the found Suspend2 which has a mess of cool features compared to the default software suspend in the kernel. Plus it had the added advantage of actually working on my hardware. I'm not going to go deep into how I patched the kernel and setup my suspend file because, well, it was really easy and their HOWTO and FAQ are very well written. Just be sure you read the section entitled Avoiding Data Loss to see if any of it applies to you. Also, make this the first thing you setup because until you get it right it's very easy to corrupt your file system beyond repair. I nuked my system once while experimenting with this. Lucky for me I didn't put much effort into anything else at that point. Of course, if you nuke your system with important data on it and don't have a good backup it's not my fault.

At this point, assuming that Suspend2 and the necessary hibernate script is installed the easiest way to hibernate your system is to just type hibernate as root. The problem with this is it's manual. I wanted automatic. In particular I wanted it to automatically hibernate when the battery got below a certain point and also when I closed the lid. And to have it do all of this without depending on KDE to do it.

In order to to this I figured acpid would be the way to go. The default configuration of the 2.6.13 kernel with Slack had everything I needed compiled as modules. So to get ACPI working I just had to modprobe them all in. In /etc/rc.d/rc.local I put:

echo "Loading ACPI and Speedstep Modules..."
/sbin/modprobe ac
/sbin/modprobe battery
/sbin/modprobe button
/sbin/modprobe processor
/sbin/modprobe ibm_acpi
/sbin/modprobe thermal
/sbin/modprobe video
/sbin/modprobe fan
/sbin/modprobe speedstep_ich
You may not need the ibm_acpi module or the speedstep_ich modules depending on your laptop. The ibm_acpi module adds Thinkpad specific support for certain acpi events (not sure exactly which ones) and the speedstep_ich adds support for CPU throttling to save battery. Once these are modprobed in make sure /etc/rc.d/rc.acpid is executable then either reboot (the windows way) or just run /etc/rc.d/rc.acpid start. Now, if we keep an eye on /var/log/acpid we can see just what events are generated when we close the lid and change between battery and AC power.

tail -f /var/log/acpid gave me this when I opened and closed the lid a bunch of times.

[Wed Oct 27 00:42:17 2005] received event "button/lid LID 00000080 00000001"
[Wed Oct 26 00:42:17 2005] executing action "/etc/acpi/acpi_handler.sh button/lid LID 00000080 00000001"
[Wed Oct 26 00:42:17 2005] BEGIN HANDLER MESSAGES
[Wed Oct 26 00:42:17 2005] END HANDLER MESSAGES
[Wed Oct 26 00:42:17 2005] action exited with status 0
[Wed Oct 26 00:42:17 2005] completed event "button/lid LID 00000080 00000001"
[Wed Oct 26 00:42:20 2005] received event "button/lid LID 00000080 00000002"
[Wed Oct 26 00:42:20 2005] executing action "/etc/acpi/acpi_handler.sh button/lid LID 00000080 00000002"
[Wed Oct 26 00:42:20 2005] BEGIN HANDLER MESSAGES
[Wed Oct 26 00:42:20 2005] END HANDLER MESSAGES
[Wed Oct 26 00:42:20 2005] action exited with status 0
[Wed Oct 26 00:42:20 2005] completed event "button/lid LID 00000080 00000002"
This was for closing the lid and then opening it. So it generates an event when the lid is closed and again when opening it each time counting up the event in HEX. So, I needed a rule that would only catch every other lid event and then call hibernate.

To do this I created an event catcher called lid_button in /etc/acpid/events/ with the following two lines. Any time you add an event you have to restart acpid with /etc/rc.d/rc.acpid restart to make it load the new rule.

event=button[/]lid LID.*[13579bdf]$
action=/usr/local/sbin/hibernate
What this does is it catches every button/lid event that ends in the odd HEX numbers and runs the hibernate script for you. The first time I did this I put in the even numbers which caused it to hibernate whenever I opened the lid! :P

At this point it's probably wise to take a look at the hibernate.conf file in /etc/hibernate and to read the man page and related documents on the suspend2 site if you haven't already. Two entries I use are OnSuspend and OnResume which allow you to run commands just before suspending or just after resuming. You can also do other nice things like unload and load modules, unmount and mount file systems, and up and down network cards.

The hibernate.conf file

The hibernate.conf file is nice to take care of those odd things that make suspend/resume mess up. For instance, when I would resume and try to use the wireless network my system would lock. So I figured I'd have to kill dhcpcd, down eth1, and unload the airo and related modules just before suspend. I change wireless networks so I didn't choose to bring it all back up on resume but manually run a script to bring up the wireless depending on where I am. Here is the script for those who care. It's a modified version of a script I was given by someone I can't remember from the #suspend2 channel on freenode.

#/bin/bash
#
# chkconfig: 10 90 9
# description: Starts and stops the wireless interface.

#ESSID=<essid>
case $2 in
	home)
		ESSID=<home_essid>
		#MY_ADDR=10.1.1.2
		#NET_MASK=255.0.0.0
		DHCP=1
		WEP=<home_wep_key>
		#WPA_SUPP=1
		#GATEWAY=10.1.1.1
		#FIREWALL_ARGS=
		#CHANNEL=2
		#AP=00:0f:b5:53:da:ac
		;;
	work)
		ESSID=<work_essid>
		#MY_ADDR=10.1.1.2
		#NET_MASK=255.0.0.0
		DHCP=1
		WEP=<work_wep_key>
		#WPA_SUPP=1
		#GATEWAY=10.1.1.1
		#FIREWALL_ARGS=
		#CHANNEL=2
		#AP=00:0f:b5:53:da:ac
		;;
	*)
		ESSID=Any
		#ESSID=LCA2005
		#MY_ADDR=172.24.2.169
		#NET_MASK=255.255.0.0
		#GATEWAY=172.24.0.1
		#DNS=172.24.0.1
		#FIREWALL_ARGS=
		#CHANNEL=1
		DHCP=1
		#AP=00:90:4C:60:00:2A
		#RATE=
esac

stop() {
	echo Removing modules...

	/sbin/dhcpcd -k eth1
	/sbin/ifconfig eth1 down
	/sbin/rmmod airo
	/sbin/rmmod aes_i586
	#cardctl eject
	#cat /etc/resolv.conf.backup > /etc/resolv.conf
}

start() {
	echo Loading interface...
	#cardctl insert
	/sbin/modprobe airo
	sleep 5
	if [[ $ESSID ]]; then
		echo /sbin/iwconfig eth1 essid $ESSID
		/sbin/iwconfig eth1 essid $ESSID
		#sleep 5
	fi
	if [[ $WEP ]]; then
		echo /sbin/iwconfig eth1 key restricted $WEP
		/sbin/iwconfig eth1 key restricted  $WEP
		#sleep 5
	fi
	#echo /sbin/iwconfig eth1 nwid CunninghamHome
	#/sbin/iwconfig eth1 nwid CunninghamHome
	if [[ $CHANNEL ]]; then
		echo /sbin/iwconfig eth1 channel $CHANNEL
		/sbin/iwconfig eth1 channel $CHANNEL
		#sleep 5
	fi
	if [[ $AP ]]; then
		echo /sbin/iwconfig eth1 ap $AP
		/sbin/iwconfig eth1 ap $AP
		#sleep 5
	fi
	if [[ $RATE ]]; then
		echo /sbin/iwconfig eth1 rate 54M
		/sbin/iwconfig eth1 rate 54M
		/sbin/iwconfig eth1 rate fixed
		#sleep 5
	fi
	echo /sbin/ifconfig eth1 up
	/sbin/ifconfig eth1 up

	if [[ $AP ]]; then
		iwconfig eth1 ap $AP
		#sleep 5
	fi

	if [[ $WPA_SUPP ]] ; then
		echo Starting wpa supplicant
		/usr/bin/wpa_supplicant -c/etc/wpa_supplicant.conf -Dmadwifi -ieth1 -B -w 
		#sleep 5
	fi
	#/usr/bin/wpa_supplicant -c/etc/wpa_supplicant.conf -Dmadwifi -ieth1 -B

	if [[ $DHCP ]]; then
		sleep 2
		echo /sbin/dhcpcd -d -t 60 eth1
		/sbin/dhcpcd -d -t 60 eth1
		#/sbin/route add default gw 10.1.1.1
		#/etc/rc.d/rc.firewall fdjkl
	else
		/sbin/ifconfig eth1 $MY_ADDR netmask $NET_MASK
		/sbin/route add default gw $GATEWAY
		#cat /etc/resolv.conf > /etc/resolv.conf.backup
		echo "nameserver $DNS" > /etc/resolv.conf
	fi

	#/etc/rc.d/rc.firewall $FIREWALL_ARGS
	#iptables -A POSTROUTING -t nat -o eth1 -j MASQUERADE
}

restart() {
	stop
	/sbin/cardctl insert
	start
}

case $1 in
	start)
		start
		;;
	open)
		start-open
		;;
	stop)
		stop
		;;
	restart)
		restart
		;;
	*)
		echo "Unrecognised option: $1"
		exit 1
esac
exit $?
This script isn't clean yet but it works for now. Assuming the script is called "wifi" then wifi stop brings down the wireless, wifi start brings up the wireless in a generic way, and wifi start home brings up the wireless with the settings for home.

One other quick trick I picked up somewhere is to put OnSuspend 20 /opt/kde/bin/dcop --all-users --all-sessions kdesktop KScreensaverIface lock somewhere in your hibernate.conf file to have KDE lock all sessions on suspend.

General Power Saving Ideas

In addition to the suspend to disk I did a few other things to prolong the life of my battery. I deviated away slightly from my "don't depend on the window manager" rule and used klaptop for now to have the system automatically suspend to RAM after 5 minutes of idle time when on battery. This is really only temporary until I find out a way to watch the keyboard and mouse for activity.

All I needed to do was catch two more ACPI events. One when the AC was removed, and the other when it was plugged back in, and have it run a script to set various power saving options. To do this I used the same tactic of tail -f /var/log/acpid to see what event codes were generated then made the rules and scripts. In /etc/acpid/ I made two separate scripts on called ac.sh for when the system is running on AC and the other called onbattery.sh for when AC is unplugged. The contents of ac.sh is:

# Sets the CPU to 1.8 GHz when on wall AC
#
echo -n 1800000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
#
# Set hard drive sleep time to 20 minutes
hdparm -S 240 /dev/hda
#
# Turn off laptop mode and wifi power management
sysctl -w vm.laptop_mode=0
iwconfig eth1 power off
My system only supports two CPU speeds, 1.8GHz and 1.2GHz. When I change to AC I want it running full speed and that's what the first line does. Then I set the hard drive sleep time to 20 minutes, turn off kernel laptop mode, and then turn of wifi power management. I don't fully understand the sysctl command but supposedly what this does is change the way the disk cache is flushed. In laptop mode it flushes less often causing the hard drive to spin up fewer times thus using less battery.

onbattery.sh is similar but it sets the CPU frequency to 1.2GHz, hard drive sleep time to 5 minutes, turns on laptop mode, and sets up wifi power saving.

# Sets the CPU to 1.2 GHz when on battery
#
echo -n 1200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
#
# Set Hard Drive sleep to 5 minutes
hdparm -S 60 /dev/hda
#
# Turn on laptop mode and set the wifi timeout and wake up periods
sysctl -w vm.laptop_mode=1
iwconfig eth1 power timeout 30
iwconfig eth1 power period 20
Remember, for the speedstep to work I had to load the speedstep_ich module.

I just created those scripts and put them in /etc/acpid and then created two rules to catch the battery and ac events, then to run the scripts. To make the rules just make two new files in /etc/acpid/events one that contains:

event=ac_adapter AC 00000080 00000001
action=/etc/acpi/ac.sh
and another than has:
event=ac_adapter AC 00000080 00000000
action=/etc/acpi/onbattery.sh
The event files don't need to be executable. They can't be hidden. And to make them take effect you have to /etc/rc.d/rc.acpid restart. So far, with these settings I get nearly as long of battery life in Linux as I did in Windows.



Automatically Hibernate when the Battery is Critical

To do this I initially tried to have klaptop called hibernate for me. I had setup sudo to allow anyone on the system to run the hibernate script without having to give the password. But, no matter what I did it never ran. I figured there had to be an easy way to have something run in the background that didn't depend on KDE. A bit of Googling got me this:

#!/bin/bash

LIMIT="1600" ## Suspend if battery level drops below this (in mAh/mWh)
SLEEP="60"  ## Seconds between each battery level check
BAT="BAT0" ## Part of path: /proc/acpi/battery/BAT/
HIBERNATE="/usr/local/sbin/hibernate" ## command used to suspend
#HIBERNATE="echo 4 > /proc/acpi/sleep"
ONBATTERY="/etc/acpi/onbattery.sh"
ONAC="/etc/acpi/ac.sh"

while [ true ]; do
  if [ -e "/proc/acpi/battery/$BAT/state" ]; then
     PRESENT=$(/bin/sed -ne "/present:/{s/^present:[ ]*\([a-z]*\)$/\1/p;q}" /proc/acpi/battery/$BAT/state)
     #echo $PRESENT
     if [ "$PRESENT" = "yes" ]; then
        STATE=$(/bin/sed -ne "/charging state:/{s/^charging state:[ ]*\([a-zA-Z]*\)$/\1/p;q}" /proc/acpi/battery/$BAT/state)
        BATTERY=$(/bin/sed -ne "/remaining capacity:/{s/^remaining capacity:[ ]*\([0-9]*\) m[WA]h$/\1/p;q}" /proc/acpi/battery/$BAT/state)
        #echo $BATTERY
        #echo $STATE
        if [ "$BATTERY" -lt "$LIMIT" ] && [ "$STATE" = "discharging" ]; then

           ## Comment out the following line if you don't
           ## want to log the event to system log:
           logger "Battery at ${BATTERY} mWh. Suspending to disk."
           #echo "Battery at ${BATTERY} mWh. Suspending to disk."

           ## Suspend:
           "$HIBERNATE"
        elif [ "$STATE" = "discharging" ]; then
           # Run script to set powersave settings while on battery
           "$ONBATTERY"
        elif [ "$STATE" = "charging" ] || [ "$STATE" = "charged" ] ; then
           # Run script to set power settings while on AC
           "$ONAC"
       fi
     fi
  fi
  sleep ${SLEEP}s
done
What this script does is checks the battery state in /proc/acpi/battery every 60 seconds and runs the hibernate script for you if the battery gets below the level you set as critical in the $LIMIT variable.

On final thing I noticed though is that when you hibernate while plugged in, unplug the power while hibernated, then wake the system that it never gets the event to run the on battery script. This left the system in a not power saving mode when it needed to be. The original script above didn't have the last two elif statements. I put those in to check the battery and run the power saving scripts as needed. So the worst case would be that the system would not be in power save mode for the max of one minute.

Just call the script whatever you want and set it to run at start up in whatever way works for your distribution. For Slackware I called it rc.batstate, put it in /etc/rc.d/ and made an entry in rc.local to run it in the background on start up.

Drive Bay Hot swapping

The utility to use is called Khotswap that sits in your system tray in KDE. The author also has a Gnome applet for it and it has a command line only version of itself just called hotswap and an X generic one called xhotswap.

Video

There really isn't to much to say about the video on this laptop. It uses a Radeon Mobility 7500 and X.org has a driver for it. Just use the generic Radeon driver and it works pretty well. Glxgears gives me about 250 FPS which is more than enough for my purposes. This is a work laptop after all.

As I've said elsewhere in this document I've heard of problems with suspending to disk when using the official Radeon drivers from ATI but I can say from experience that the X.org one works perfectly for both suspend to ram and suspend to disk. Well, at least it works for me.

Networking (Wireless and Wired)

Networking just worked for me. The 2.6.13 (and I think the 2.4.x) kernel support both the Intel Pro 100/VE and the AIRONET Wireless Cisco cards that are integrated into my system. If for some reason they aren't working for you just make sure the modules are loaded. For the Intel load 'eepro100' and for the Aironet load 'airo'. Like I said though, these just worked right after the Slackware install. Of course, if you have WEP or whatever on your network you have to use iwconfig to set the essid and WEP key.

Modem

From everything I've read all versions of the Thinkpad T30 have a winmodem of some sort. In my case it's an integrated Intel modem whose lspci output is:

00:1f.6 Modem: Intel Corporation 82801CA/CAM AC'97 Modem Controller (rev 02)
I've searched and it seems that not every T30 has the same model, but every reference indicates that they are winmodems. So if your modem doesn't match the lspci output above just visit Linmodems.org and follow their instructions. That's all I did to get this one working.

If your modem is exactly the same then just do the following:

  1. Go to Smartlink driver page and download the latest version of slmodemd for alsa. I used slmodem 2.9.9e. Explode the file and read the docs.
  2. modprobe in the snd_intel8x0m module
  3. Run slmodemd --alsa -c USA hw:1 & and it should tell you what device to use. Then use kppp, wxdial, or whatever utility you choose to configure the modem and establish a connection. I used kppp since I'm running KDE on this laptop anyway but I'll probably switch to wxdial eventually just to have one less thing depending on KDE.

Since I only use dial up as a very last resort I figured it doesn't make sense to have the module loaded and slmodemd running all the time. So I came up with this little script to setup the modem device and take it back down when not needed.

#/bin/bash

stop() {
echo "/bin/killall slmodemd"
/bin/killall slmodemd
sleep 5
/sbin/rmmod snd_intel8x0m
}


start() {
echo "/sbin/modprobe/snd-intel8x0m"
/sbin/modprobe snd-intel8x0m
echo "/usr/bin/slmodemd --alsa -c USA hw:1 &"
/usr/bin/slmodemd --alsa -c USA hw:1 &
}

restart() {
        stop
        start
}

case $1 in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                restart
                ;;
        *)
                start
                ;;
esac

exit $?
This has to be run as root. If you need to run it often maybe adding it to sudo would make sense.