#! /bin/sh
#
# Laptop mode tools module: adjust hard drive powermanagement settings
#
# This is a core module that takes its configuration from the main config
# file.
#

#
# Function for drive capability check. This prevents ugly errors in
# the kernel output about unsupported commands.
#
# $1 = drive name
# $2 = capability (SDPARM/HDPARM or IDLE_TIMEOUT/POWERMGMT/WRITECACHE)


UDEVADM=`which udevadm 2>/dev/null`;
UDEVINFO=`which udevinfo 2>/dev/null`;

is_capable() {
	local dev=${1#/dev/}
	local MEDIA=
	local BUS=

	# Make sure the drive exists before checking anything.
	if ! [ -e $1 ]; then
		return 1;
	fi

	# If the disk is an SSD, we skip any of the power mgmt features, for now.
	SYSFS_DEV=/sys/block/$dev
	if [ -f $SYSFS_DEV/queue/rotational ]; then
		IS_ROTATIONAL=`cat $SYSFS_DEV/queue/rotational`
	else
		log "VERBOSE" "Disk $THISHD does not support the rotational interface"
		# We'll apply the traditional drive assumptions here.
		IS_ROTATIONAL=1
	fi

	if [ "$IS_ROTATIONAL" -eq 0 ]; then
		log "VERBOSE" "Disk $THISHD is a NAND (SSD / Flash) Device. Not apply power management savings"
		return 1
	fi

	# If we are running udev, this is the most portable way
	# It assumes more or less recent udev (> 070)
	if [ $HAVE_UDEVINFO -ne 0 ] ; then
		log "VERBOSE" "Querying $1 media type using udevinfo: "
		if [ -x "$UDEVADM" ]; then
			eval "$(udevadm info -q env -n $1 | egrep '(ID_TYPE=|ID_BUS=)' )"
		else
			eval "$(udevinfo -q env -n $1 | egrep '(ID_TYPE=|ID_BUS=)' )"
		fi
		if [ -n "$ID_TYPE" -a -n "$ID_BUS" ] ; then
			log "VERBOSE" "type '$ID_TYPE' on bus '$ID_BUS' detected"
			MEDIA=$ID_TYPE
			BUS=$ID_BUS
		else
			log "ERR" "failed - udev not active?"
		fi
	fi

	if [ -z "$MEDIA" ] ; then
		log "VERBOSE" "Querying $1 media type using device name: "
		case $dev in
			hd*)	# IDE device
				if [ -r /proc/ide/$dev/media ]; then
					MEDIA="$(cat /proc/ide/$dev/media)"
					BUS=ata
					if [ "$MEDIA" = cdrom ] ; then
						MEDIA=cd
					fi
				fi
			;;
			sd*)	# SCSI disk
				# No need to check, sd is always SCSI disk
				MEDIA=disk
				BUS=scsi
			;;
			sr* | scd* )
				# No need to check, sr or scd is always SCSI CD-ROM
				MEDIA=cd
				BUS=scsi
			;;

		esac
		if [ -n "$MEDIA" ] ; then
			log "VERBOSE" "type '$MEDIA' on bus '$BUS' detected"
		else
			log "ERR" "failed - unknown name"
		fi
	fi

	if [ -z "$MEDIA" ] ; then
		if [ "$HDPARM_AVAILABLE" = "1" ]; then
			log "VERBOSE" "Querying $1 type using hdparm: "
			if hdparm -I $1 | grep -q CD-ROM ; then
				MEDIA=cd
			else
				MEDIA=disk
			fi
			BUS=ata # or acts like it anyway, because hdparm supports it.
			log "VERBOSE" "type '$MEDIA' on bus '$BUS' detected"
		fi
	fi

	# Sanity check
	if [ -z "$MEDIA" -o -z "$BUS" ] ; then
		log "ERR" "Querying $1 type - unknown type or bus, disabling hdparm/sdparm"
		return 1
	fi

	if [ "$BUS" = "scsi" -a "$ASSUME_SCSI_IS_SATA" -ne 0 ] ;then
		# Treat scsi disks as SATA devices. Unfortunately they are hard
		# to recognize -- if anybody has a drive and cares to find out
		# how to recognize them, please enlighten me!
		BUS=ata
	fi

	# Now check what capabilities we support for the
	# various media and bus types.
	case "$MEDIA:$BUS:$2" in
		# Although CD-ROM drives usually support
		# idle timeout settings, they don't usually
		# support very low values, and we don't want
		# to mess with that. We simply ignore anything
		# that is a CD player.
		cd:*:* ) return 1;;

		# ATA drives support the "hdparm" command but
		# not normally the "sdparm" command.
		*:ata:HDPARM ) return 0 ;;
		*:ata:SDPARM ) return 1 ;;

		# SCSI drives support the "sdparm" command, but
		# not normally the "hdparm" command.
		*:scsi:SDPARM ) return 0 ;;
		*:scsi:HDPARM ) return 1 ;;

		# On ATA disks everything is supported.
		disk:ata:* ) return 0 ;;

		# For sdparm we only know how to set the idle
		# timeout, nothing else at the moment.
		*:scsi:IDLE_TIMEOUT ) return 0 ;;

		# No other capabilities are supported.
		* ) return 1 ;;
	esac
}


