From 642bf1cc27c64464dc95ff187fccf3e5d2a48f24 Mon Sep 17 00:00:00 2001 From: Maarten van Gompel Date: Mon, 27 Jul 2020 00:23:28 +0200 Subject: [PATCH] Implementing optional rtc wakeup at regular intervals, added presuspend, rtcwake and postwake scripts (with hooks) (includes missing return 0 statement caught by Serge Hallyn) Signed-off-by: Maarten van Gompel --- programs/sxmo_screenlock.c | 174 ++++++++++++++++++++++++++++---- scripts/core/sxmo_postwake.sh | 9 ++ scripts/core/sxmo_presuspend.sh | 10 ++ scripts/core/sxmo_rtcwake.sh | 11 ++ 4 files changed, 187 insertions(+), 17 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 30a3e13..543ac16 100644 --- a/programs/sxmo_screenlock.c +++ b/programs/sxmo_screenlock.c @@ -5,19 +5,24 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include // 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() @@ -59,7 +128,7 @@ configuresuspendsettingsandwakeupsources() die("Couldn't open directory /sys/class/wakeup\n"); while ((wakeupsource = readdir(wakeupsources)) != NULL) { sprintf( - wakeuppath, + wakeuppath, "/sys/class/wakeup/%.50s/device/power/wakeup", wakeupsource->d_name ); @@ -90,6 +159,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( @@ -107,6 +179,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 @@ -123,6 +196,7 @@ sigterm() { state = StateDead; syncstate(); + if (wakeinterval) close(rtc_fd); exit(0); } @@ -223,6 +297,7 @@ readinputloop(Display *dpy, int screen) { else state = StateNoInput; break; case XF86XK_PowerOff: + waketime = 0; state = StateDead; break; } @@ -235,6 +310,7 @@ readinputloop(Display *dpy, int screen) { syncstate(); } + if (state == StateDead) break; } } @@ -257,17 +333,64 @@ 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"); + } + return 0; +} + 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); @@ -286,11 +409,8 @@ syncstate() } } -void -updatestatusbar() -{ - system("sxmo_statusbarupdate.sh"); -} + + void writefile(char *filepath, char *str) @@ -309,6 +429,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; @@ -317,6 +446,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")) { @@ -326,6 +458,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; @@ -337,6 +471,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); @@ -352,5 +488,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..7d57cda --- /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..2319330 --- /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..efa31a9 --- /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