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.
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 -3Learn more about email & git
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
Testing this patch it works fairly well, but it seems that after some time it stops going back to suspend and just turns then screen on instead...
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