# Preparation: determine the tools we have available.
if [ -x "$(which hdparm 2> /dev/null)" ]; then
	HDPARM_AVAILABLE=1
fi
if [ -x "$(which sdparm 2> /dev/null)" ]; then
	SDPARM_AVAILABLE=1
fi

HAVE_UDEVINFO=0
if [ -x "$UDEVADM" ]; then
	UDEVVERSION=$(udevadm info -V)
	UDEV_VER_VERIFY=$(echo $UDEVVERSION | cut -b 1)
	case $UDEV_VER_VERIFY in
		[a-z]) UDEVVERSION=$(udevadm info -V | awk '{print $3}')
		;;
		*)
		;;
	esac

	if [ "$UDEVVERSION" -gt 70 ] ; then
		HAVE_UDEVINFO=1
	else
		log "VERBOSE" "udevadm info present but version not > 070, not using udev"
	fi
else
	# Older versions of udev (udevinfo) give output in the form of
	# "udevinfo, version 125"
	# Will be removed later. Currently only for backward compatibility
	if [ -x "$UDEVINFO" ] ; then
		UDEVVERSION=$(udevinfo -V | awk '{ print $3; }')
		if [ "$UDEVVERSION" -gt 70 ] ; then
			HAVE_UDEVINFO=1
		else
			log "VERBOSE" "udevinfo present but version not > 070, not using udev"
		fi
	fi
fi

if [ x$CONTROL_HD_POWERMGMT = x1 ] || [ x$ENABLE_AUTO_MODULES = x1 -a x$CONTROL_HD_POWERMGMT = xauto ]; then
	if [ $ON_AC -eq 1 ] ; then
		if [ "$ACTIVATE" -eq 1 ] ; then
			HD_POWERMGMT=$LM_AC_HD_POWERMGMT
		else
			HD_POWERMGMT=$NOLM_AC_HD_POWERMGMT
		fi
	else
		HD_POWERMGMT=$BATT_HD_POWERMGMT
	fi

	log "VERBOSE" "Setting powermanagement on drives to $HD_POWERMGMT."
	for THISHD in $HD ; do
		if is_capable $THISHD POWERMGMT ; then
			if is_capable $THISHD HDPARM ; then
				if [ "$HDPARM_AVAILABLE" = "1" ]; then
					log "VERBOSE" "Executing: hdparm -B $HD_POWERMGMT $THISHD"
					log "VERBOSE" "`hdparm -B $HD_POWERMGMT $THISHD 2>&1`"
				else
					log "ERR" "ERROR: hdparm not installed."
				fi
			else
				log "VERBOSE" "Skipping $THISHD: powermgmt only possible with hdparm but drive does not"
				log "VERBOSE" "support hdparm."
			fi
		else
			log "VERBOSE" "Skipping $THISHD: powermanagement control not supported."
		fi
	done
fi

