{ lib, pkgs, ... }: let pythonValidityPatch = pkgs.writeText "python-validity-python-3.14.patch" '' diff --git a/validitysensor/flash.py b/validitysensor/flash.py index d37f15a..be3d4a1 100644 --- a/validitysensor/flash.py +++ b/validitysensor/flash.py @@ -34,6 +34,10 @@ def get_flash_info(): rsp = tls.cmd(unhex('3e')) assert_status(rsp) rsp = rsp[2:] + if len(rsp) < 0xe: + # An uninitialised 06cb:009a returns only the success status word. + # Let init_flash() format it instead of failing in struct.unpack(). + return FlashInfo(None, 0, 0, 0, 0, []) hdr = rsp[:0xe] rsp = rsp[0xe:] jid0, jid1, blocks, unknown0, blocksize, unknown1, pcnt = unpack('/dev/null 2>&1; then exit 0 fi ${sleep} 0.5 done echo "wait-for-fprint: fingerprint backend did not become ready" >&2 exit 0 ''; # The xHCI controller is reset during resume, which invalidates # python-validity's USB handle. Restart it from a systemd-sleep hook: the # hook runs before logind emits PrepareForSleep(false), so hyprlock's # built-in fingerprint auth sees a working sensor as soon as it resumes. fprintResumeHook = pkgs.writeShellScript "fprint-resume" '' if [ "$1" != "post" ]; then exit 0 fi # Stop the old instance first so its stale D-Bus target cannot be selected # by the readiness checks below. Start without blocking because # python-validity's ExecStartPost waits for fprintd, but open-fprintd is # still suspended until Resume is called below. ${timeout} 10 ${systemctl} stop python3-validity.service || true ${systemctl} start --no-block python3-validity.service || true # Clear open-fprintd's suspended flag. This also unblocks the new # python-validity instance's ExecStartPost. ${timeout} 10 ${busctl} call net.reactivated.Fprint /net/reactivated/Fprint/Manager \ net.reactivated.Fprint.Manager Resume || true # Wait for the new backend to register. for ((i = 0; i < 30; i++)); do if ${timeout} 0.5 ${fprintdList} root >/dev/null 2>&1; then break fi ${sleep} 0.1 done # The target now exists, so Resume reinitializes the sensor. Wait for it # to become responsive before logind starts hyprlock's fingerprint auth. ${timeout} 10 ${busctl} call net.reactivated.Fprint /net/reactivated/Fprint/Manager \ net.reactivated.Fprint.Manager Resume || true for ((i = 0; i < 30; i++)); do if ${timeout} 0.5 ${fprintdList} root >/dev/null 2>&1; then exit 0 fi ${sleep} 0.1 done echo "fprint-resume: fingerprint backend did not become ready" >&2 ''; sleepTargets = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" "suspend-then-hibernate.target" ]; in { services = { # The 06cb:009a sensor is driven by python-validity through open-fprintd; # stock fprintd/libfprint cannot handle it. fprintd.enable = false; dbus.packages = [ pkgs.open-fprintd python-validity ]; }; security.pam.services = { greetd.fprintAuth = true; polkit-1.fprintAuth = true; sudo.fprintAuth = true; login.fprintAuth = true; # hyprlock still needs a PAM service for password authentication, but it # must not run pam_fprintd: its own D-Bus fingerprint implementation is # used instead. Two implementations would fight over the device, and the # PAM one breaks when the sensor is reset across suspend. hyprlock = {}; }; environment.etc."systemd/system-sleep/50-fprint-resume".source = fprintResumeHook; systemd.services = { open-fprintd = { description = "Open FPrint daemon"; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "dbus"; BusName = "net.reactivated.Fprint"; ExecStart = "${pkgs.open-fprintd}/lib/open-fprintd/open-fprintd"; Restart = "on-failure"; }; }; python3-validity = { description = "python-validity fingerprint sensor backend"; wantedBy = [ "multi-user.target" ]; after = [ "open-fprintd.service" ]; wants = [ "open-fprintd.service" ]; serviceConfig = { Type = "simple"; ExecStart = "${python-validity}/bin/python-validity-service"; ExecStartPost = "-${waitForFprint}"; Restart = "always"; RestartSec = 2; RuntimeDirectory = "python-validity"; RuntimeDirectoryMode = "0700"; RuntimeDirectoryPreserve = "yes"; }; }; open-fprintd-suspend = { description = "Suspend open-fprintd backends"; wantedBy = sleepTargets; before = sleepTargets; serviceConfig = { Type = "oneshot"; ExecStart = "${pkgs.open-fprintd}/lib/open-fprintd/suspend.py"; }; }; }; # The sleep hook above resumes open-fprintd before logind wakes the # graphical session. The stock open-fprintd-resume unit runs later and # resets the sensor while hyprlock is already verifying, so it must not be # enabled. Order greetd (and therefore Hyprland/hyprlock) after the # fingerprint backend so the first lock screen can use the sensor. systemd.services.greetd = { after = [ "python3-validity.service" ]; wants = [ "python3-validity.service" ]; }; environment.systemPackages = [ fprintd-clients python-validity ]; }