- Introduced helper functions in validity_tls.c to encapsulate command sending and response handling for TLS flash read and handshake processes, reducing code duplication and enhancing clarity.
- Reorganized the validity_verify.c file by creating dedicated functions for each step in the verification process, including capture building, matching, and result retrieval, to streamline the state machine logic.
- Enhanced the delete and clear operations in validity_verify.c by modularizing the code into smaller functions, improving readability and maintainability.
- Updated the list operations in validity_verify.c to utilize helper functions for fetching user storage and user details, simplifying the state management.
- Overall, these changes aim to improve the structure of the code, making it easier to follow and modify in the future.
Apply uncrustify formatting to all validity driver and test files
to pass the CI test_indent check.
Fix two pre-existing test failures:
- test-validity-capture: LED command blobs are 125 bytes, not 128
- test-validity-enroll: add 2-byte length prefix to test data to
match parser's expected format, fix empty-data assertion (parser
returns FALSE for data_len < 2)
All 41 tests pass, 0 failures.
Add init_hardcoded and init_clean_slate transmission to the open SSM.
Four new states (OPEN_SEND/RECV_INIT_HARDCODED, OPEN_SEND/RECV_INIT_
CLEAN_SLATE) between GET_FW_INFO and UPLOAD_FWEXT, matching the
python-validity send_init() flow. init_hardcoded is always sent;
clean_slate only when fwext is not loaded. Skipped in emulation mode.
Remove dead crt_hardcoded[] (420 bytes, G_GNUC_UNUSED) from
validity_tls.c — this CA cert data now lives exclusively in
validity_pair_constants.inc.
Expose enrollment response parser for unit testing:
- EnrollmentUpdateResult struct and ENROLLMENT_MAGIC_LEN moved to
validity.h
- parse_enrollment_update_response() and enrollment_update_result_clear()
no longer static
Remove in-tree doc/ directory — documentation lives in
../validity-artifacts/docs/.
Tests: 9 new enrollment parser test cases, 0 regressions.
Result: 41 OK, 0 Fail, 2 Skipped.
Add HAL (validity_hal.h/c) with per-device lookup table for 4 PIDs
(0090, 0097, 009a, 009d). Each entry holds init_hardcoded, clean_slate,
reset_blob, db_write_enable blobs and a flash layout with partition
table + RSA signature.
Add device pairing SSM (validity_pair.h/c) — a 30-state machine that
runs as a child of the open SSM when the sensor has no TLS partitions.
Phases: raw USB keygen + partition flash, TLS handshake, erase 5
partitions, write 4096-byte TLS flash image, reboot.
Integration:
- OPEN_PAIR state in open SSM (between FWEXT and TLS_READ_FLASH)
- Skipped in emulation, no-fwext, or already-paired cases
- Post-reboot returns FP_DEVICE_ERROR_REMOVED for fprintd retry
Migration:
- validity_db.c and validity_fwext.c now use HAL lookups
- Removed hardcoded validity_blob_dbe_009a.inc
Tests: 24 new test cases (10 HAL + 14 pairing), 0 regressions.
Result: 40 OK, 0 Fail, 2 Skipped.
Comprehensive bugfix for issues found during code audit:
1. parse_match_result (CRITICAL): Replace dead while loop + hardcoded
offsets with proper TLV dict parsing (tag LE16 + len LE16 + data)
matching python-validity's parse_dict(). Extracts user_dbid (tag 1),
subtype (tag 3), and hash (tag 4) from match result.
2. ENROLL_CREATE_USER (CRITICAL): Fix NULL user_id crash —
g_uuid_string_random() now generates UUID for user identity instead
of passing NULL to g_variant_new_string().
3. Identify gallery matching: Match sensor result against gallery by
comparing finger subtype instead of always returning first entry.
4. Field abuse: Add dedicated enroll_user_dbid field to FpiDeviceValidity
instead of reusing delete_storage_dbid for enrollment state.
5. Delete SSM: Full implementation — enumerate users via get_user_storage,
iterate users to find matching finger subtype, delete via cmd 0x48
(del_record). Proper error handling for missing records.
6. match_finger double allocation: Remove redundant 12-byte alloc/free,
single clean 13-byte allocation per python-validity format.
7. clear_storage: Full SSM implementation — enumerate user storage,
del_record for each user. Replaces NOT_SUPPORTED stub.
8. Clean stale TODO/placeholder comments.
All 37 tests pass (0 fail, 2 skip — unchanged baseline).
Add core fingerprint operations: enrollment, verification, identification,
print listing, print deletion, and storage clearing.
New files:
- validity_db.h/c: On-chip template database operations — command builders
for all DB commands (0x45-0x4B, 0x47-0x48, 0x51, 0x5E, 0x60, 0x62, 0x63,
0x64, 0x68, 0x69, 0x6B), response parsers for DB info/user storage/user/
record value/record children/new record ID, identity builder (UUID→VCSFW
binary), finger data builder, and db_write_enable blob accessor.
- validity_enroll.c: 31-state enrollment SSM with interrupt-driven finger
detection (EP 0x83), capture command orchestration via build_cmd_02(),
enrollment session management (create/update/commit), DB record creation
(user + finger), and LED glow feedback.
- validity_verify.c: 17-state verify/identify SSM with match command
dispatching (cmd 0x5E for verify, cmd 0x60 for identify), 6-state list
SSM for enumerating enrolled prints via GPtrArray, 8-state delete SSM,
and clear_storage stub.
Modified files:
- validity.h: Added DB header include, 5 new state enums (CalibState,
EnrollState, VerifyState, ListState, DeleteState), new struct fields
for enrollment/verification/list/delete state, function declarations.
- validity.c: Replaced all operation stubs with real implementations,
added cleanup for new fields in dev_close, wired all FpDevice methods.
- meson.build: Added 3 new source files to driver.
- tests/meson.build: Added test-validity-db executable.
- tests/validity/custom.py: Updated feature assertions (STORAGE,
STORAGE_LIST, STORAGE_CLEAR now enabled).
Tests: 29 new unit tests in test-validity-db.c covering all command
builders, response parsers, identity/finger data builders, and blob
accessor. All 37 tests pass (0 fail, 2 skip).
Implement the capture command building infrastructure ported from
python-validity's sensor.py and timeslot.py. This provides all the
algorithms needed to construct sensor capture commands for calibration,
enrollment, and identification modes.
New components:
- TLV chunk parsing (split/merge) for capture programs
- Timeslot DSP instruction decoder (16 opcodes, 1-3 bytes each)
- Timeslot table patching (Call repeat multiplication, factory cal)
- Line Update Type 1 algorithm for 0xb5-class sensors
- build_cmd_02(): main capture command builder
- Factory bits parsing (subtag 3 cal values, subtag 7 cal data)
- Frame averaging with multi-line deinterlacing
- Calibration data processing (scale/accumulate/clip)
- Clean slate format with SHA256 verification
- Bitpack compression for factory calibration values
- Finger ID mapping (FpFinger <-> VCSFW subtype 1-10)
- LED control commands (glow_start_scan, glow_end_scan)
- CaptureProg lookup table for firmware 6.x type-1 devices
- OPEN_CAPTURE_SETUP state in the open SSM
27 unit tests covering all components.
Full test suite: 36 OK, 0 Fail, 2 Skipped.
- Migrate from deprecated HMAC_* API to EVP_MAC (OpenSSL 3.x):
tls_hmac_sign(), validity_tls_prf(), handle_priv_block()
- Remove unused ec_privkey_from_coords() function
- Remove unused x_le/y_le variables in handle_priv_block()
- Fix const discard in OSSL_PARAM_utf8_string() call
- Restore crt_hardcoded[] with G_GNUC_UNUSED (needed in Iter 6)
Implement the firmware extension (fwext) upload module for
Validity/Synaptics VCSFW sensors. When the sensor reports no
firmware loaded (GET_FW_INFO returns status 0xB004), the driver
uploads the .xpfwext firmware file using the following sequence:
1. WRITE_HW_REG32 (0x08) to prepare hardware register
2. READ_HW_REG32 (0x07) to verify register state
3. Load .xpfwext file from filesystem search paths
4. For each 4KB chunk:
a. Send db_write_enable blob (encrypted auth token)
b. WRITE_FLASH (0x41) with chunk payload
c. CLEANUP (0x1A) to commit chunk
5. WRITE_FW_SIG (0x42) to upload RSA signature
6. GET_FW_INFO (0x43) to verify successful upload
7. REBOOT (0x05 0x02 0x00) to activate new firmware
Architecture: Uses the NULL-callback subsm pattern where SEND
states call vcsfw_cmd_send(self, ssm, cmd, len, NULL) and RECV
states read self->cmd_response_status/data directly. This avoids
the double-advance bug with fpi_ssm_start_subsm auto-advancing
the parent.
New files:
- validity_fwext.h: Structures, SSM state enum, API declarations
- validity_fwext.c: Upload SSM, file parser, command builders
- validity_blob_dbe_009a.inc: db_write_enable blob for 06cb:009a
- test-validity-fwext.c: 19 unit tests covering all pure functions
Modified files:
- validity.h: Add cmd_response_status field to FpiDeviceValidity
- validity.c: Add OPEN_UPLOAD_FWEXT state to open sequence
- vcsfw_protocol.c: Save status in cmd_receive_cb for RECV states
- meson.build: Add validity_fwext.c to driver sources
Test results: 34 OK, 0 Fail, 2 Skipped
Add a new "validity" driver for Validity/Synaptics fingerprint sensors
that use the VCSFW protocol (as opposed to BMKT). This is iteration 1
of a multi-phase effort to bring native libfprint support to these
widely-deployed sensors found in ThinkPad T480/T480s/T580/X1 Carbon
Gen6 and many other laptops.
This initial iteration implements:
- VCSFW command/response transport layer over USB bulk endpoints
- GET_VERSION command parsing (firmware version, product ID, build)
- Synchronous probe and async open/close state machines
- Stub implementations for enroll/verify/identify (return NOT_SUPPORTED)
- umockdev replay test with real hardware capture
Supported USB IDs (VCSFW protocol):
- 138a:0090 (Validity VFS7500)
- 138a:0097 (Validity VFS5011)
- 06cb:009a (Synaptics Metallica MIS Touch)
- 138a:009d (Validity VFS7552)
These were previously (incorrectly) claimed by the synaptics driver
which uses the BMKT protocol.
Re-record the umockdev USB capture to include the new
GOODIX_VERIFY_WAIT_FINGER_UP state's USB exchanges during
verify and identify operations.
Captured on a Goodix MOC sensor (27c6:609c, Framework Laptop 13)
with FP_DEVICE_EMULATION=1 as required by the test framework.
The goodixmoc driver's verify state machine was missing a
WAIT_FINGER_UP state between IDENTIFY and PWR_BTN_SHIELD_OFF.
After the sensor reported a match/no-match result, the driver
immediately completed the verify operation without waiting for
the finger to be lifted from the sensor.
This caused problems when fprintd retried verification after a
no-match: the finger was still on the sensor, so the same
(wrong) finger was scanned again instantly. On fast match-on-chip
readers, all retry attempts could be exhausted in under a second,
giving the user no time to reposition their finger.
Add GOODIX_VERIFY_WAIT_FINGER_UP to the verify state machine,
mirroring the existing GOODIX_ENROLL_WAIT_FINGER_UP in the
enroll flow. The new state sends MOC_CMD0_FINGER_MODE /
MOC_CMD1_SET_FINGER_UP to the sensor and waits for the finger
to be removed before advancing to PWR_BTN_SHIELD_OFF.
The callback handles the same cases as the enroll equivalent:
- Timeout (5s): retry the finger-up wait
- Protocol error: fail the SSM
- Success: clear FINGER_STATUS_PRESENT and advance
Link: https://gitlab.freedesktop.org/libfprint/fprintd/-/issues/209
This FPreader was found on Minisforum X1 Pro MiniPC, and it is
listed by lsusb as "Generic Realtek USB2.0 Finger Print Bridge".
Though the VID is not the same as the well-known "0bda", we can
confirm it's from Realtek by analyzing Windows version driver.
Signed-off-by: Ryan Wong <colorfulshark@gmail.com>