From 19871889bd2b6ba7972b3104c5e220b714ad5895 Mon Sep 17 00:00:00 2001 From: Milad Olad Date: Wed, 27 May 2026 18:21:18 -0400 Subject: [PATCH 1/3] Add USB device entry and firmware tables for 138a:0092 Add DEV_92 (0x138a, 0x0092) to SupportedDevices in usb.py. Map device 0x0092 to use blobs_9d in blobs.py. Add proper firmware partition table entries for DEV_92 in firmware_tables.py. This sensor is found in HP EliteBook x360 1030 G2 and similar HP laptops. It uses sensor type 0x1825 with ROM product=0x30 major=0x6 minor=0x7. --- validitysensor/blobs.py | 2 +- validitysensor/firmware_tables.py | 10 ++++++++-- validitysensor/usb.py | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/validitysensor/blobs.py b/validitysensor/blobs.py index a9f16ab..3d65ca3 100644 --- a/validitysensor/blobs.py +++ b/validitysensor/blobs.py @@ -6,7 +6,7 @@ def __load_blob(blob: str) -> bytes: from . import blobs_90 as blobs elif usb.usb_dev().idProduct == 0x0097: from . import blobs_97 as blobs - elif usb.usb_dev().idProduct == 0x009d: + elif usb.usb_dev().idProduct in (0x009d, 0x0092): from . import blobs_9d as blobs elif usb.usb_dev().idVendor == 0x06cb: if usb.usb_dev().idProduct == 0x009a: diff --git a/validitysensor/firmware_tables.py b/validitysensor/firmware_tables.py index bd0b867..86a4b54 100644 --- a/validitysensor/firmware_tables.py +++ b/validitysensor/firmware_tables.py @@ -22,12 +22,18 @@ 'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe', 'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe', 'sha512': 'a4a4e6058b1ea8ab721953d2cfd775a1e7bc589863d160e5ebbb90344858f147d695103677a8df0b2de0c95345df108bda97196245b067f45630038fb7c807cd' - } + }, + SupportedDevices.DEV_92: { + 'driver': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe', + 'referral': 'https://download.lenovo.com/pccbbs/mobiles/nz3gf07w.exe', + 'sha512': 'a4a4e6058b1ea8ab721953d2cfd775a1e7bc589863d160e5ebbb90344858f147d695103677a8df0b2de0c95345df108bda97196245b067f45630038fb7c807cd' + }, } FIRMWARE_NAMES = { SupportedDevices.DEV_90: '6_07f_Lenovo.xpfwext', SupportedDevices.DEV_97: '6_07f_lenovo_mis_qm.xpfwext', SupportedDevices.DEV_9a: '6_07f_lenovo_mis_qm.xpfwext', - SupportedDevices.DEV_9d: '6_07f_lenovo_mis_qm.xpfwext' + SupportedDevices.DEV_9d: '6_07f_lenovo_mis_qm.xpfwext', + SupportedDevices.DEV_92: '6_07f_lenovo_mis_qm.xpfwext', } diff --git a/validitysensor/usb.py b/validitysensor/usb.py index 464b092..4c21559 100644 --- a/validitysensor/usb.py +++ b/validitysensor/usb.py @@ -18,6 +18,7 @@ class SupportedDevices(Enum): DEV_97 = (0x138a, 0x0097) DEV_9d = (0x138a, 0x009d) DEV_9a = (0x06cb, 0x009a) + DEV_92 = (0x138a, 0x0092) @classmethod def from_usbid(cls, vendorid, productid): From 27ef7f78f105755ab3ae348e747a21a0fd33bb93 Mon Sep 17 00:00:00 2001 From: Milad Olad Date: Wed, 27 May 2026 18:21:27 -0400 Subject: [PATCH 2/3] Fix finger detection for sensor type 0x1825 (138a:0092) The 0x1825 sensor firmware does not understand chunk type 0x4e used by newer sensors for finger detection during IDENTIFY mode. Instead, it requires chunk 0x26 with adjusted threshold parameters for both IDENTIFY and ENROLL. Key changes: - IDENTIFY mode: Use chunk 0x26 (not 0x4e) with threshold bytes 0x5400/0x3400 - ENROLL mode: Use chunk 0x26 with same adjusted threshold parameters - Set calibration_iterations=2 for 0x1825 (differs from 0x199 which uses 3) Without this fix, the sensor captures immediately without waiting for a finger to be placed, resulting in empty/invalid fingerprint data. Tested on HP EliteBook x360 1030 G2 with successful enrollment and identification of multiple fingers. --- validitysensor/sensor.py | 54 ++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/validitysensor/sensor.py b/validitysensor/sensor.py index dd019bb..83a53bd 100644 --- a/validitysensor/sensor.py +++ b/validitysensor/sensor.py @@ -232,6 +232,10 @@ def open(self): self.key_calibration_line = 0x38 # (lines_per_calibration_data/2), but hardcoded for sensor type 0x199 self.calibration_frames = 3 # TODO: workout where it's really comming from self.calibration_iterations = 3 # hardcoded for type + elif self.device_info.type == 0x1825: + self.key_calibration_line = 0x38 + self.calibration_frames = 3 + self.calibration_iterations = 2 elif self.device_info.type == 0xdb: self.key_calibration_line = 0x48 # TODO 48 is just a guess -- find it self.calibration_frames = 6 # TODO: workout where it's really comming from @@ -424,28 +428,46 @@ def line_update_type_1(self, mode: CaptureMode, chunks += [[0x17, b'']] if mode == CaptureMode.IDENTIFY: - # This type of fragment is not present in the debugging dump routine. - # It seems to be only used for identification and it looks almost identical to Finger Detect (0x26) - # Seems to be the same all the time for a given sensor and mostly hardcoded - # TODO: analyse construct_wtf_4e @0000000180090BF0 - chunks += [[ - 0x4e, - unhexlify( - 'fbb20f0000000f00300000008700020067000a00018000000a0200000b1900008813b80b01091000' - ) - ]] + # Finger Detect + if self.device_info.type == 0x1825: + # 0x1825 uses chunk 0x26 (not 0x4e) with adjusted thresholds + chunks += [[ + 0x26, + unhexlify( + 'fbb20f0000000f00300000005400020034000a00018000000a0200000b19000050c360ea01091000' + ) + ]] + else: + # This type of fragment is not present in the debugging dump routine. + # It seems to be only used for identification and it looks almost identical to Finger Detect (0x26) + # Seems to be the same all the time for a given sensor and mostly hardcoded + # TODO: analyse construct_wtf_4e @0000000180090BF0 + chunks += [[ + 0x4e, + unhexlify( + 'fbb20f0000000f00300000008700020067000a00018000000a0200000b1900008813b80b01091000' + ) + ]] # Image Reconstruction. # TODO: analyse add_image_reconstruction_cmd_02_buff_list_item @000000018008EA70 chunks += [[ 0x2e, unhexlify('0200180002000000700070004d010000a0008c003c32321e3c0a0202') ]] elif mode == CaptureMode.ENROLL: - chunks += [[ - 0x26, - unhexlify( - 'fbb20f0000000f00300000008700020067000a00018000000a0200000b19000050c360ea01091000' - ) - ]] + if self.device_info.type == 0x1825: + chunks += [[ + 0x26, + unhexlify( + 'fbb20f0000000f00300000005400020034000a00018000000a0200000b19000050c360ea01091000' + ) + ]] + else: + chunks += [[ + 0x26, + unhexlify( + 'fbb20f0000000f00300000008700020067000a00018000000a0200000b19000050c360ea01091000' + ) + ]] # Image Reconstruction. There is only one byte difference with the "identify" version. (same is true for 0097) chunks += [[ 0x2e, unhexlify('0200180023000000700070004d010000a0008c003c32321e3c0a0202') From 284cd34d99d2619a339cee33e33c16add68532d5 Mon Sep 17 00:00:00 2001 From: Milad Olad Date: Wed, 27 May 2026 18:21:34 -0400 Subject: [PATCH 3/3] Add USB device reset on initialization for 138a:0092 The 0092 sensor requires a USB device reset before establishing TLS communication, otherwise the first USB bulk transfer times out. This adds a reset + 1 second delay in init.open() when the device is detected. This workaround is necessary on Linux where the sensor may be in a stale state from a previous session or from being previously claimed by another driver. --- validitysensor/init.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/validitysensor/init.py b/validitysensor/init.py index 1153cf7..2cc77f5 100644 --- a/validitysensor/init.py +++ b/validitysensor/init.py @@ -46,6 +46,19 @@ def open_common(): def open(): + # Reset the USB device first to clear any stale state from previous sessions + import usb.core as ucore + import time + dev = ucore.find(custom_match=lambda d: (d.idVendor, d.idProduct) in + {(0x138a, 0x0090), (0x138a, 0x0097), (0x138a, 0x009d), + (0x138a, 0x0092), (0x06cb, 0x009a)}) + if dev is not None: + try: + dev.reset() + del dev + time.sleep(1) + except Exception: + pass usb.open() open_common()