Environment
- Hardware: Lenovo ThinkPad X1 Yoga Gen 3
- Sensor:
06cb:009a Synaptics, Inc. Metallica MIS Touch Fingerprint Reader - Distro: Ubuntu 24.04, kernel 6.14.0-36-generic
python3-validity 0.15~ppa2, open-fprintd 0.7~ppa2 (from PPA)
Summary
After deep (S3) suspend, Resume() frequently fails with
usb.core.USBError: [Errno 19] No such device — roughly half the
time on this machine, depending on how long the sensor was suspended
for. When the kernel re-enumerates the sensor as a new USB device
number, the service still holds the file descriptor from the
pre-suspend device; neither the primary open_common() call nor
the existing except-clause retry reopens the USB device.
When this happens, recovery requires systemctl restart python3-validity
(~1 second), which is long enough for GDM/gnome-shell to poll fprintd,
get an error, and fall back to password prompt — so the sensor never
lights up on lid-open. The rest of the time (short suspends where
USB is not re-enumerated) Resume() works fine.
The current Resume() already tries twice
defResume(self):
logging.debug('In Resume')
tls.reset()
try:
init.open_common()
except:
init.open_common() # same stale usb.dev — retry is a no-opBoth invocations issue commands through the same usb.dev object.
open_common() runs init_flash() → usb.send_init() → writes over
self.dev — and there is no path in the resume flow that calls
usb.open() again. usb.open() is only invoked at process startup
via init.open().
Timeline of a failed resume (representative)
11:06:08 kernel: PM: suspend exit
11:06:08 open-fprintd: (from resume.py) Manager.Resume
11:06:08 python3-validity: In Resume <- old fd
11:06:09 gdm-fingerprint: gkr-pam: no password is available for user
(GDM has already polled fprintd, got an error, gave up)
11:06:09 dbus-service: USBError [Errno 19] x2 (initial + internal retry)
11:06:09 systemd: Stopped / Started python3-validity (external restart)
11:06:10 python3-validity: Manager is back online, registering
(too late — GDM has moved on)
Suggested fix
Reopen the USB device in the except-clause of Resume():
defResume(self):
logging.debug('In Resume')
tls.reset()
try:
init.open_common()
exceptException:
# USB device likely re-enumerated across S3; fd is stale.fromvaliditysensor.usbimportusbusb.close()
usb.open()
init.open_common()This eliminates the need for the external process restart and closes
the race window with the desktop's fingerprint poll.
s2idle resume is unaffected because the USB device is not
re-enumerated — the sensor keeps the same device number, and the
existing retry succeeds. It's specifically the S3 case that breaks.
Related open-fprintd change
Locally I patched open-fprintd's resume.py to (a) cancel any
pending VerifyStop/Release before calling Resume(), and
(b) fall back to systemctl restart python3-validity when
Resume() throws. Happy to open a separate PR to
uunicorn/open-fprintd if that would be useful — but the
process-restart workaround only matters until Resume() handles
stale handles itself.
Reproducibility
Intermittent — approximately 50/50 in a recent 10-resume sample on
this hardware. Longer suspends (multiple hours) fail more often;
short suspends often succeed. The failure mode itself is consistent
whenever it triggers: same USBError from the same code path.
Related issues
Environment
06cb:009aSynaptics, Inc. Metallica MIS Touch Fingerprint Readerpython3-validity 0.15~ppa2,open-fprintd 0.7~ppa2(from PPA)Summary
After deep (S3) suspend,
Resume()frequently fails withusb.core.USBError: [Errno 19] No such device— roughly half thetime on this machine, depending on how long the sensor was suspended
for. When the kernel re-enumerates the sensor as a new USB device
number, the service still holds the file descriptor from the
pre-suspend device; neither the primary
open_common()call northe existing except-clause retry reopens the USB device.
When this happens, recovery requires
systemctl restart python3-validity(~1 second), which is long enough for GDM/gnome-shell to poll fprintd,
get an error, and fall back to password prompt — so the sensor never
lights up on lid-open. The rest of the time (short suspends where
USB is not re-enumerated)
Resume()works fine.The current Resume() already tries twice
Both invocations issue commands through the same
usb.devobject.open_common()runsinit_flash()→usb.send_init()→ writes overself.dev— and there is no path in the resume flow that callsusb.open()again.usb.open()is only invoked at process startupvia
init.open().Timeline of a failed resume (representative)
Suggested fix
Reopen the USB device in the except-clause of
Resume():This eliminates the need for the external process restart and closes
the race window with the desktop's fingerprint poll.
s2idleresume is unaffected because the USB device is notre-enumerated — the sensor keeps the same device number, and the
existing retry succeeds. It's specifically the S3 case that breaks.
Related open-fprintd change
Locally I patched
open-fprintd'sresume.pyto (a) cancel anypending
VerifyStop/Releasebefore callingResume(), and(b) fall back to
systemctl restart python3-validitywhenResume()throws. Happy to open a separate PR touunicorn/open-fprintdif that would be useful — but theprocess-restart workaround only matters until
Resume()handlesstale handles itself.
Reproducibility
Intermittent — approximately 50/50 in a recent 10-resume sample on
this hardware. Longer suspends (multiple hours) fail more often;
short suspends often succeed. The failure mode itself is consistent
whenever it triggers: same USBError from the same code path.
Related issues
open-fprintd-resume.servicefails to start after resuming from suspend #106, Reader works initially on reboot, but stops working after suspend/sleep #187 — same USBError, treated as unrelated; all sharethis root cause.
of stale fprintd state surviving suspend.