#!/bin/bash

################################################################################
# BASH script to fake suspend mode on the Fujitsu Siemens Pocket Loox 720
# written by Harald Radke (harryrat@gmx.de)
# - records state of pc card devices, bluetooth and cpu freq and policy
#   and powers them down as well as turning off LCD
#   additionally "noncritical" processes get the STOP signal
# - restores the previous settings when waking up
# - use it with powertoggle-agent, pass script path to it
# USE IT ON YOUR OWN RISK, I DON'T TAKE RESPONSEBILITY FOR ANY DAMAGE CAUSED
#            (by actually using this script you agree (-: )
###############################################################################

# this is EXPERIMENTAL an thus by default DISABLED!
doStopProcesses=off


# indices of the session[] array for known elements
POWER=0
PCCARD0=1
PCCARD1=2
BT0=3
CPU_GOV=4
CPU_FREQ=5

#if session[PIDs] == 'PIDs" the following entries are process PIDs
PIDs=6

# replace <NAMEx> by commands u don't want to be stopped (more than 3 possible)
# when scanning processes to send SIGSTOP those are left out
# following processes are implicitly filtered out
# - init and all session 1 processes (kernel stuff)
# - this script and programs run by it (those will be gone anyways)
# - the powerbutton-agent

# nonStoppableProcesses=$(echo "<NAME1> <NAME2> <NAME3>" | tr -s " " "\n")

# initial setting is "on", on boot there is no log file in /var/run 
session[$POWER]=on

# ==============================================================================
# ====               read current state from log file                       ====
# ==============================================================================
if [ -r /var/run/hibernate_loox.status ]; then
	read  word < /var/run/hibernate_loox.status
	if [ $word == off ]; then # only need to read settings when coming up
		i=0
		while read word; do 
			session[$i]=$word 
			i=$(( i+1 )); 
		done < /var/run/hibernate_loox.status
	fi
fi


# ==============================================================================
if [ ${session[$POWER]} == on ]; then  #    ====    prepare hibernation     ====
# ==============================================================================
# >>> collect current devices status and send them to sleep  >>>>>>>>>>>>>>>>>>>
# ---- PCCARDS -----------------------------------------------------------------
# the Loox actually has 2 sockets, one visible for those nice add-on cards, one
# hidden, the Wifi device is "in" it...though leaded in, it's handled as a 
# removeable device

statusPCCard=$(pccardctl status | tr -s "\n" " ")
statusPCCard=$(echo $statusPCCard | tr -s " " "-")

# ---- socket 0 -------------------------
socket0=${statusPCCard%%Socket-1:*}
socket0=${socket0##*Socket-0:}
if [[ $socket0 == "-no-card-" ]]; then # ---- don't turn on card next wakeup
	session[$PCCARD0]=ejected
else # ---- let see if card is actually running or suspended
	if [[ $socket0 == *suspended* ]]; then
		session[$PCCARD0]=off # ---- card is in slot but suspended
	else
		session[$PCCARD0]=on # ---- card is inserted and running	
		pccardctl suspend 0
	fi
fi # ---------------- socket 0 done ----

# ---- socket 1 -------------------------
socket1=${statusPCCard##*Socket-1:}
if [[ $socket1 == "-no-card" ]]; then # ---- don't turn on card next wakeup
	session[$PCCARD1]=ejected
else # let see if card is actually running or suspended
	if [[ $socket1 == *suspended* ]]; then
		session[$PCCARD1]=off # card is in slot but suspended
	else
		session[$PCCARD1]=on # card is inserted  and running	
		pccardctl suspend 1
	fi
fi # ---------------- socket 1 done ----
# ----------------------------------------------------------- PCCARDS done ----

# ---- BLUETOOTH --------------------------------------------------------------
# Now this is kinda ugly as it basically is nothing more than kill the 
# hcitattach process and marks it to be restarted during wakeup. 
# No elegant use of device features...
# Also only focussing on INTERNAL BT device, no USB dongles
# (well at least PCCard is already covered *g*)

hciattachPID=$(ps ax -o comm,pid| grep hciattach| tr -s " " | cut -d " " -f 2)
if [[ -n $hciattachPID ]]; then # ok, BT device is active
	session[$BT0]=on
	/bin/kill $hciattachPID
else # ok, no attach, no connect, no power (-:
	session[$BT0]=off
fi
# ---------------------------------------------------------- BLUETOOTH done ----

# ---- LCD ---------------------------------------------------------------------
echo 1 > /sys/class/backlight/corgi-bl/power
echo 1 >  /sys/class/graphics/fb0/blank
# ---------------------------------------------------------------- LCD done ----


# ---- CPU log -----------------------------------------------------------------
# get frequency  and policy - actual frequency will be logged but only restored
# if scaling governor == userspace !
# actual powerdown mode is last action for hybernation, see below

session[$CPU_GOV]=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)
session[$CPU_FREQ]=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq)
# ------------------------------------------------------------ CPU log done ----

