# -*- sh -*-
# vim:ft=sh:ts=8:sw=4:noet

SWSUSP_INTERFACE_VERSION_MIN=15
SWSUSP_INTERFACE_VERSION_MAX=15

# Detect the new /proc root.
if [ -d "/proc/software_suspend/" ] ; then
    SWSUSP_ROOT="/proc/software_suspend"
else
    SWSUSP_ROOT="/proc/swsusp"
fi

AddConfigHandler Swsusp2ConfigEnabler
AddOptionHandler Swsusp2OptionHandler

AddConfigHelp "UseSwsusp2 <boolean>" "Enable/disable the use of Software Suspend 2 to suspend."

AddConfigHelp "Reboot <boolean> (requires UseSwsusp2 on)" "Controls whether or not Software Suspend 2 should reboot after suspending. Setting this to yes will force a reboot. Setting this to no will force shutting down. Omitting this option will leave rebooting behaviour unchanged."
AddShortOption "r::"
AddLongOption "reboot::"
AddOptionHelp "-r[0|1], --reboot[=<0|1>] (requires UseSwsusp2 on)" "If 0 is specified, disables rebooting after suspend, regardless of the Reboot option in the configuration file. If 1 or omitted, will force a reboot after suspending."

AddConfigHelp "EnableEscape <boolean> (requires UseSwsusp2 on)" "If specified, allows (or disallows) the use of the escape key to abort a suspend. If this line is not specified, the Escape key's behaviour is unchanged."

AddConfigHelp "DefaultConsoleLevel N (requires UseSwsusp2 on)" "If specified, sets the default console level. 0 gives a progress bar. 1 gives a progress bar with numbers. 2 or higher give increasing amounts of debugging. If this line is not specified, the default console level is unchanged."

AddConfigHelp "Swsusp2AllSettings <params> (requires UseSwsusp2 on)" "If specified, sets all of the settings for Software Suspend 2 at once. The parameter should be the contents of $SWSUSP_ROOT/all_settings after you have set your desired settings manually."

AddShortOption "n"
AddLongOption "no-suspend"
AddOptionHelp "-n, --no-suspend (requires UseSwsusp2 on)" "Disables actually suspending the system. This is useful for testing the suspend script itself."

Swsusp2ConfigEnabler() {
    [ "$1" != "useswsusp2" ] && return 1
    BoolIsOn "$1" "$2" || return 0
    [ -n "$USING_SWSUSP2" ] && return 0
    AddConfigHandler Swsusp2ConfigOptions
    AddSuspendHook 10 EnsureSwsusp2Capable
    AddSuspendHook 99 DoSwsusp2
    USING_SWSUSP2=1
    return 0
}

Swsusp2OptionHandler() {
    # Don't parse options if we're not being used.
    [ -z "$USING_SWSUSP2" ] && return 1

    case $1 in
	-r|--reboot)
	    if [ -z "$2" ] ; then
		# Cmd-line options will always override config file
		OPT_REBOOT=1
	    else
		BoolIsOn "-r" "$2" && OPT_REBOOT=1 || OPT_REBOOT=0
	    fi
	    ;;
	-n|--no-suspend)
	    SWSUSP2_NO_SUSPEND=1
	    ;;
	*)
	    return 1
    esac
    return 0
}

Swsusp2ConfigOptions() {
    case $1 in
	reboot)
	    if [ -z "$OPT_REBOOT" ] ; then
		BoolIsOn "$1" "$2" && OPT_REBOOT=1 || OPT_REBOOT=0
	    fi
	    ;;
	enableescape)
	    if [ -z "$OPT_ESCAPE" ] ; then
		BoolIsOn "$1" "$2" && OPT_ESCAPE=1 || OPT_ESCAPE=0
	    fi
	    ;;
	defaultconsolelevel)
	    if [ -z "$OPT_CONSOLELEVEL" ] ; then
		BoolIsOn "$1" "$2" && OPT_CONSOLELEVEL=1 || OPT_CONSOLELEVEL=0
	    fi
	    ;;
	swsusp2allsettings)
	    if [ -z "$OPT_ALLSETTINGS" ] ; then
		BoolIsOn "$1" "$2" && OPT_ALLSETTINGS=1 || OPT_ALLSETTINGS=0
	    fi
	    ;;
	*)
	    return 1
    esac
    if [ -z "$SWSUSP2_HOOKED" ] ; then
	AddSuspendHook 98 Swsusp2ConfigSet
	SWSUSP2_HOOKED=1
    fi
    return 0
}

Swsusp2ConfigSet() {
    # In earlier versions, /proc/swsusp/reboot was only available when
    # compiled with debugging. Unfortunately the interface version didn't
    # change when this was made available always :(
    [ -n "$OPT_REBOOT" ] && [ -f "$SWSUSP_ROOT/reboot" ] && \
	echo $OPT_REBOOT > $SWSUSP_ROOT/reboot

    [ -n "$OPT_ESCAPE" ] && echo $OPT_ESCAPE > $SWSUSP_ROOT/enable_escape
    [ -n "$OPT_CONSOLELEVEL" ] && echo $OPT_CONSOLELEVEL > $SWSUSP_ROOT/default_console_level
    [ -n "$OPT_ALLSETTINGS" ] && echo $OPT_ALLSETTINGS > $SWSUSP_ROOT/all_settings
    return 0
}

