#!/usr/bin/perl -w

# This is a long but straightforward script supposed to handle the
# management of the most common acpi events, with a handful of possible tweaks
# here and there.

my $verbose=0;

open ERROR, ">&STDOUT";
if ($verbose) { open DEBUG, ">&STDOUT"; } else { open DEBUG, ">/dev/null";}

###########################################################################
# Laptop-specific controls...

# S3 can be used is the video board is able to spin up after cold
# standby. Which is somewhat rare.
my $sleep_mode="S3";

# Backlight control. Currently I know how to do this for radeon only.
sub backlight {
	print DEBUG "Setting light $_[0]\n";
	unshift @_, "light";
	system "/etc/acpi/actions/radeontool", @_;
}

# These two are not really laptop-specific, but either useful or pointless. So
# you may deactivate them by making these functions trivial.
# sub changevt {
# 	chomp(my $res=`/etc/acpi/actions/vtutil.pl -q`);
# 	chomp(my $new=$_[0]);
# 	print DEBUG "vt: $res --> $new\n";
# 	my @args=($new);
# 	if ($new ge 7) {
# 		# We add the monitoring stuff to circumvent possible
# 		# vt change failures seen on resume
# 		unshift @args, "-monitor";
# 	}
# 	system "/etc/acpi/actions/vtutil.pl", @args;
# 	return $res;
# }

sub save_clock {
	print DEBUG "clock: $_[0]\n";
	system "/sbin/hwclock", @_;
}

# Services to stop in order to safely reach the corresponding sleep level. They
# are reactivated in the reverse order.

# In case you don't know, a ``service'' is basically a script in /etc/init.d,
# accepting the start and stop arguments.

# Note that /etc/init.d/usb does not exist by default, it's a personal addition
# (deliberately not chkconfig enabled, since it's the global initscripts' job).
my %stop_services=(
	"S1" => [ "networking"],
	"S3" => ["networking"],
);

my %forbidden_modules=(
	"S1" => [],
	"S3" => ["tg3"],
);


###########################################################################
# read permanent state

my $lid_state="unknown";
my $lid_wakeupstate="unknown";
my $ac_state="unknown";

sub read_lid_state() {
	open F, "</proc/acpi/button/lid/LID/state";
	if ((<F>) =~ /^state:\s*(open|closed)$/) {
		$lid_state=$1;
	}
	close F;
}

sub read_ac_state() {
	open F, "</proc/acpi/ac_adapter/AC/state";
	if ((<F>) =~ /^state:\s*(on|off)-line$/) {
		$ac_state=$1;
	}
	close F;
}

# This needs a special patch.
my $wakeup_proc_file="/proc/acpi/wakeup";
sub read_lid_wakeupstate {
	return 0 unless -f "$wakeup_proc_file";
	open F, "<$wakeup_proc_file";
	while (<F>) {
		/^\s*LID.*((en|dis)abled)$/ && do {$lid_wakeupstate=$1;};
	}
	close F;
}

read_lid_state;		print DEBUG "LID: $lid_state\n";
read_ac_state;		print DEBUG "AC: $ac_state\n";
read_lid_wakeupstate;	print DEBUG "LID_WAKEUP: $lid_wakeupstate\n";

###########################################################################
# Note that in fact, we cannot enable a device for waking up from another power
# state than the one(s) it is specified to be able to wake up from...

sub enable_lid_wakeup {
	read_lid_wakeupstate;
	return 0 unless ($lid_wakeupstate eq "disabled");
	print DEBUG "Trying to switch LID wakeup state\n";
	open F, ">$wakeup_proc_file";
	print F "LID\n";
	close F;
	read_lid_wakeupstate;
	if ($lid_wakeupstate ne "enabled") {
		print ERROR "Could not enable LID for wakeup !\n";
	} else {
		print DEBUG "LID now enabled for wakeup\n";
	}
}

sub go_sleep {
	print DEBUG "Preparing to go to $sleep_mode\n";
	my $astops=$stop_services{"$sleep_mode"};
	my $rmmod_these=$forbidden_modules{"$sleep_mode"};
	system "touch /suspending";
	for my $s (@$astops) {
		print DEBUG "STOP $s\n";
		system "/etc/init.d/$s stop";
	}
	for my $s (@$rmmod_these) {
		print DEBUG "RMMOD $s\n";
		system "/sbin/modprobe -r $s";
	}
	my $s=$sleep_mode;
	$s =~ s/^S([0-9].*)$/$1/g;
	print DEBUG "Going to $sleep_mode\n";
	save_clock "--systohc";
	# backlight "off";
	# print DEBUG "BYE\n";
	# doing the equivalent of the following line in the perl way sometimes
	# break with an FPE.
	system "echo $s >/proc/acpi/sleep";
	##################################################
	# Here we return from the sleep procedure.
	# backlight "on";
	save_clock "--hctosys";
	for my $s (reverse(@$astops)) {
		# print DEBUG "START $s\n";
		system "/etc/init.d/$s start";
	}
	system "rm -f /suspending";
#	system "updfstab";
# /usr/sbin/updfstab should be moved to updfstab.orig and replaced by the
# following script:
# ------------------------------------------------------
# #!/bin/sh
# 
# if [ -e /suspending ] ; then
#         echo "Skipping : updfstab $@"
#         echo "Skipping : updfstab $@" >&2
# else
#         exec updfstab.orig "$@"
# fi
# ------------------------------------------------------
# This is to prevent stupid lockups situations where updfstab lingers at
# standby time
}

my ($event,$device,$dummy,$serial)=@ARGV;

my $will_sleep=0;
# In some cases we might have to go to sleep !.
if ($device eq "SBTN") {
	$will_sleep=1;
}

if ($device eq "LID" && $lid_state eq "closed" && $ac_state eq "off")
{
	# If the special semaphore file
	# /tmp/do_not_standby is present, with a mtime in the future,
	# then don't sleep.

	# Use touch /tmp/do_not_standby -F 3600 to get 1hour rest.
	my @d;
	if (!(-e "/tmp/do_not_standby" && (@d=stat(_)) && $d[9] > time())){
		$will_sleep=1;
	}
}

# Manage the backlight. Even if we're not going to sleep, it makes sense.
if ($will_sleep==0 && $device eq "LID") {
 	if ($lid_state eq "open") { # reopening.
		backlight "on";
	} elsif ($lid_state eq "closed") { #closing.
		backlight "off";
	}
}


if ($will_sleep) {
	enable_lid_wakeup;
	go_sleep;
}

# I should also handle other events like power button and so on...
