#!/bin/sh ######################################################################### ## /etc/acpi/acpi_handler.sh ## ## This script handles all ACPI calls for IBM notebook ## It has CPU frequency handling, XOSD display, nice popups, ## working standby, hibernate, Fn+ function buttons... ## ## Written by Mirza Muharemagic ## for IBM ThinkPad X31 2672-CEG (with Bluetooth). ## ## BSD Licence ## ## Dependencies: ## - ACPI support in kernel ## - ibm_acpi ## - acpid (ACPI daemon - included in every distro) ## - laptop_mode tools (for CPU speed) ## - radeontool (for turning on/off the display) ## - zenity [in GNOME] or kdialog [in KDE] (for question popups) ## - xosd (for On Screen Display) ## - xscreensaver (for locking the display) ## ## Also usefull: ## - cpudyn (for CPUFreq) ## ## Default settings: ## - AC is attached: laptop_mode start ## - AC not attached: laptop_mode stop ## - Display closed & AC is attached: display off & lock display ## - Display closed & AC not attached: standby ## - Display open: display on (& wake up from standby if kernel >= 2.6.10) ## - Fn+F12: hibernate (ask) ## - Fn+F9: ethernet on/off (ask) ## - Fn+F8: wireless on/off (ask) ## - Fn+F7: turn on/off external display (ask) ## - Fn+F5: bluetooth on/off (ask) ## - Fn+F4: standby (ask) ## - Fn+F3: display on/off ## - Power button: shutdown (ask) ## ## Special additions: ## The following functions can have param "ask" passed to force them ## to ask the user whether s/he wants to proceed: ## * hibernate, standby, bluetooth, wireless, ## ethernet, power, display, external_display ## ## IMPORTANT: be sure that all needed additional apps are in the $PATH ## ## Test OS: Slackware 9.1 (last kernel I tested 2.6.11.5) ## but it can also be used with any other Linux distribution ############################################################################ ## USER CONFIGURATION ## desktop envirnoment: kde|gnome|else (used to draw "ask" dialog) DE=gnome ## needed for popups (zenity or kdialog) ## default user (needed just for some stuff here) USER=mm ## needed for (X) apps which need USER settings ## example: gnome with a specific theme ## encoding for popup messages LC_ENC=de_DE ## choose right encoding for you language ## do you want to log actions: no|yes SYSLOG=yes ## logs are in /var/log/messages ## xosd settings: use "man xosd_cat" for more info XOSD_SETTINGS="--font=-adobe-courier-medium-r-normal-*-*-120-*-*-m-*-iso10646-1 --colour=Green --shadow=3 --outline=3 --pos=bottom --align=left --offset=-41 --indent=25 --delay=3" ## MACs needed to find IP (just for OSD) MAC_WIRELESS=00:04:23:7E:BB:44 ## mac address of wireless card MAC_ETHERNET=00:0D:60:2D:19:B8 ## mac address of ethernet card ## values for force reduce cpu speed FORCE_REDUCE_TIMEOUT=180 ## for how many seconds to keep the CPU speed reduced FORCE_REDUCE_TMPFILE=/tmp/acpi_reduce_speed.tmp ## blank tmp file needed for forced scaling ## POPUP QUESTIONS - translate it in your language Q_TITLE="Was moechten Sie tun?" ## german umlauts don't work in zenity title Q_SHUTDOWN="Möchten Sie Ihren Computer herunterfahren?" Q_EXTERNAL_DISPLAY_OFF="Möchten Sie den externen Bildschirm-Ausgang ausschalten?" Q_EXTERNAL_DISPLAY_ON="Möchten Sie den externen Bildschirm-Ausgang einschalten?" Q_HIBERNATE="Möchten Sie Ihren Rechner wirklich in den Ruhezustand (Hibernate - Suspend to Disk) versetzen?" Q_STANDBY="Möchten Sie Ihren Rechner wirklich in das Standby versetzen?" Q_ETHERNET_OFF="Möchten Sie die Netzwerkkarte ausschalten?" Q_ETHERNET_ON="Möchten Sie die Netzwerkkarte einschalten und DHCP-Query senden?" Q_WIRELESS_OFF="Möchten Sie die drahtlose Verbindung deaktivieren?" Q_WIRELESS_ON="Möchten Sie die drahtlose Verbindung aktivieren?" Q_BLUETOOTH_OFF="Möchten Sie das Bluetooth deaktivieren?" Q_BLUETOOTH_ON="Möchten Sie das Bluetooth aktivieren?" Q_DISPLAY_OFF="Möchten Sie den Bildschirm ausschalten?" ## XOSD MESSAGES - translate it in your language X_AC_OFF="Netzkabel nicht angeschlossen" X_AC_ON="Netzkabel angeschlossen" X_EXTERNAL_DISPLAY_OFF="Externer Bildschirm ausgeschaltet" X_EXTERNAL_DISPLAY_ON="Externer Bildschirm eingeschaltet" X_ETHERNET_OFF="Netzwerkkarte ausgeschaltet" X_ETHERNET_ACTIVATING="Netzwerkkarte eingeschaltet. DHCP-Query wird gesendet..." X_ETHERNET_ON="Netzwerkarte eingeschaltet" X_WIRELESS_OFF="Drahtlose Verbindung deaktiviert" X_WIRELESS_ACTIVATING="Drahtlose Verbindung wird aktiviert..." X_WIRELESS_ON="Drathlose Verbindung aktiviert" X_NOIP_FOUND="+++ Keine IP-Adresse geholt +++" X_BLUETOOTH_OFF="Bluetooth deaktiviert" X_BLUETOOTH_ACTIVATING="Bluetooth wird aktiviert..." X_BLUETOOTH_ON="Bluetooth aktiviert" #################################################################### ## FUNCTIONS ## lock the screen with xscreensaver lock_screen() { syslog "Locking screen: xscreensaver-command -lock" start_as_user xscreensaver-command -lock } ## display state changed (open or closed) lid() { ## if ac_adapter is plugged in if [ `get_ac_state` == "on-line" ]; then ## lock screen and turn the display off if [ $1 == "off" ]; then lock_screen display off ## turn the display on else display on fi ## standby if ac_adapter is plugged out else if [ $1 == "off" ]; then standby else display on fi fi } ## if ac adapter not connected, start laptop_mode ## if ac adapter is connected, stop laptop_mode ac_adapter() { if [[ $1 ]] && [[ $1 == "on" ]]; then syslog "AC adapter plugged in." xosd $X_AC_ON laptop_mode stop >/dev/null 2>&1 /etc/rc.d/rc.cpudyn stop > /dev/null 2>&1 else ## needed if running ac_adapter() w/o params if [[ $1 == "off" ]] || [[ ! $1 && `get_ac_state` == "off-line" ]]; then syslog "AC adapter plugged out." xosd $X_AC_OFF laptop_mode start /etc/rc.d/rc.cpudyn start > /dev/null 2>&1 fi fi } ## reduce CPU speed forcefully for 180 seconds reduce_speed() { if [ ! -e $FORCE_REDUCE_TMPFILE ]; then let "AGE_ACPI_TMP=$FORCE_REDUCE_TIMEOUT+1" else let "AGE_ACPI_TMP=`date +%s`-`stat -c %Y $FORCE_REDUCE_TMPFILE`" fi if [ $AGE_ACPI_TMP -gt $FORCE_REDUCE_TIMEOUT ]; then syslog "Starting Force Speed Reduce. Waiting $FORCE_REDUCE_TIMEOUT seconds to encrease CPU speed" ## touching a file not to start this func twice touch $FORCE_REDUCE_TMPFILE CURR_SPEED=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq` CURR_GOVERNOR=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor` echo -n 600000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq echo -n powersave > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor sleep $FORCE_REDUCE_TIMEOUT syslog "Stopping Force Speed Reduce" echo -n $CURR_SPEED > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq echo -n $CURR_GOVERNOR > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor fi } ## - if you pass param "ask", it will ask you whether you want to power off computer ## - running it without parameters will just power off without asking power() { start_as_user "gnome-session-save --kill" ANSWER=`ask_user "$1" "$Q_SHUTDOWN"` if [[ $ANSWER -eq 0 ]]; then syslog "Shutting down..." chvt 1 shutdown -h now fi } ## get state of ac adapter (on-line or off-line) get_ac_state() { echo `cat /proc/acpi/ac_adapter/AC/state | awk '{ print $2 }'` } ## - if you pass param "ask", it will ask you whether you want to hibernate computer ## - running it without parameters will just hibernate without asking hibernate() { ANSWER=`ask_user "$1" "$Q_HIBERNATE"` if [[ $ANSWER -eq 0 ]]; then syslog "Suspending to Disk..." AC_STATE_OLD=`get_ac_state` FGCONSOLE=`fgconsole` ## use these commands only in X (console > 6) if [ $FGCONSOLE -gt 6 ]; then lock_screen chvt 1 fi umount -a -r -t nfs,smbfs bluetooth off sync echo -n disk > /sys/power/state ## WHAT TO DO AFTER WAKING UP? hwclock --hctosys display on chvt $FGCONSOLE ## this checks, did you connect your ac_adapter ## to your notebook while notebook was "sleeping" AC_STATE_NEW=`get_ac_state` if [ $AC_STATE_OLD != $AC_STATE_NEW ]; then if [ $AC_STATE_NEW == "on-line" ]; then ac_adapter on else ac_adapter off fi fi fi } ## - if you pass param "ask", it will ask you whether you want to suspend computer ## - running it without parameters will just suspend without asking standby() { ANSWER=`ask_user "$1" "$Q_STANDBY"` if [[ $ANSWER -eq 0 ]]; then syslog "Suspending to RAM..." AC_STATE_OLD=`get_ac_state` FGCONSOLE=`fgconsole` ## use these commands only in X (console > 6) if [ $FGCONSOLE -gt 6 ]; then syslog "Changed to VT 1" lock_screen chvt 1 fi umount -a -r -t nfs,smbfs bluetooth off sync display off echo -n mem > /sys/power/state ## WHAT TO DO AFTER WAKING UP? hwclock --hctosys chvt $FGCONSOLE chvt $FGCONSOLE display on display on ## this checks, did you connect your ac_adapter ## to your notebook while notebook was "sleeping" AC_STATE_NEW=`get_ac_state` if [ $AC_STATE_OLD != $AC_STATE_NEW ]; then if [ $AC_STATE_NEW == "on-line" ]; then ac_adapter on else ac_adapter off fi fi fi } ## turn on/off the external display ## - if you pass param "ask", it will ask you whether you want to turn on/off the device ## - running it without parameters will just turn on or off the device external_display() { radeontool dac | grep on$ >/dev/null STATUS=$? check_status "$1" "$STATUS" ANSWER=`ask_user "$1" "$Q_EXTERNAL_DISPLAY_OFF" "$Q_EXTERNAL_DISPLAY_ON" "$STATUS"` if [[ $ANSWER -eq 0 ]]; then if [ $STATUS -eq 0 ]; then syslog "External video output disabled" xosd $X_EXTERNAL_DISPLAY_OFF radeontool dac off else syslog "External video output enabled" xosd $X_EXTERNAL_DISPLAY_ON radeontool dac on fi fi } ## get IP thru mac address get_ip() { echo `ifconfig -a | grep -A 1 $1 | grep 'inet addr' | sed -re 's/^(.*addr:)(.*?)( Bcast.*)/\2/g'` } ## turn on/off ethernet card and get ip thru dhcp. ## - if you pass param "ask", it will ask you whether you want to turn on/off the device ## - running it without parameters will just turn on or off the device ethernet() { lsmod | grep eepro100 >/dev/null STATUS=$? check_status "$1" "$STATUS" ANSWER=`ask_user "$1" "$Q_ETHERNET_OFF" "$Q_ETHERNET_ON" "$STATUS"` if [[ $ANSWER -eq 0 ]]; then if [ $STATUS -eq 0 ]; then syslog "Deactivating ethernet" xosd $X_ETHERNET_OFF rmmod eepro100 else syslog "Activating ethernet" xosd $X_ETHERNET_ACTIVATING modprobe eepro100 ETH_DEVICE=`ifconfig -a | grep eth | grep $MAC_ETHERNET | awk '{ print $1 }'` sleep 2 dhcpcd -t 10 $ETH_DEVICE IP=`get_ip $MAC_ETHERNET` if [ ! $IP ]; then xosd $X_ETHERNET_ON: $X_NOIP_FOUND else xosd $X_ETHERNET_ON: $IP fi fi fi } ## turn on/off wireless card ## - if you pass param "ask", it will ask you whether you want to turn on/off the device ## - running it without parameters will just turn on or off the device wireless() { lsmod | grep ipw2100 >/dev/null STATUS=$? check_status "$1" "$STATUS" ANSWER=`ask_user "$1" "$Q_WIRELESS_OFF" "$Q_WIRELESS_ON" "$STATUS"` ## try to turn it off only if it is turned on if [[ $ANSWER -eq 0 ]]; then if [[ $1 == "off" ]] || [[ $STATUS -eq 0 ]]; then syslog "Wireless Card turned off" xosd $X_WIRELESS_OFF rmmod ipw2100 killall -9 wlan >/dev/null 2>&1 rmmod ieee80211 ieee80211_crypt_wep ieee80211_crypt else syslog "Wireless Card turned on" xosd $X_WIRELESS_ACTIVATING modprobe ipw2100 modprobe ieee80211_crypt_wep start_as_root /etc/scripts/wlan IP=`get_ip $MAC_WIRELESS`; if [ ! $IP ]; then if [ lsmod | grep ipw2100 >/dev/null -eq 1 ]; then xosd $X_WIRELESS_OFF else xosd $X_WIRELESS_ON: $X_NOIP_FOUND fi else xosd $X_WIRELESS_ON: $IP fi fi fi } ## turn on/off bluetooth ## - if you pass param "ask", it will ask you whether you want to turn on/off the device ## - running it without parameters will just turn on or off the device bluetooth() { MODULES="rfcomm bnep l2cap hci_usb bluetooth uhci_hcd ohci_hcd ehci_hcd usbcore" cat /proc/acpi/ibm/bluetooth | grep enabled >/dev/null STATUS=$? check_status "$1" "$STATUS" ANSWER=`ask_user "$1" "$Q_BLUETOOTH_OFF" "$Q_BLUETOOTH_ON" "$STATUS"` if [[ $ANSWER -eq 0 ]]; then if [[ $1 == "off" ]] || [[ $STATUS -eq 0 ]]; then syslog "Deactivating bluetooth" xosd $X_BLUETOOTH_OFF hciconfig hci0 down > /dev/null 2>&1 echo disable > /proc/acpi/ibm/bluetooth killall -9 hcid sdpd kbluetoothd gnome-obex-server gnome-bluetooth-manager >/dev/null 2>&1 for MODULE in `echo $MODULES` ; do rmmod $MODULE >/dev/null 2>&1 done else syslog "Activating bluetooth" xosd $X_BLUETOOTH_ACTIVATING echo enable > /proc/acpi/ibm/bluetooth for MODULE in `echo $MODULES` ; do modprobe $MODULE >/dev/null 2>&1 done sleep 1 hciconfig hci0 up >/dev/null 2>&1 hcid -f /etc/bluetooth/hcid.conf sdpd xosd $X_BLUETOOTH_ON #if [[ $COMMAND == "ask" && `ask_user $Q_RUN_BLUETOOTHD` -eq 0 ]]; then # start_as_user /opt/kde/bin/kbluetoothd ## menu in tray doesn't function # start_as_user 'konqueror sdp://[00:0e:ed:5f:83:32]/' #fi #start_as_user gnome-obex-server & #start_as_user gnome-bluetooth-manager & fi fi } ## turn display on/off ## - you can pass params: "on" or "off" ## - running it without parameters will just turn on or off the device display() { radeontool light | grep on$ >/dev/null STATUS=$? check_status "$1" "$STATUS" ANSWER=`ask_user "$1" "$Q_DISPLAY_OFF" "$STATUS"` if [[ $ANSWER -eq 0 ]]; then if [[ $1 == "off" ]] || [[ $STATUS -eq 0 ]]; then syslog "Display off" radeontool light off else syslog "Display on" radeontool light on fi fi } ## check whether params proceeded to commands are useless ## example: param == "on" and device is already turned on check_status() { PARAM="-$1" [[ $PARAM == "-ask" ]] && return if [[ $1 == "-on" && $2 -eq 0 ]] || [[ $1 == "-off" && $2 -eq 1 ]]; then exit fi } ## show XOSD messages ## - param: text to show xosd() { [[ `fgconsole` -lt 7 ]] && return TEXT="$*" killall -9 osd_cat >/dev/null 2>&1 export DISPLAY=:0.0 start_as_user "echo '$TEXT' | osd_cat ${XOSD_SETTINGS} &" ## if something wrong in XOSD settings, show error if [ $? -eq 1 ]; then XOSD_DEFAULT_SETTINGS="--colour=Green --outline=3 --pos=middle --align=center --offset=-41" start_as_user "echo 'Error in your XOSD settings.' | osd_cat ${XOSD_DEFAULT_SETTINGS} &" fi } ## it shows "ask" dialog to user depending on his/her DE (gnome or kde) ## other users will not be asked. "yes" will be asumed ## params: ## - param 1: it has to be "-ask". everything else will be ignored ## - param 2: off-question like "do you want to turn it off" ## - param 3: on-question like "do you want to turn it on" or status (if there is no on-question) ## - param 4: status 0 or 1 (0=on, 1=off) ask_user() { ASK="-$1" QUESTION_OFF=$2 QUESTION_ON=$3 STATUS=$4 [[ $ASK != "-ask" ]] && return if [ ! $STATUS ]; then STATUS=$QUESTION_ON unset QUESTION_ON fi # if not in X (VT < 7), "yes" will be asumed if [[ `fgconsole` -lt 7 ]]; then echo 0 else if [ $DE == "gnome" ]; then if [[ $STATUS -eq 0 ]]; then start_as_user "zenity --question --title='$Q_TITLE' --text='$QUESTION_OFF'" else if [[ ! $QUESTION_ON ]]; then return else start_as_user "zenity --question --title='$Q_TITLE' --text='$QUESTION_ON'" fi fi else if [ $DE == "kde" ]; then if [[ $STATUS -eq 0 ]]; then start_as_user "kdialog --yesno '$QUESTION_OFF' --title '$Q_TITLE'" else if [[ ! $QUESTION_ON ]]; then return else start_as_user "kdialog --yesno '$QUESTION_ON' --title '$Q_TITLE'" fi fi else return fi fi fi } ## run X application as user ## - param: command to run start_as_user() { syslog "Running as $USER: $*" su - $USER -c "env DISPLAY=:0.0 LC_ALL=$LC_ENC HOME=/home/$USER $* " >/dev/null 2>&1 echo $? } ## run X application as root ## - param: command to run start_as_root() { syslog "Running as ROOT: $*" su - root -c "env DISPLAY=:0.0 LC_ALL=$LC_ENC HOME=/root $* " >/dev/null 2>&1 echo $? } ## syslog everything to /var/log/messages ## - param: text to log syslog() { if [ $SYSLOG == "yes" ]; then logger ' ' logger "****************************************" logger "ACPI: $*" logger "****************************************" fi } ## logging all passed params if [ $SYSLOG == "yes" ]; then syslog $* fi case "$1" in ## I ignore other calls like battery, processor, button/sleep, button/lid ## because they are not needed (at the moment) #button/sleep|button/lid|battery|processor) button/power|ac_adapter) case "$2" in ## power button pressed PWRF) power ask;; ## ac-adapter plugged in or out AC) case $4 in ## ac_adapter plugged out 00000000) ac_adapter off;; ## ac_adapter plugged in 00000001) ac_adapter on;; esac ;; esac ;; ibm/hotkey) case "$4" in ## Fn+F12: Hibernate on/off 0000100c) hibernate ask;; ## Fn+F9 : LAN on/off 00001009) ethernet ask;; ## Fn+F8 : WLAN on/off 00001008) wireless ask;; ## Fn+F7 : external display on/off 00001007) external_display ask;; ## Fn+F5 : BLUETOOTH on/off 00001005) bluetooth ask;; ## Fn+F4 : STANDBY 00001004) standby ask;; ## Fn+F3 : DISPLAY on/off 00001003) display;; ## LID (closed display) + STANDBY (if AC adapter not connected) 00005001) lid off;; ## LID (open display) 00005002) lid on;; esac ;; ## extra options for some external applications extra) case "$2" in ## $3 can be "ask", "on", "off" or nothing ## if there is no $3, device will be turned off if it is turned on etc. bluetooth|wireless|ethernet|display|external_display) if [[ ! $3 ]] || [[ $3 == "on" ]] || [[ $3 == "off" ]] || [[ $3 == "ask" ]]; then $2 $3 fi;; ## $3 can be "ask" or nothing ## if there is no $3, command will be run w/o asking standby|hibernate|power) if [[ ! $3 ]] || [[ $3 == "ask" ]]; then $2 $3 fi;; ac_adapter|get_ac_state|reduce_speed|get_forced_acpi_timeout) $2;; esac ;; esac