~mil/sxmo-devel

Implementing optional rtc wakeup at regular intervals, added presuspend, rtcwake and postwake scripts (with hooks) v1 APPLIED

Maarten van Gompel: 1
 Implementing optional rtc wakeup at regular intervals, added presuspend, rtcwake and postwake scripts (with hooks)

 4 files changed, 185 insertions(+), 16 deletions(-)
Sorry for the delay. You raise a good point indeed, the
missing/undefined return value is a bug on my part.

The timediff check itself is necessary to determine whether the wakeup
is due to the RTC call (in which case the timediff should be smallish)
or due to the user pressing the powerbutton. It's a bit patchy, I agree. I
couldn't really think of a better way of distinguishing the two events.
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mil/sxmo-devel/patches/11769/mbox | git am -3
Learn more about email & git

[PATCH] Implementing optional rtc wakeup at regular intervals, added presuspend, rtcwake and postwake scripts (with hooks) Export this patch

This is a first attempt to implement regular rtcwake ability in
screenlock, so it can wake from deep sleep (crust) every X seconds to do
some business like check notifications, and then sleep again. By default
this functionality is disabled. You can enable it by setting the
environment variable SXMO_RTCWAKEINTERVAL to the desired interval in
seconds, or call sxmo_screenlock with a parameter. When RTC wakeup
occurs, the script sxmo_rtcwake.sh is run, and during this time the led
turns blue, afterwards in turns purple for a few seconds (4) when
suspension is pending again. By default the script doesn't really do
anything yet, but there are new hooks in ~/.config/sxmo/ you can
implement yourself:

* presuspend - called prior to sleeping (called by sxmo_presuspend.sh, which does nothing else yet)
* rtcwake - called every $SMXO_RTCWAKEINTERVAL seconds when in deep sleep
* postwake - called after exiting sleep normally (called by
  sxmo_postwake.sh), this by default calls sxmo_statusbarupdate.sh

---
 programs/sxmo_screenlock.c      | 171 +++++++++++++++++++++++++++++---
 scripts/core/sxmo_postwake.sh   |   9 ++
 scripts/core/sxmo_presuspend.sh |  10 ++
 scripts/core/sxmo_rtcwake.sh    |  11 ++
 4 files changed, 185 insertions(+), 16 deletions(-)
 create mode 100755 scripts/core/sxmo_postwake.sh
 create mode 100755 scripts/core/sxmo_presuspend.sh
 create mode 100755 scripts/core/sxmo_rtcwake.sh

diff --git a/programs/sxmo_screenlock.c b/programs/sxmo_screenlock.c
index cc1a445..7e65e3e 100644
--- a/programs/sxmo_screenlock.c
+++ b/programs/sxmo_screenlock.c
@@ -5,19 +5,24 @@
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <linux/rtc.h>

