Skip to content

Commit cd0def0

Browse files
committed
usb.py: defensive USB reset at open_dev() entry
Multiple users reported the 0xd51-family chips (138a:00ab, 06cb:00b7) getting stuck after cold boot, unclean exit, or suspend/resume — the chip accepts the bulk-OUT but never responds on bulk-IN, so the first cleartext cmd (cmd 3e get_flash_info) times out and the daemon restart-loops every 15 seconds. The workaround users were running manually is a USB-level reset: sudo systemctl stop python3-validity open-fprintd sudo udevadm trigger --attr-match=idVendor=138a --attr-match=idProduct=00ab sudo systemctl start python3-validity open-fprintd The reset call here is the in-driver equivalent. The chip's USB address can shift after reset, so we re-find by vid/pid. Reported by Killersparrow1 (issue uunicorn#238, Fedora 44, sensor vanishes on reboot) and a separate Arch / ZBook G5 user (USBTimeoutError on cmd 3e). The patch matches what the project memory has flagged for the past two sessions as "kept local and not in PR" — turns out it was the actually-load-bearing piece. Locally confirmed: clean daemon restart, sudo matches in 1 retry, no traceback, no "USB reset failed" warning.
1 parent baec76e commit cd0def0

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

debian/changelog

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
python-validity (0.16~hp7) noble; urgency=medium
2+
3+
* usb.py: defensive USB reset at the start of open_dev(). The 0xd51-
4+
family chips (138a:00ab, 06cb:00b7) can be left in a "stuck"
5+
protocol state across cold boot, unclean exit of the daemon, or
6+
sudden suspend/resume — in that state the chip accepts the
7+
bulk-OUT but never replies on bulk-IN, so the first cleartext
8+
command (get_flash_info, cmd 3e) times out and the daemon enters
9+
a 15-second restart loop. The reset is the in-driver equivalent
10+
of the manual `udevadm trigger --attr-match=idVendor=...
11+
--attr-match=idProduct=...` workaround multiple users were
12+
running to recover after every reboot. Reported by Killersparrow1
13+
(uunicorn/python-validity#238, Fedora 44, sensor vanishes on
14+
reboot) and a separate Arch / ZBook G5 user (USBTimeoutError on
15+
cmd 3e). Also observed intermittently on the maintainer's
16+
machine.
17+
* After reset, re-find the device by vid/pid because the USB
18+
address can shift across the reset.
19+
20+
-- SimpleX-T <ntmark2004@gmail.com> Sun, 25 May 2026 00:30:00 +0100
21+
122
python-validity (0.16~hp6) noble; urgency=medium
223

324
* Diagnostic logging (no behavior change):

validitysensor/usb.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import errno
22
import logging
3+
import time
34
import typing
45
from binascii import hexlify, unhexlify
56
from enum import Enum
@@ -63,6 +64,34 @@ def open_dev(self, dev: ucore.Device):
6364
if dev is None:
6465
raise Exception('No matching devices found')
6566

67+
# Defensive USB reset on init.
68+
#
69+
# The 0xd51-family chips (HP 138a:00ab / 06cb:00b7) can be left in
70+
# a "stuck" protocol state across a previous unclean exit of this
71+
# daemon, a cold boot, or a sudden suspend/resume. In that state
72+
# the chip accepts the bulk-OUT but never replies on bulk-IN, so
73+
# the very first cleartext command (typically `cmd 3e`
74+
# get_flash_info) times out — the daemon then restart-loops at
75+
# 15s intervals and the sensor is "vanished" until a manual USB
76+
# reset. This block is the in-driver equivalent of the manual
77+
# `udevadm trigger --attr-match=idVendor=... --attr-match=idProduct=...`
78+
# workaround users have been running to recover.
79+
#
80+
# Reported by Killersparrow1 (#238, Fedora 44, vanishes on reboot)
81+
# and Maarten (Arch, ZBook G5, USBTimeoutError on first 3e). Also
82+
# observed locally on the maintainer's machine (sensor prompts but
83+
# doesn't detect after a while).
84+
try:
85+
vid, pid = dev.idVendor, dev.idProduct
86+
dev.reset()
87+
time.sleep(0.5)
88+
# USB address may shift after reset; re-find by vid/pid.
89+
dev = ucore.find(idVendor=vid, idProduct=pid)
90+
if dev is None:
91+
raise Exception('Device disappeared after USB reset')
92+
except USBError as e:
93+
logging.warning('open_dev: USB reset failed (often non-fatal): %s', e)
94+
6695
self.dev = dev
6796
self.dev.default_timeout = 15000
6897
dev.set_configuration()

0 commit comments

Comments
 (0)