DoSwsusp2() {
    local error
    if [ -z "$SWSUSP2_NO_SUSPEND" ] ; then
	vecho 1 "$EXE: Activating suspend ..."
	error=`DoSuspend` && SUSPEND_SUCCESS=1 || SUSPEND_SUCCESS=0
	PrintErrors $error
    else
	vecho 1 "$EXE: Not actually suspending (--no-suspend given)"
    fi
    return 0
}

# DoSuspend: activates the suspend and echo's the error code to be processed by
# PrintErrors. We can't just return it, because error codes may be > 255
DoSuspend() {
    echo > $SWSUSP_ROOT/activate
    read REPLY < $SWSUSP_ROOT/last_result
    echo $REPLY
    return 0
}

# PrintAnErrorString <bit value>: prints a string corresponding to the relevant
# bit in an error code. These match up with the SUSPEND_* values in
# include/linux/suspend-debug.h
PrintAnErrorString() {
    case "$1" in
	0) echo "Suspend was aborted."
	;;
	1) echo "Suspend was aborted by user."
	;;
	2) echo "No swapspace was available. Try swapon?"
	;;
	3) echo "Not enough swapspace available. Free some memory?"
	;;
	4) echo "Freezing processes failed. Wacky driver problems :( (see dmesg?)"
	;;
	5) echo "Internal error - an unexpected memory allocation occured. Driver problems? (see dmesg?)"
	;;
	6) echo "Image has been kept as requested."
	;;
	7) echo "Suspend would have eaten memory. Yumm...."
	;;
	8) echo "Unable to free enough memory."
	;;
	*) echo "Unknown error! (see dmesg?)"
	;;
    esac
    return 0
}

# PrintErrors <last_result value>: prints the list of errors associated with a
# given result code
PrintErrors() {
    local code
    local bit_num

    code="$1"
    [ "$code" -eq 0 ] && return 0

    echo "$EXE: Suspend was not completed because:"

    # A special case for "suspend aborted" and "suspend aborted by user"
    # because it'd look silly to have both.
    if [ $(($code&3)) -ne 0 ] ; then
	echo -n " - " && PrintAnErrorString 1 # aborted by user
	return 0
    fi

    bit_num=0
    while [ "$code" -ne 0 ] ; do
	[ "$((code&1))" -eq 1 ] && echo -n " - " && PrintAnErrorString "$bit_num"

	bit_num=$(($bit_num+1))
	code=$(($code>>1))
    done
    return 0
}

# GetInterfaceVersion: returns the interface version of the swsusp
# implementation in the kernel. This returns 0 if there is no swsusp in the
# kernel (note, this is interpreted as "true" in shell-speak)
GetInterfaceVersion() {
    [ -d "$SWSUSP_ROOT" ] || return 0
    [ -f "$SWSUSP_ROOT/interface_version" ] || return 0
    read REPLY < $SWSUSP_ROOT/interface_version
    return $REPLY
}

# EnsureSwsusp2Capable: makes sure we have a swsusp-capable kernel. Stops the
# script with an error message if we don't. Also ensure's we have root -
# (even though the actual suspend might not need it, everything else will)
EnsureSwsusp2Capable() {
    # Test for *any* recent swsusp support
    local ver
    GetInterfaceVersion
    ver=$?
    if [ "$ver" -eq 0 ] ; then
	cat <<EOT
Your kernel does not have any recent Software Suspend 2 support compiled in.
Please follow the HOWTO linked from http://softwaresuspend.berlios.de/ for
instructions on how to compile Software Suspend into your kernel.
EOT
	return 2
    fi

    # Check the interface version matches. Warn if it doesn't.
    if [ $ver -lt "$SWSUSP_INTERFACE_VERSION_MIN" -o $ver -gt "$SWSUSP_INTERFACE_VERSION_MAX" ] ; then
	cat <<EOT
WARNING: The swsusp2 scriptlet was written for a different swsusp2 interface
	 version from the one in your running kernel. This scriptlet was
	 written for version between $SWSUSP_INTERFACE_VERSION_MIN and $SWSUSP_INTERFACE_VERSION_MIN where as the version running
	 in your kernel is $ver. Things may not work as expected, but proceeding
	 anyway ...

EOT
    fi

    # If we have the resume2 proc entry, we can do some further tests
    # Otherwise, bail gracefully
    [ -f $SWSUSP_ROOT/resume2 ] || return 0

    # See if we have been given a resume2 line
    read REPLY < $SWSUSP_ROOT/resume2
    if [ -z "$REPLY" ] ; then
	cat <<EOT
You haven't specified a resume2= parameter on your kernel command line

Your GRUB or LILO config should have something like resume2=swap:/dev/hdaX
where /dev/hdaX is your swap partition. You will then need to either reboot
after doing so or set it manually (this time only) using:
    echo swap:/dev/hdaX > $SWSUSP_ROOT/resume2
EOT
	return 2
    fi
    return 0
}

# $Id: swsusp2_15 347 2004-07-29 20:18:29Z dagobah $