// Types
enum State {
	StateNoInput,         // Screen on / input lock
	StateNoInput,		 // Screen on / input lock
	StateNoInputNoScreen, // Screen off / input lock
	StateSuspend,         // Deep sleep
	StateSuspend,		 // Deep sleep
	StateSuspendPending,  // Suspend 'woken up', must leave state in <5s, or kicks to StateSuspend
	StateDead             // Exit the appliation
	StateDead			 // Exit the appliation
};
enum Color {
	Red,
@@ -27,14 +32,21 @@ enum Color {
};

// Fn declarations
int checkrtcwake();
void configuresuspendsettingsandwakeupsources();
time_t convert_rtc_time(struct rtc_time * rtc);
void die(const char *err, ...);
int getoldbrightness();
void init_rtc();
void lockscreen(Display *dpy, int screen);
void readinputloop(Display *dpy, int screen);
int presuspend();
void postwake();
void setpineled(enum Color c);
int setup_rtc_wakeup();
void sigterm();
void syncstate();
void updatestatusbar();
void usage();
void writefile(char *filepath, char *str);

// Variables
@@ -47,6 +59,63 @@ int lastkeyn = 0;
char oldbrightness[10] = "200";
char * brightnessfile = "/sys/devices/platform/backlight/backlight/backlight/brightness";
char * powerstatefile = "/sys/power/state";
int rtc_fd = 0; //file descriptor
time_t wakeinterval = 0; //wake every x seconds
time_t waketime = 0; //next wakeup time according to the RTC clock

#define RTC_DEVICE	  "/dev/rtc0"

time_t
convert_rtc_time(struct rtc_time * rtc) {
	struct tm		 tm;
	memset(&tm, 0, sizeof tm);
	tm.tm_sec = rtc->tm_sec;
	tm.tm_min = rtc->tm_min;
	tm.tm_hour = rtc->tm_hour;
	tm.tm_mday = rtc->tm_mday;
	tm.tm_mon = rtc->tm_mon;
	tm.tm_year = rtc->tm_year;
	tm.tm_isdst = -1;  /* assume the system knows better than the RTC */
	return mktime(&tm);
}

int setup_rtc_wakeup() {
	//(code adapted from util-linux's rtcwake)
	struct tm		 *tm;
	struct rtc_wkalrm	wake;
	struct rtc_time now_rtc;

	if (ioctl(rtc_fd, RTC_RD_TIME, &now_rtc) < 0) {
		fprintf(stderr, "Error reading rtc time\n");
	}
	const time_t now = convert_rtc_time(&now_rtc);
	waketime = now + wakeinterval;

	tm = localtime(&waketime);

	wake.time.tm_sec = tm->tm_sec;
	wake.time.tm_min = tm->tm_min;
	wake.time.tm_hour = tm->tm_hour;
	wake.time.tm_mday = tm->tm_mday;
	wake.time.tm_mon = tm->tm_mon;
	wake.time.tm_year = tm->tm_year;
	/* wday, yday, and isdst fields are unused by Linux */
	wake.time.tm_wday = -1;
	wake.time.tm_yday = -1;
	wake.time.tm_isdst = -1;

	fprintf(stderr, "Setting RTC wakeup to %ld: (UTC) %s", waketime, asctime(tm));

	if (ioctl(rtc_fd, RTC_ALM_SET, &wake.time) < 0) {
		fprintf(stderr, "error setting rtc alarm\n");
		return -1;
	}
	if (ioctl(rtc_fd, RTC_AIE_ON, 0) < 0) {
		fprintf(stderr, "error enabling rtc alarm\n");
		return -1;
	}
	return 0;
}

void
configuresuspendsettingsandwakeupsources()
@@ -83,6 +152,9 @@ configuresuspendsettingsandwakeupsources()
		"enabled"
	);

	//set RTC wake
	if (wakeinterval > 0) setup_rtc_wakeup();

	// Temporary hack to disable USB driver that doesn't suspend
	fprintf(stderr, "Disabling buggy USB driver\n");
	writefile(
@@ -100,6 +172,7 @@ configuresuspendsettingsandwakeupsources()
	// E.g. make sure we're using CRUST
	fprintf(stderr, "Flip mem_sleep setting to use crust\n");
	writefile("/sys/power/mem_sleep", "deep");

}

void
@@ -116,6 +189,7 @@ sigterm()
{
	state = StateDead;
	syncstate();
	if (wakeinterval) close(rtc_fd);
	exit(0);
}

@@ -216,6 +290,7 @@ readinputloop(Display *dpy, int screen) {
						else state = StateNoInput;
						break;
					case XF86XK_PowerOff:
						waketime = 0;
						state = StateDead;
						break;
				}
@@ -228,6 +303,7 @@ readinputloop(Display *dpy, int screen) {
			syncstate();
		}


		if (state == StateDead) break;
	}
}
@@ -250,17 +326,63 @@ setpineled(enum Color c)
	}
}

int
presuspend() {
	//called prior to suspension, a non-zero return value cancels suspension
	return system("sxmo_presuspend.sh");
}

void
postwake() {
	//called after fully waking up (not used for temporary rtc wakeups)
	system("sxmo_postwake.sh");
}

int
checkrtcwake()
{
	struct rtc_time now;
	if (ioctl(rtc_fd, RTC_RD_TIME, &now) < 0) {
		fprintf(stderr, "Error reading rtc time\n");
		return -1;
	}

	const long int timediff = convert_rtc_time(&now) - waketime;
	fprintf(stderr, "Checking rtc wake? timediff=%ld\n", timediff);
	if (timediff >= 0 && timediff <= 3) {
		fprintf(stderr, "Calling RTC wake script\n");
		setpineled(Blue);
		return system("sxmo_rtcwake.sh");
	}
}

void
syncstate()
{
	int rtcresult;
	if (state == StateSuspend) {
		setpineled(Red);
		configuresuspendsettingsandwakeupsources();
		writefile(powerstatefile, "mem");
		// Just woke up
		updatestatusbar();
		state = StateSuspendPending;
		suspendpendingtimeouts = 0;
		if (presuspend() != 0) {
			state = StateDead;
		} else {
			setpineled(Red);
			configuresuspendsettingsandwakeupsources();
			writefile(powerstatefile, "mem");
			//---- program blocks here due to sleep ----- //
			// Just woke up again
			fprintf(stderr, "Woke up\n");
			if (waketime > 0) {
				rtcresult = checkrtcwake();
			} else {
				rtcresult = 0;
			}
			if (rtcresult == 0) {
				state = StateSuspendPending;
				suspendpendingtimeouts = 0;
			} else {
				postwake();
				state = StateDead;
			}
		}
		syncstate();
	} else if (state == StateNoInput) {
		setpineled(Blue);
@@ -279,11 +401,8 @@ syncstate()
	}
}

