diff --git a/hosts/modules/gui/sxmo/default.nix b/hosts/modules/gui/sxmo/default.nix index fdc14f2f..8b18d649 100644 --- a/hosts/modules/gui/sxmo/default.nix +++ b/hosts/modules/gui/sxmo/default.nix @@ -93,9 +93,9 @@ let pkgs = [ "systemd" "xdg-user-dirs" ]; src = ./hooks; }; - suspend = pkgs.static-nix-shell.mkBash { + suspend = pkgs.static-nix-shell.mkPython3Bin { pname = "sxmo_suspend.sh"; - pkgs = [ "coreutils" "findutils" "gnugrep" "rtl8723cs-wowlan" "time" "util-linux" ]; + pkgs = [ "rtl8723cs-wowlan" "util-linux" ]; src = ./hooks; }; }; diff --git a/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh b/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh index c85f441a..3eff31f3 100755 --- a/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh +++ b/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh @@ -1,5 +1,5 @@ #!/usr/bin/env nix-shell -#!nix-shell -i bash -p coreutils -p findutils -p gnugrep -p rtl8723cs-wowlan -p time -p util-linux +#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p rtl8723cs-wowlan -p util-linux # yeah, this isn't technically a hook, but the hook infrastructure isn't actually # restricted to stuff that starts with sxmo_hook_ ... @@ -20,40 +20,80 @@ # common sources of wakelocks (which one may wish to reduce) include: # - `sxmo_led.sh blink` (every 2s, by default) -suspend_time=300 +import argparse +import logging +import subprocess +import time -# TODO: don't do this wowlan stuff every single time. -# - it's costly (can take like 1sec) -# - it seems to actually block suspension quite often -# - possibly rtl8723cs takes time to apply wowlan changes during which suspension is impossible -# alternative is to introduce some layer of cache: -# - do so in a way such that WiFi connection state changes invalidate the cache -# - because wowlan enable w/o connection may well behave differently than w/ connection -# - calculating IP addr from link, and then caching on the args we call our helper with may well suffice -doas rtl8723cs-wowlan enable-clean -# wake on ssh -doas rtl8723cs-wowlan tcp --dest-port 22 --dest-ip SELF -# wake on notification (ntfy/Universal Push) -doas rtl8723cs-wowlan tcp --source-port 2587 --dest-ip SELF -# wake if someone doesn't know how to route to us, because that could obstruct the above -# doas rtl8723cs-wowlan arp --dest-ip SELF -# specifically wake upon ARP request via the broadcast address. -# should in theory by covered by the above (TODO: remove this!), but for now hopefully helps wake-on-lan be more reliable? -doas rtl8723cs-wowlan arp --dest-ip SELF --dest-mac ff:ff:ff:ff:ff:ff +logger = logging.getLogger(__name__) -# TODO: wake for Dino (call) traffic +SUSPEND_TIME=300 -echo "calling suspend for duration: $suspend_time" +class Executor: + def __init__(self, dry_run: bool = False): + self.dry_run = dry_run -time_start="$(date "+%s")" -irq_start="$(cat /proc/interrupts | grep 'rtw_wifi_gpio_wakeup' | tr -s ' ' | xargs echo | cut -d' ' -f 2)" + def exec(self, cmd: list[str], sudo: bool = False, check: bool = True): + if sudo: + cmd = [ 'doas' ] + cmd -rtcwake -m mem -s "$suspend_time" || exit 1 + logger.debug(" ".join(cmd)) + if self.dry_run: + return -irq_end="$(cat /proc/interrupts | grep 'rtw_wifi_gpio_wakeup' | tr -s ' ' | xargs echo | cut -d' ' -f 2)" -time_spent="$(( $(date "+%s") - time_start ))" + res = subprocess.run(cmd, capture_output=True) + logger.debug(res.stdout) + if res.stderr: + logger.warning(res.stderr) + if check: + res.check_returncode() -echo "suspended for $time_spent seconds. wifi IRQ count: ${irq_start} -> ${irq_end}" +def main(): + logging.basicConfig() + logging.getLogger().setLevel(logging.INFO) -sxmo_hook_postwake.sh + parser = argparse.ArgumentParser(description="suspend the pinephone to RAM, and configure wake triggers to make that appear more transparent") + parser.add_argument("--dry-run", action='store_true', help="print commands instead of executing them") + parser.add_argument("--verbose", action='store_true', help="log each command before executing") + args = parser.parse_args() + + if args.verbose: + logging.getLogger().setLevel(logging.DEBUG) + + + executor = Executor(dry_run=args.dry_run) + # TODO: don't do this wowlan stuff every single time. + # - it's costly (can take like 1sec) + # alternative is to introduce some layer of cache: + # - do so in a way such that WiFi connection state changes invalidate the cache + # - because wowlan enable w/o connection may well behave differently than w/ connection + # - calculating IP addr from link, and then caching on the args we call our helper with may well suffice + # and no need to invoke a subprocess here, when it's just python code calling other python code! + executor.exec(['rtl8723cs-wowlan', 'enable-clean'], sudo=True) + # wake on ssh + executor.exec(['rtl8723cs-wowlan', 'tcp', '--dest-port', '22', '--dest-ip', 'SELF'], sudo=True) + # wake on notification (ntfy/Universal Push) + # executor.exec(['rtl8723cs-wowlan', 'tcp', '--source-port', '2587', '--dest-ip', 'SELF'], sudo=True) + # wake if someone doesn't know how to route to us, because that could obstruct the above + # executor.exec(['rtl8723cs-wowlan', 'arp', '--dest-ip', 'SELF'], sudo=True) + # specifically wake upon ARP request via the broadcast address. + # should in theory by covered by the above (TODO: remove this!), but for now hopefully helps wake-on-lan be more reliable? + executor.exec(['rtl8723cs-wowlan', 'arp', '--dest-ip', 'SELF', '--dest-mac', 'ff:ff:ff:ff:ff:ff'], sudo=True) + + logger.info(f"calling suspend for duration: {SUSPEND_TIME}") + + time_start = time.time() + # irq_start="$(cat /proc/interrupts | grep 'rtw_wifi_gpio_wakeup' | tr -s ' ' | xargs echo | cut -d' ' -f 2)" + # + executor.exec(['rtcwake', '-m', 'mem', '-s', str(SUSPEND_TIME)], check=False) + + # irq_end="$(cat /proc/interrupts | grep 'rtw_wifi_gpio_wakeup' | tr -s ' ' | xargs echo | cut -d' ' -f 2)" + time_spent = time.time() - time_start + + logger.info(f"suspended for {time_spent:.0f} seconds") + + executor.exec(['sxmo_hook_postwake.sh'], check=False) + +if __name__ == '__main__': + main()