# that's it for now...maybe later USB? Or things I forgot (-:
# <<<<<<<<<<<<<<collect current devices status and send them to sleep done <<<<

# >>>> processes sending the STOP signal >>>>>>>>>>>>>>>>>>>>>>> EXPERIMENTAL!!
# if you want that to be activated set doStopProcesses to "on" at the beginning
# of the script
if [[ $doStopProcesses == on ]]; then 

# get current process without explicitly excluded ones (see at script start)
	if [[ -n $nonStoppableProcesses ]]; then 
		prString=$(ps axh -o comm,pid,sid,ppid | grep -v -F "$nonStoppableProcesses" | tr -s " ")
	else
		prString=$(ps axh -o comm,pid,sid,ppid | tr -s " ")
	fi
# arrays for parent own and session IDs
	ppidString=$(echo "$prString"  | cut -d " " -f 4)
	pidString=$(echo "$prString"  | cut -d " " -f 2)
	sidString=$(echo "$prString" | cut -d " " -f 3)
	pids=( $(echo $pidString))
	sids=( $(echo $sidString))
	ppids=( $(echo $ppidString))
	agentPID=$(cat /var/run/powerbutton-agent.pid) 
	session[$PIDs]=PIDs
	i=0;
	j=$(( $PIDs + 1 ))
	while [[ -n ${pids[$i]} ]]; do
	        if [[ ${sids[$i]} > 1 ]]; then # exclude kernel stuff
	                if [[ ${ppids[$i]} != $$ && ${pids[$i]} != $$ && ${pids[$i]} != $agentPID ]]; then
			# exclude this script and progs started from it
				session[$j]=${pids[$i]};
				/bin/kill -STOP ${pids[$i]}
			        j=$(( j + 1 ))
	                fi
	        fi
	        i=$(( i + 1 ))
	done
fi
# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< processes sending the STOP signal done <<<<

# >>> write system and devices status in log file >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if [ -r /var/run/hibernate_loox.status ]; then
	rm /var/run/hibernate_loox.status
fi
# current session is now "off" so mark it
session[$POWER]=off
i=0;
while [[ -n ${session[$i]} ]]; do
	echo ${session[$i]} >> /var/run/hibernate_loox.status
	i=$(( i + 1))
done

# ensures file has been updated!
sync

# ---- CPU power down ----------------------------------------------------------
# set to powersave, last action (-:
echo powersave > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# ------------------------------------------------------- CPU powerdown done ---

# hibernation done! ============================================================

# ==============================================================================
else # ====           wake up and restore the previous session              ====
# ==============================================================================
# ---- CPU ---------------------------------------------------------------------
# set scaling goovernor ... if it was userdefined, also restore frequency

echo ${session[$CPU_GOV]} > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
if [ ${session[$CPU_GOV]} == userspace ]; then 
	echo ${session[$CPU_FREQ]} > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
fi

# ---- LCD ---------------------------------------------------------------------
# ... with background light on, no matter what previous state was
echo 0 >  /sys/class/graphics/fb0/blank
echo 0 > /sys/class/backlight/corgi-bl/power
# ---------------------------------------------------------------- LCD done ----

# ---- BLUETOOTH ---------------------------------------------------------------
# turn on INTERNAL Bluetooth device
if [ ${session[BT0]} == on ]; then
	hciattach ttyS1 texas
fi
# ---------------------------------------------------------- BLUETOOTH done ----

# ---- PCCARDS -----------------------------------------------------------------
if [ ${session[PCCARD0]} == on ]; then
	pccardctl resume 0
fi
if [ ${session[PCCARD1]} == on ]; then
	pccardctl resume 1
fi
# ------------------------------------------------------------ PCCARDS done ----

if [[ $doStopProcesses == on ]]; then # EXPERIMENTAL!!! 
                                     # set doStopProcess to 'on' if wanted!
	if [[ ${session[$PIDs]} == PIDs ]]; then # ok, we have saved PIDs to wakeup
		i=$(( $PIDs + 1 )) # first PID
		while [[ -n ${session[$i]} ]]; do
			/bin/kill -CONT ${session[$i]}
			i=$(( i + 1 ))
		done
	fi
# and send them signal -CONT (all? save?)
# ...
fi

# store status "on" in log file, rest is not necessart
echo on > /var/run/hibernate_loox.status

# =========================================================== wake up done! ====
fi