if [ x$CONTROL_HD_IDLE_TIMEOUT = x1 ] ; then
	# Spindown timeouts may only be set when data-loss sensitive
	# features are active.
	if [ "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq 1 ] ; then
		if [ $ON_AC -eq 1 ] ; then
			HD_IDLE_TIMEOUT=$LM_AC_HD_IDLE_TIMEOUT
			HD_IDLE_TIMEOUT_SECONDS=$LM_AC_HD_IDLE_TIMEOUT_SECONDS
		else
			HD_IDLE_TIMEOUT=$LM_BATT_HD_IDLE_TIMEOUT
			HD_IDLE_TIMEOUT_SECONDS=$LM_BATT_HD_IDLE_TIMEOUT_SECONDS
		fi
	else
		HD_IDLE_TIMEOUT=$NOLM_HD_IDLE_TIMEOUT
		HD_IDLE_TIMEOUT_SECONDS=$NOLM_HD_IDLE_TIMEOUT_SECONDS
	fi
	log "VERBOSE" "Setting spindown timeout on drives to $HD_IDLE_TIMEOUT_SECONDS seconds."
	log "VERBOSE" "(hdparm configuration value = $HD_IDLE_TIMEOUT.)"
	for THISHD in $HD ; do
		if is_capable $THISHD IDLE_TIMEOUT ; then
			if is_capable $THISHD SDPARM ; then
				if [ "$SDPARM_AVAILABLE" = "1" ]; then
					HD_IDLE_TIMEOUT_DECISECONDS=$(($HD_IDLE_TIMEOUT_SECONDS*10))
					log "VERBOSE" "Executing: sdparm -q -s SCT=$HD_IDLE_TIMEOUT_DECISECONDS $THISHD"
					log "VERBOSE" "`sdparm -q -s SCT=$HD_IDLE_TIMEOUT_DECISECONDS $THISHD 2>&1`"
				else
					log "ERR" "ERROR: sdparm not installed."
				fi
			elif is_capable $THISHD HDPARM ; then
				if [ "$HDPARM_AVAILABLE" = "1" ]; then
					log "VERBOSE" "Executing: hdparm -S $HD_IDLE_TIMEOUT $THISHD"
					log "VERBOSE" "`hdparm -S $HD_IDLE_TIMEOUT $THISHD 2>&1`"
				else
					log "VERBOSE" "ERROR: hdparm not installed."
				fi
			else
				log "VERBOSE" "Skipping $THISHD: drive supports neither hdparm nor sdparm."
			fi
		else
			log "VERBOSE" "Skipping $THISHD: idle timeout control not supported."
		fi
	done
fi

if [ x$CONTROL_HD_WRITECACHE = x1 ] ; then
	# The writecache may only be enabled when data-loss sensitive
	# features are active.

	if [ "$ACTIVATE" -eq 1 ] ; then
		if [ "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq 0 ] ; then
			HD_WRITECACHE=0
		else
			HD_WRITECACHE=$LM_HD_WRITECACHE
		fi
	else
		if [ $ON_AC -eq 1 ] ; then
			HD_WRITECACHE=$NOLM_AC_HD_WRITECACHE
		else
			HD_WRITECACHE=$NOLM_BATT_HD_WRITECACHE
		fi
	fi
	log "VERBOSE" "Setting write cache on drives to $HD_WRITECACHE."
	for THISHD in $HD ; do
		if is_capable $THISHD WRITECACHE ; then
			if is_capable $THISHD SDPARM ; then
				if [ "$SDPARM_AVAILABLE" = "1" ]; then
					log "VERBOSE" "Executing: sdparm Write Cache $HD_WRITECACHE $THISHD"
					log "VERBOSE" "`sdparm --set WCE=$HD_WRITECACHE $THISHD 2>&1`"
				else
					log "ERR" "ERROR: sdparm not installed."
				fi
			elif is_capable $THISHD HDPARM ; then
				if [ "$HDPARM_AVAILABLE" = "1" ]; then
					log "VERBOSE" "Executing: hdparm -W $HD_WRITECACHE $THISHD"
					log "VERBOSE" "`hdparm -W $HD_WRITECACHE $THISHD 2>&1`"
				else
					log "ERR" "ERROR: hdparm not installed."
				fi
			else
				log "VERBOSE" "Skipping $THISHD: writecache only possible with hdparm but drive does not"
				log "VERBOSE" "support hdparm."
			fi
		else
			log "VERBOSE" "Skipping $THISHD: writecache control not supported."
		fi
	done
fi