void
updatestatusbar()
{
	system("sxmo_statusbarupdate.sh");
}



void
writefile(char *filepath, char *str)
@@ -302,6 +421,15 @@ void usage() {
	fprintf(stderr, "Usage: sxmo_screenlock [--screen-off] [--suspend]\n");
}


void init_rtc() {
	rtc_fd = open(RTC_DEVICE, O_RDONLY);
	if (rtc_fd < 0) {
		die("Unable to open rtc device");
		exit(EXIT_FAILURE);
	}
}

int
main(int argc, char **argv) {
	int screen;
@@ -310,6 +438,9 @@ main(int argc, char **argv) {

	signal(SIGTERM, sigterm);

	const char* rtcwakeinterval = getenv("SXMO_RTCWAKEINTERVAL");
	if (rtcwakeinterval != NULL) wakeinterval = atoi(rtcwakeinterval);

	//parse command line arguments
	for (i = 1; i < argc; i++) {
		if(!strcmp(argv[i], "-h")) {
@@ -319,6 +450,8 @@ main(int argc, char **argv) {
			target = StateNoInputNoScreen;
		} else if(!strcmp(argv[i], "--suspend")) {
			target = StateSuspend;
		} else if(!strcmp(argv[i], "--wake-interval")) {
			wakeinterval = (time_t) atoi(argv[++i]);
		} else {
			fprintf(stderr, "Invalid argument: %s\n", argv[i]);
			return 2;
@@ -330,6 +463,8 @@ main(int argc, char **argv) {
	if (!(dpy = XOpenDisplay(NULL)))
		die("Cannot open display\n");

	if (wakeinterval) init_rtc();

	XkbSetDetectableAutoRepeat(dpy, True, NULL);
	screen = XDefaultScreen(dpy);
	XSync(dpy, 0);
@@ -345,5 +480,9 @@ main(int argc, char **argv) {
		syncstate();
	}
	readinputloop(dpy, screen);
	if (wakeinterval) {
		ioctl(rtc_fd, RTC_AIE_OFF, 0);
		close(rtc_fd);
	}
	return 0;
}
diff --git a/scripts/core/sxmo_postwake.sh b/scripts/core/sxmo_postwake.sh
new file mode 100755
index 0000000..f8b6b2f
--- /dev/null
+++ b/scripts/core/sxmo_postwake.sh
@@ -0,0 +1,9 @@
#!/usr/bin/env sh

# This script is called when the system has successfully woken up after sleep

sxmo_statusbarupdate.sh

if [ -x "$XDG_CONFIG_HOME/sxmo/hooks/postwake" ]; then
    "$XDG_CONFIG_HOME/sxmo/hooks/postwake"
fi
diff --git a/scripts/core/sxmo_presuspend.sh b/scripts/core/sxmo_presuspend.sh
new file mode 100755
index 0000000..d08ed81
--- /dev/null
+++ b/scripts/core/sxmo_presuspend.sh
@@ -0,0 +1,10 @@
#!/usr/bin/env sh

# This script is called prior to suspending

# If this script returns a non-zero exit code, suspension will be cancelled

if [ -x "$XDG_CONFIG_HOME/sxmo/hooks/presuspend" ]; then
    "$XDG_CONFIG_HOME/sxmo/hooks/presuspend"
	exit $?
fi
diff --git a/scripts/core/sxmo_rtcwake.sh b/scripts/core/sxmo_rtcwake.sh
new file mode 100755
index 0000000..7e45ad5
--- /dev/null
+++ b/scripts/core/sxmo_rtcwake.sh
@@ -0,0 +1,11 @@
#!/usr/bin/env sh

# This script (and anything it calls) should return as quickly as possible
# as it blocks the system from suspending (and processing input) until done

# If this script returns a non-zero exit code, the system will wake up

if [ -x "$XDG_CONFIG_HOME/sxmo/hooks/rtcwake" ]; then
    "$XDG_CONFIG_HOME/sxmo/hooks/rtcwake"
	exit $?
fi
--
2.27.0
This has now been applied with an additional extra fix (and some
cleanup) that was needed, in commit
642bf1cc27c64464dc95ff187fccf3e5d2a48f24. It's been tested for quite a
while now and after the 5.9 kernel update I have not had any crashes
with crust and wakeup.

Note that a difference with the 5.9 kernel, compared to before, is that
the red LED no longer remains lit during sleep.

--

Maarten van Gompel (proycon)
https://proycon.anaproy.nl