From c89d841c174656a95861a494f2956b2ab72f6770 Mon Sep 17 00:00:00 2001 From: Efe E Date: Tue, 26 May 2026 21:55:04 +0100 Subject: [PATCH 01/26] PR for libfprint --- README.md | 69 ++++++++- meson.build | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 486 insertions(+), 8 deletions(-) create mode 100644 meson.build diff --git a/README.md b/README.md index dbea943..102052e 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ ## Status: **WORKING** ✓ -Enrollment and verify confirmed working on real hardware (2026-03-10). +Enrollment and verify confirmed working on real hardware (2026-05-26). ## Hardware -[TNP Nano USB Fingerprint Reader](https://www.amazon.com/dp/B07DW62XS7) (Amazon) +[TNP Nano USB Fingerprint Reader](https://www.amazon.com/dp/B07DW62XS7) (Amazon US) +[TNP Nano USB Fingerprint Reader](https://www.amazon.co.uk/TNP-Fingerprint-Reader-Windows-Hello/dp/B07DW62XS7/) (Amazon UK) TNP Nano USB Fingerprint Reader @@ -45,27 +46,79 @@ See [Reverse Engineering](docs/reverse-engineering.md) for how to reproduce. - The required number of GenChar samples for RegModel is `DAT_180032020 / 3`clamped to \[3, 6\]. For this device the value is 6. - Extra GET_IMAGE calls between GenChars corrupt the device's char buffer state. The `waiting_for_lift` approach must minimize GET_IMAGE polling between captures. -## Build +## Instructions +1. Update your machine ```bash -./build.sh +sudo apt update ``` -Copies `src/microarray.c` into the libfprint source tree (`$LIBFPRINT_SRC`, defaults to `~/libfprint`), builds with ninja, and installs the shared library. +2. Install all required tools/libraries -## Testing +```bash +sudo apt install -y git build-essential meson ninja-build \ + pkg-config libglib2.0-dev libgusb-dev libgudev-1.0-dev \ + libpixman-1-dev libnss3-dev libssl-dev libcairo2-dev \ + libgirepository1.0-dev gtk-doc-tools \ + fprintd libpam-fprintd + ``` + +3. Clone the official libfprint source code to ~/libfprint directory + +```bash +git clone https://gitlab.freedesktop.org/libfprint/libfprint.git ~/libfprint +``` + +4. Manually create the microarray directory + +```bash +cd ~/libfprint +mkdir -p libfprint/drivers/microarray +meson setup build +``` + +5. Copy this repo + +```bash +git clone https://github.com/jdillon/libfprint-microarray.git ~/libfprint-microarray +``` + +6. Make changes to ~/libfprint ```bash +# copy fixed meson.build file over to other library +cp ~/libfprint-microarray/meson.build ~/libfprint/libfprint/meson.build +cp ~/libfprint-microarray/src/microarray.c ~/libfprint/libfprint/drivers/microarray/microarray.c + +cd ~/libfprint/build +meson configure -Ddrivers=all +ninja +sudo cp libfprint/libfprint-2.so.2.0.0 /usr/lib/x86_64-linux-gnu/libfprint-2.so.2 +sudo cp libfprint/libfprint-2.so.2.0.0 /usr/lib/libfprint-2.so.2 +sudo ldconfig +sudo udevadm control --reload-rules && sudo udevadm trigger +#sudo systemctl enable --now fprintd +``` + +## Testing +```bash + +# Restart daemon +sudo systemctl stop fprintd +sudo G_MESSAGES_DEBUG=all /usr/libexec/fprintd -t 2>&1 + # Enroll right index finger (6 press/lift cycles) fprintd-enroll -f right-index-finger # Verify fprintd-verify -f right-index-finger -# Debug logging -sudo G_MESSAGES_DEBUG=all /usr/libexec/fprintd -t 2>&1 ``` +**If there is a future update of libfprint, re-running Step (6) above should make everything work again.** + + + ## License [MIT](LICENSE) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..32ed7dd --- /dev/null +++ b/meson.build @@ -0,0 +1,425 @@ +spi_sources = [] +spi_headers = [] + +if enabled_spi_drivers.length() > 0 + spi_headers = ['fpi-spi-transfer.h'] + spi_sources = ['fpi-spi-transfer.c'] +endif + +libfprint_sources = [ + 'fp-context.c', + 'fp-device.c', + 'fp-image.c', + 'fp-print.c', + 'fp-image-device.c', +] + +libfprint_private_sources = [ + 'fpi-assembling.c', + 'fpi-byte-reader.c', + 'fpi-byte-writer.c', + 'fpi-device.c', + 'fpi-image-device.c', + 'fpi-image.c', + 'fpi-print.c', + 'fpi-ssm.c', + 'fpi-usb-transfer.c', +] + spi_sources + +libfprint_public_headers = [ + 'fp-context.h', + 'fp-device.h', + 'fp-image-device.h', + 'fp-image.h', + 'fp-print.h', +] + +libfprint_private_headers = [ + 'fpi-assembling.h', + 'fpi-byte-reader.h', + 'fpi-byte-utils.h', + 'fpi-byte-writer.h', + 'fpi-compat.h', + 'fpi-context.h', + 'fpi-device.h', + 'fpi-image-device.h', + 'fpi-image.h', + 'fpi-log.h', + 'fpi-minutiae.h', + 'fpi-print.h', + 'fpi-usb-transfer.h', + 'fpi-ssm.h', +] + spi_headers + +nbis_sources = [ + 'nbis/bozorth3/bozorth3.c', + 'nbis/bozorth3/bz_alloc.c', + 'nbis/bozorth3/bz_drvrs.c', + 'nbis/bozorth3/bz_gbls.c', + 'nbis/bozorth3/bz_io.c', + 'nbis/bozorth3/bz_sort.c', + 'nbis/mindtct/binar.c', + 'nbis/mindtct/block.c', + 'nbis/mindtct/chaincod.c', + 'nbis/mindtct/contour.c', + 'nbis/mindtct/detect.c', + 'nbis/mindtct/dft.c', + 'nbis/mindtct/free.c', + 'nbis/mindtct/getmin.c', + 'nbis/mindtct/globals.c', + 'nbis/mindtct/imgutil.c', + 'nbis/mindtct/init.c', + 'nbis/mindtct/line.c', + 'nbis/mindtct/link.c', + 'nbis/mindtct/log.c', + 'nbis/mindtct/loop.c', + 'nbis/mindtct/maps.c', + 'nbis/mindtct/matchpat.c', + 'nbis/mindtct/minutia.c', + 'nbis/mindtct/morph.c', + 'nbis/mindtct/quality.c', + 'nbis/mindtct/remove.c', + 'nbis/mindtct/ridges.c', + 'nbis/mindtct/shape.c', + 'nbis/mindtct/sort.c', + 'nbis/mindtct/util.c', + 'nbis/mindtct/xytreps.c', +] + +driver_sources = { + 'upekts' : + [ 'drivers/upekts.c', 'drivers/upek_proto.c' ], + 'upektc' : + [ 'drivers/upektc.c' ], + 'upeksonly' : + [ 'drivers/upeksonly.c' ], + 'uru4000' : + [ 'drivers/uru4000.c' ], + 'aes1610' : + [ 'drivers/aes1610.c' ], + 'aes1660' : + [ 'drivers/aes1660.c' ], + 'aes2501' : + [ 'drivers/aes2501.c' ], + 'aes2550' : + [ 'drivers/aes2550.c' ], + 'aes2660' : + [ 'drivers/aes2660.c' ], + 'aes3500' : + [ 'drivers/aes3500.c' ], + 'aes4000' : + [ 'drivers/aes4000.c' ], + 'vcom5s' : + [ 'drivers/vcom5s.c' ], + 'vfs101' : + [ 'drivers/vfs101.c' ], + 'vfs301' : + [ 'drivers/vfs301.c', 'drivers/vfs301_proto.c' ], + 'vfs5011' : + [ 'drivers/vfs5011.c' ], + 'vfs7552' : + [ 'drivers/vfs7552.c' ], + 'upektc_img' : + [ 'drivers/upektc_img.c', 'drivers/upek_proto.c' ], + 'etes603' : + [ 'drivers/etes603.c' ], + 'egis0570' : + [ 'drivers/egis0570.c' ], + 'egismoc' : + [ 'drivers/egismoc/egismoc.c' ], + 'vfs0050' : + [ 'drivers/vfs0050.c' ], + 'elan' : + [ 'drivers/elan.c' ], + 'elanmoc' : + [ 'drivers/elanmoc/elanmoc.c' ], + 'elanspi' : + [ 'drivers/elanspi.c' ], + 'nb1010' : + [ 'drivers/nb1010.c' ], + 'virtual_image' : + [ 'drivers/virtual-image.c' ], + 'virtual_device' : + [ 'drivers/virtual-device.c' ], + 'virtual_device_storage' : + [ 'drivers/virtual-device-storage.c' ], + 'synaptics' : + [ 'drivers/synaptics/synaptics.c', 'drivers/synaptics/bmkt_message.c' ], + 'goodixmoc' : + [ 'drivers/goodixmoc/goodix.c', 'drivers/goodixmoc/goodix_proto.c' ], + 'fpcmoc' : + [ 'drivers/fpcmoc/fpc.c' ], + 'realtek' : + [ 'drivers/realtek/realtek.c' ], + 'focaltech_moc' : + [ 'drivers/focaltech_moc/focaltech_moc.c' ], + 'microarray' : + [ 'drivers/microarray/microarray.c' ], +} + +helper_sources = { + 'aeslib' : + [ 'drivers/aeslib.c' ], + 'aesx660' : + [ 'drivers/aesx660.c' ], + 'aes3k' : + [ 'drivers/aes3k.c' ], + 'openssl' : + [ ], + 'udev' : + [ ], + 'virtual' : + [ 'drivers/virtual-device-listener.c' ], +} + +drivers_sources = [] +drivers_cflags = [] +foreach driver: drivers + drivers_sources += driver_sources[driver] +endforeach + +drivers_sources += driver_sources['microarray'] +foreach helper : driver_helpers + drivers_sources += helper_sources[helper] +endforeach + + +fp_enums = gnome.mkenums_simple('fp-enums', + sources: libfprint_public_headers, + install_header: true, + install_dir: get_option('includedir') / versioned_libname, +) +fp_enums_h = fp_enums[1] + +fpi_enums = gnome.mkenums_simple('fpi-enums', + sources: libfprint_private_headers, + install_header: false, +) +fpi_enums_h = fpi_enums[1] + +enums_dep = declare_dependency( + sources: [ fp_enums_h, fpi_enums_h ] +) + +# Export the drivers' types to the core code +drivers_type_list = [] +drivers_type_func = [] +drivers_type_list += '#include ' +drivers_type_list += '#include "fpi-context.h"' +drivers_type_list += '' +drivers_type_func += 'GArray *' +drivers_type_func += 'fpi_get_driver_types (void)' +drivers_type_func += '{' +drivers_type_func += ' GArray *drivers = g_array_new (TRUE, FALSE, sizeof (GType));' +drivers_type_func += ' GType t;' +drivers_type_func += '' + +supported_drivers += 'microarray' + +foreach driver: supported_drivers + drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);' + drivers_type_func += ' t = fpi_device_' + driver + '_get_type ();' + drivers_type_func += ' g_array_append_val (drivers, t);' + drivers_type_func += '' +endforeach +drivers_type_list += '' +drivers_type_func += ' return drivers;' +drivers_type_func += '}' + +drivers_sources += configure_file(input: 'empty_file', + output: 'fpi-drivers.c', + capture: true, + command: [ + 'echo', + '\n'.join(drivers_type_list + [] + drivers_type_func) + ]) + +deps = [ + enums_dep, + gio_dep, + glib_dep, + gobject_dep, + gusb_dep, + mathlib_dep, +] + optional_deps + +# These are empty and only exist so that the include directories are created +# in the build tree. This silences a build time warning. +subdir('nbis/include') +subdir('nbis/libfprint-include') +deps += declare_dependency(include_directories: [ + root_inc, + include_directories('nbis/include'), + include_directories('nbis/libfprint-include'), +]) + +libnbis = static_library('nbis', + nbis_sources, + dependencies: deps, + c_args: cc.get_supported_arguments([ + '-Wno-error=redundant-decls', + '-Wno-redundant-decls', + '-Wno-discarded-qualifiers', + '-Wno-array-bounds', + '-Wno-array-parameter', + '-Wno-unused-but-set-variable', + ]), + install: false) + +libfprint_private = static_library('fprint-private', + sources: [ + fpi_enums, + libfprint_private_sources, + ], + dependencies: deps, + link_with: libnbis, + install: false) + +libfprint_drivers = static_library('fprint-drivers', + sources: drivers_sources, + c_args: drivers_cflags, + dependencies: deps, + link_with: libfprint_private, + install: false) + +mapfile = files('libfprint.ver')[0] +if meson.version().version_compare('>=1.4') + mapfile_path = mapfile.full_path() +else + mapfile_path = meson.project_source_root() / '@0@'.format(mapfile) +endif +vflag = '-Wl,--version-script,@0@'.format(mapfile_path) + +libfprint = shared_library(versioned_libname.split('lib')[1], + sources: [ + fp_enums, + libfprint_sources, + ], + soversion: soversion, + version: libversion, + link_args : vflag, + link_depends : mapfile, + link_with: [libfprint_drivers, libfprint_private], + dependencies: deps, + install: true) + +libfprint_dep = declare_dependency(link_with: libfprint, + include_directories: root_inc, + dependencies: [ + enums_dep, + gio_dep, + glib_dep, + gobject_dep, + gusb_dep, + ]) + +install_headers(['fprint.h'] + libfprint_public_headers, + subdir: versioned_libname +) + +libfprint_private_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: libfprint_private, + dependencies: [ + deps, + libfprint_dep, + ] +) + +udev_hwdb = executable('fprint-list-udev-hwdb', + 'fprint-list-udev-hwdb.c', + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, + install: false) + +udev_hwdb_generator = custom_target('udev-hwdb', + output: 'autosuspend.hwdb', + depend_files: drivers_sources, + capture: true, + command: [ udev_hwdb ], + install: false, +) + +metainfo = executable('fprint-list-metainfo', + 'fprint-list-metainfo.c', + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, + install: false) + +metainfo_generator = custom_target('metainfo', + output: 'org.freedesktop.libfprint.metainfo.xml', + depend_files: drivers_sources, + capture: true, + command: [ metainfo ], + install: true, + install_dir: datadir / 'metainfo' +) + +if install_udev_rules + udev_rules = executable('fprint-list-udev-rules', + 'fprint-list-udev-rules.c', + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, + install: false) + + custom_target('udev-rules', + output: '70-@0@.rules'.format(versioned_libname), + depend_files: drivers_sources, + capture: true, + command: [ udev_rules ], + install: true, + install_dir: udev_rules_dir, + ) +endif + +sync_udev_udb = custom_target('sync-udev-hwdb', + depends: udev_hwdb_generator, + output: 'sync-udev-hwdb', + install: false, + command: [ + 'cp', '-v', + udev_hwdb_generator.full_path(), + meson.project_source_root() / 'data' + ] +) + +alias_target('sync-udev-hwdb', sync_udev_udb) + +supported_devices = executable('fprint-list-supported-devices', + 'fprint-list-supported-devices.c', + dependencies: libfprint_private_dep, + link_with: libfprint_drivers, + install: false) + + +if get_option('introspection') + # We do *not* include the private header here + libfprint_girtarget = gnome.generate_gir(libfprint, + sources : fp_enums + [ + libfprint_public_headers, + libfprint_sources, + ], + nsversion : '@0@.0'.format(soversion), + namespace : 'FPrint', + symbol_prefix : 'fp_', + identifier_prefix : 'Fp', + export_packages : 'fprint', + extra_args : [ + '--c-include=fprint.h', + ], + link_with : libfprint, + dependencies : [ + gio_dep, + gobject_dep, + gusb_dep, + ], + includes : [ + 'Gio-2.0', + 'GObject-2.0', + 'GUsb-1.0', + ], + fatal_warnings: true, + install : true) + libfprint_gir = libfprint_girtarget[0] + libfprint_typelib = libfprint_girtarget[1] +endif From 66027a9babbbe27ff7b0816ab287051e96d6e943 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Mon, 1 Jun 2026 11:53:39 +0100 Subject: [PATCH 02/26] Change microarray.c so you can enrol multiple fingerprints --- src/microarray.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index d85c6c2..7f962fd 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -420,25 +420,38 @@ enroll_run_state (FpiSsm *ssm, FpDevice *device) break; case ENROLL_EMPTY: { - /* Check bitmap from pre-enrollment ReadIndex */ + /* Check bitmap from pre-enrollment ReadIndex to find a free slot */ const guint8 *resp = self->resp_buf + MA_OVERHEAD; + self->fid = -1; + if (resp[0] == 0x00) { for (int byte = 0; byte < 4 && self->fid < 0; byte++) { for (int bit = 0; bit < 8; bit++) { + int candidate_slot = byte * 8 + bit; + + /* STRICT CAP: Your chip only supports up to slot 9 (10 fingers total) */ + if (candidate_slot > 9) { + break; + } + + /* If this bit is 0, the slot is empty! Let's claim it. */ if (!(resp[1 + byte] & (1 << bit))) { - self->fid = byte * 8 + bit; + self->fid = candidate_slot; break; } } } } - if (self->fid >= 0) { - fp_dbg ("FID slot %d free, skipping Empty", self->fid); + + /* If we found a valid free slot (0-9), skip erasing and proceed to scan */ + if (self->fid >= 0 && self->fid <= 9) { + fp_dbg ("Found free FID slot %d. Proceeding to image capture.", self->fid); fpi_ssm_jump_to_state (ssm, ENROLL_GET_IMAGE); return; } - /* No free slots — erase all templates, use slot 0 */ - fp_dbg ("no free FID slots, clearing all templates (CMD 0x0D)"); + + /* FALLBACK: If slots 0-9 are completely full, nuke the chip to start fresh */ + fp_dbg ("Storage slots 0-9 are completely full! Clearing all templates."); self->fid = 0; cmd[0] = MA_CMD_EMPTY; ma_submit_cmd (ssm, device, cmd, 1); From 16676247cefb090df74f0128a379f47bfcbadd9e Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Mon, 1 Jun 2026 12:10:15 +0100 Subject: [PATCH 03/26] updated steps in README.md for this to actually work! --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 102052e..519c204 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ meson setup build 5. Copy this repo ```bash -git clone https://github.com/jdillon/libfprint-microarray.git ~/libfprint-microarray +git clone https://github.com/jadegamesuk/libfprint-microarray.git ~/libfprint-microarray ``` 6. Make changes to ~/libfprint @@ -102,17 +102,19 @@ sudo udevadm control --reload-rules && sudo udevadm trigger ## Testing ```bash - # Restart daemon sudo systemctl stop fprintd sudo G_MESSAGES_DEBUG=all /usr/libexec/fprintd -t 2>&1 +``` + +### Open a new Terminal Window for the following +```bash # Enroll right index finger (6 press/lift cycles) fprintd-enroll -f right-index-finger # Verify fprintd-verify -f right-index-finger - ``` **If there is a future update of libfprint, re-running Step (6) above should make everything work again.** From 9aff905b91aaf174a219419f310b7c4bfc82e85e Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Mon, 1 Jun 2026 12:33:39 +0100 Subject: [PATCH 04/26] Cleaning up README.md steps even more --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 519c204..650ec1a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ See [Reverse Engineering](docs/reverse-engineering.md) for how to reproduce. - Extra GET_IMAGE calls between GenChars corrupt the device's char buffer state. The `waiting_for_lift` approach must minimize GET_IMAGE polling between captures. ## Instructions -1. Update your machine +1. Plug in device & update your machine ```bash sudo apt update @@ -66,6 +66,7 @@ sudo apt install -y git build-essential meson ninja-build \ 3. Clone the official libfprint source code to ~/libfprint directory ```bash +rm -rf ~/libfprint git clone https://gitlab.freedesktop.org/libfprint/libfprint.git ~/libfprint ``` @@ -80,6 +81,7 @@ meson setup build 5. Copy this repo ```bash +rm -rf ~/libfprint-microarray/ git clone https://github.com/jadegamesuk/libfprint-microarray.git ~/libfprint-microarray ``` @@ -115,6 +117,18 @@ fprintd-enroll -f right-index-finger # Verify fprintd-verify -f right-index-finger + +# Options for [finger]: +left-thumb +left-index-finger +left-middle-finger +left-ring-finger +left-little-finger +right-thumb +right-index-finger +right-middle-finger +right-ring-finger +right-little-finger ``` **If there is a future update of libfprint, re-running Step (6) above should make everything work again.** From cafe3353c7e7ce5acfe41c2179716e2ae948c71c Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 14:59:06 +0100 Subject: [PATCH 05/26] allowing ability to delete fingerprints --- src/microarray.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/microarray.c b/src/microarray.c index 7f962fd..c25551a 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -734,6 +734,58 @@ ma_verify (FpDevice *device) fpi_ssm_start (ssm, verify_ssm_done); } +/* -------------------------------------------------------------------------- + * Delete state machine + * -------------------------------------------------------------------------- */ + +enum { + DELETE_EMPTY, /* CMD 0x0D — running onboard sensor flash format */ + DELETE_RECV_EMPTY, + DELETE_NUM_STATES, +}; + +static void +delete_run_state (FpiSsm *ssm, FpDevice *device) +{ + guint8 cmd[1]; + + switch (fpi_ssm_get_cur_state (ssm)) { + + case DELETE_EMPTY: + cmd[0] = MA_CMD_EMPTY; + ma_submit_cmd (ssm, device, cmd, 1); + break; + + case DELETE_RECV_EMPTY: + ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); + break; + + default: + g_assert_not_reached (); + } +} + +static void +delete_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); + + if (!error && self->resp_buf[MA_OVERHEAD] != 0x00) { + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Delete Empty command failed: 0x%02x", + self->resp_buf[MA_OVERHEAD]); + } + + fpi_device_delete_complete (device, error); +} + +static void +ma_delete (FpDevice *device) +{ + FpiSsm *ssm = fpi_ssm_new (device, delete_run_state, DELETE_NUM_STATES); + fpi_ssm_start (ssm, delete_ssm_done); +} + /* -------------------------------------------------------------------------- * GObject boilerplate * -------------------------------------------------------------------------- */ @@ -779,6 +831,7 @@ fpi_device_microarray_class_init (FpiDeviceMicroarrayClass *klass) dev_class->close = ma_dev_close; dev_class->enroll = ma_enroll; dev_class->verify = ma_verify; + dev_class->delete = ma_delete; fpi_device_class_auto_initialize_features (dev_class); } From 7489c2b1113cf70cb3513fccfac46f08a6d80679 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 15:24:29 +0100 Subject: [PATCH 06/26] Change to allow for cancellation to not cause "device or resource is busy" message --- src/microarray.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index c25551a..e83bdfb 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -312,7 +312,7 @@ cmd_send_cb (FpiUsbTransfer *transfer, FpDevice *device, fpi_ssm_next_state (ssm); } -/* Submit a bulk-OUT command */ +/* Submit a bulk-OUT command with cancellation support */ static void ma_submit_cmd (FpiSsm *ssm, FpDevice *device, const guint8 *cmd, gsize cmd_len) @@ -323,24 +323,30 @@ ma_submit_cmd (FpiSsm *ssm, FpDevice *device, transfer->ssm = ssm; fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_OUT, pkt, pkt_len, g_free); - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, NULL, + + /* Swap NULL out for fpi_device_get_cancellable */ + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, + fpi_device_get_cancellable (device), cmd_send_cb, ssm); } -/* Submit a bulk-IN read into self->resp_buf */ +/* Submit a bulk-IN read with cancellation support */ static void ma_submit_recv (FpiSsm *ssm, FpDevice *device, gsize expect_len) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); FpiUsbTransfer *transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; - /* Point transfer buffer at our persistent resp_buf */ fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_IN, self->resp_buf, expect_len, NULL); - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, NULL, + + /* Swap NULL out for fpi_device_get_cancellable */ + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, + fpi_device_get_cancellable (device), cmd_recv_cb, ssm); } + /* -------------------------------------------------------------------------- * Enroll state machine * -------------------------------------------------------------------------- From 895f0391fcb2ccb31b0185b7090a61eb845e9895 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:11:18 +0100 Subject: [PATCH 07/26] fixed issue where only the first fingerprint worked with sudo --- src/microarray.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index e83bdfb..61f3f07 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -679,19 +679,12 @@ verify_run_state (FpiSsm *ssm, FpDevice *device) fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - /* Get FID from enrolled print */ - FpPrint *print = NULL; - fpi_device_get_verify_data (device, &print); - GVariant *data = NULL; - g_object_get (print, "fpi-data", &data, NULL); - gint fid = 0; - g_variant_get (data, "(i)", &fid); - self->fid = fid; /* CMD 0x66 fid_hi fid_lo — verify against specific FID */ cmd[0] = MA_CMD_SEARCH; - cmd[1] = (guint8)(self->fid >> 8); - cmd[2] = (guint8)(self->fid & 0xFF); + cmd[1] = 0x00; + cmd[2] = 0x00; + ma_submit_cmd (ssm, device, cmd, 3); break; } From fa02addbf8e5e08ba5980171988223efd18a700e Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:11:42 +0100 Subject: [PATCH 08/26] added notes --- src/microarray.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 61f3f07..fabcdfb 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -680,11 +680,14 @@ verify_run_state (FpiSsm *ssm, FpDevice *device) return; } - /* CMD 0x66 fid_hi fid_lo — verify against specific FID */ + /* * MODIFICATION: Instead of targeting a single self->fid passed by PAM, + * we instruct the hardware to run a global search across all stored templates + * by passing 0x00 0x00 as the slot target parameters. + */ cmd[0] = MA_CMD_SEARCH; cmd[1] = 0x00; cmd[2] = 0x00; - + ma_submit_cmd (ssm, device, cmd, 3); break; } From 508aa78c998f0704d3210567657fb1cf0d89418a Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:21:58 +0100 Subject: [PATCH 09/26] Reverted change --- src/microarray.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index fabcdfb..18588de 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -673,25 +673,29 @@ verify_run_state (FpiSsm *ssm, FpDevice *device) ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case VERIFY_SEARCH: { +case VERIFY_SEARCH: { if (self->resp_buf[MA_OVERHEAD] != 0x00) { fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - - /* * MODIFICATION: Instead of targeting a single self->fid passed by PAM, - * we instruct the hardware to run a global search across all stored templates - * by passing 0x00 0x00 as the slot target parameters. - */ + /* Get FID from enrolled print */ + FpPrint *print = NULL; + fpi_device_get_verify_data (device, &print); + GVariant *data = NULL; + g_object_get (print, "fpi-data", &data, NULL); + gint fid = 0; + g_variant_get (data, "(i)", &fid); + self->fid = fid; + + /* CMD 0x66 fid_hi fid_lo — verify against specific FID */ cmd[0] = MA_CMD_SEARCH; - cmd[1] = 0x00; - cmd[2] = 0x00; - + cmd[1] = (guint8)(self->fid >> 8); + cmd[2] = (guint8)(self->fid & 0xFF); ma_submit_cmd (ssm, device, cmd, 3); break; } - + case VERIFY_RECV_SEARCH: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; From 0e9c2571239fea717695f5d7471ecdce9a27596b Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:48:57 +0100 Subject: [PATCH 10/26] fixes issue where only first fingerprint enrolled could be used for sudo commands --- src/microarray.c | 102 ++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 18588de..19adcf9 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -627,7 +627,7 @@ ma_enroll (FpDevice *device) } /* -------------------------------------------------------------------------- - * Verify state machine + * Identify state machine * -------------------------------------------------------------------------- */ enum { @@ -641,7 +641,7 @@ enum { }; static void -verify_run_state (FpiSsm *ssm, FpDevice *device) +identify_run_state (FpiSsm *ssm, FpDevice *device) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); guint8 cmd[8]; @@ -665,38 +665,30 @@ verify_run_state (FpiSsm *ssm, FpDevice *device) } fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); cmd[0] = MA_CMD_GEN_CHAR; - cmd[1] = 0x01; /* use char buffer slot 1 for verification */ + cmd[1] = 0x01; /* extract image into temporary processing slot 1 */ ma_submit_cmd (ssm, device, cmd, 2); break; - case VERIFY_RECV_GEN_CHAR: + case IDENTIFY_RECV_GEN_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; -case VERIFY_SEARCH: { + case IDENTIFY_SEARCH: if (self->resp_buf[MA_OVERHEAD] != 0x00) { fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - /* Get FID from enrolled print */ - FpPrint *print = NULL; - fpi_device_get_verify_data (device, &print); - GVariant *data = NULL; - g_object_get (print, "fpi-data", &data, NULL); - gint fid = 0; - g_variant_get (data, "(i)", &fid); - self->fid = fid; - - /* CMD 0x66 fid_hi fid_lo — verify against specific FID */ + + /* Global database search command parameter payload */ cmd[0] = MA_CMD_SEARCH; - cmd[1] = (guint8)(self->fid >> 8); - cmd[2] = (guint8)(self->fid & 0xFF); + cmd[1] = 0x00; /* 0x00 0x00 instructs firmware to scan slots 0-9 */ + cmd[2] = 0x00; ma_submit_cmd (ssm, device, cmd, 3); break; - } - - case VERIFY_RECV_SEARCH: + + case IDENTIFY_RECV_SEARCH: + /* Read back response packet payload */ ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; @@ -706,29 +698,67 @@ case VERIFY_SEARCH: { } static void -verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE); if (error) { - /* Could be a retry error */ - fpi_device_verify_complete (device, error); + fpi_device_identify_complete (device, error); return; } - FpPrint *print = NULL; - fpi_device_get_verify_data (device, &print); - guint8 status = self->resp_buf[MA_OVERHEAD]; + if (status == 0x00) { - fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL); + /* Success! The chip matched a finger. + * The firmware response packet structure contains the matched slot ID: + * resp_buf[MA_OVERHEAD + 1] = High byte of matched slot + * resp_buf[MA_OVERHEAD + 2] = Low byte of matched slot + */ + gint matched_fid = ((gint)self->resp_buf[MA_OVERHEAD + 1] << 8) | + (gint)self->resp_buf[MA_OVERHEAD + 2]; + + fp_dbg ("Hardware match found on storage slot ID: %d", matched_fid); + + /* Search the operating system's internal DB array to find the FpPrint + * file matching this exact hardware storage slot ID. + */ + GPtrArray *prints = NULL; + FpPrint *match = NULL; + + fpi_device_get_identify_data (device, &prints); + + for (guint i = 0; i < prints->len; i++) { + FpPrint *print = g_ptr_array_index (prints, i); + GVariant *data = NULL; + g_object_get (print, "fpi-data", &data, NULL); + + if (data) { + gint fid = -1; + g_variant_get (data, "(i)", &fid); + g_variant_unref (data); + + if (fid == matched_fid) { + match = print; /* We found the matching OS template file wrapper! */ + break; + } + } + } + + if (match) { + fpi_device_identify_report (device, match, NULL, NULL); + } else { + fp_dbg ("Matched hardware slot %d, but no matching local profile file found", matched_fid); + fpi_device_identify_report (device, NULL, NULL, NULL); + } } else { - fp_dbg ("Search result: 0x%02x (no match)", status); - fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL); + fp_dbg ("Global search complete: No matching finger found (0x%02x)", status); + fpi_device_identify_report (device, NULL, NULL, NULL); } - fpi_device_verify_complete (device, NULL); + + fpi_device_identify_complete (device, NULL); } static void @@ -736,7 +766,7 @@ ma_verify (FpDevice *device) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); self->fid = -1; - FpiSsm *ssm = fpi_ssm_new (device, verify_run_state, VERIFY_NUM_STATES); + FpiSsm *ssm = fpi_ssm_new (device, identify_run_state, IDENTIFY_NUM_STATES); fpi_ssm_start (ssm, verify_ssm_done); } @@ -833,11 +863,11 @@ fpi_device_microarray_class_init (FpiDeviceMicroarrayClass *klass) dev_class->nr_enroll_stages = MA_ENROLL_SAMPLES; dev_class->scan_type = FP_SCAN_TYPE_PRESS; - dev_class->open = ma_dev_open; - dev_class->close = ma_dev_close; - dev_class->enroll = ma_enroll; - dev_class->verify = ma_verify; - dev_class->delete = ma_delete; + dev_class->open = ma_dev_open; + dev_class->close = ma_dev_close; + dev_class->enroll = ma_enroll; + dev_class->identify = ma_identify; + dev_class->delete = ma_delete; fpi_device_class_auto_initialize_features (dev_class); } From 306473fa77a22c300c1e7e8639810c698e316a12 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:51:11 +0100 Subject: [PATCH 11/26] fixed formatting issues --- src/microarray.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 19adcf9..8912aa7 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -631,13 +631,13 @@ ma_enroll (FpDevice *device) * -------------------------------------------------------------------------- */ enum { - VERIFY_GET_IMAGE, - VERIFY_RECV_IMAGE, - VERIFY_GEN_CHAR, - VERIFY_RECV_GEN_CHAR, - VERIFY_SEARCH, - VERIFY_RECV_SEARCH, - VERIFY_NUM_STATES, + IDENTIFY_GET_IMAGE, + IDENTIFY_RECV_IMAGE, + IDENTIFY_GEN_CHAR, + IDENTIFY_RECV_GEN_CHAR, + IDENTIFY_SEARCH, + IDENTIFY_RECV_SEARCH, + IDENTIFY_NUM_STATES, }; static void @@ -648,19 +648,19 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) switch (fpi_ssm_get_cur_state (ssm)) { - case VERIFY_GET_IMAGE: + case IDENTIFY_GET_IMAGE: cmd[0] = MA_CMD_GET_IMAGE; ma_submit_cmd (ssm, device, cmd, 1); break; - case VERIFY_RECV_IMAGE: + case IDENTIFY_RECV_IMAGE: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case VERIFY_GEN_CHAR: + case IDENTIFY_GEN_CHAR: if (self->resp_buf[MA_OVERHEAD] != 0x00) { fp_dbg ("GetImage not ready, retrying"); - fpi_ssm_jump_to_state (ssm, VERIFY_GET_IMAGE); + fpi_ssm_jump_to_state (ssm, IDENTIFY_GET_IMAGE); return; } fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); @@ -673,13 +673,12 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case IDENTIFY_SEARCH: + case IDENTIFY_SEARCH: if (self->resp_buf[MA_OVERHEAD] != 0x00) { - fpi_ssm_mark_failed (ssm, - fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - + /* Global database search command parameter payload */ cmd[0] = MA_CMD_SEARCH; cmd[1] = 0x00; /* 0x00 0x00 instructs firmware to scan slots 0-9 */ @@ -710,7 +709,7 @@ identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) } guint8 status = self->resp_buf[MA_OVERHEAD]; - + if (status == 0x00) { /* Success! The chip matched a finger. * The firmware response packet structure contains the matched slot ID: @@ -762,12 +761,10 @@ identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) } static void -ma_verify (FpDevice *device) +ma_identify (FpDevice *device) { - FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - self->fid = -1; FpiSsm *ssm = fpi_ssm_new (device, identify_run_state, IDENTIFY_NUM_STATES); - fpi_ssm_start (ssm, verify_ssm_done); + fpi_ssm_start (ssm, identify_ssm_done); } /* -------------------------------------------------------------------------- From 3df569377153a7a67dcc559e310e31e364de553a Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 17:57:55 +0100 Subject: [PATCH 12/26] another change to verify fingerprints --- src/microarray.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/microarray.c b/src/microarray.c index 8912aa7..743d7cf 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -760,6 +760,14 @@ identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) fpi_device_identify_complete (device, NULL); } +static void +ma_verify (FpDevice *device) +{ + /* Your original verify state machine launcher */ + FpiSsm *ssm = fpi_ssm_new (device, verify_run_state, VERIFY_NUM_STATES); + fpi_ssm_start (ssm, verify_ssm_done); +} + static void ma_identify (FpDevice *device) { @@ -863,6 +871,7 @@ fpi_device_microarray_class_init (FpiDeviceMicroarrayClass *klass) dev_class->open = ma_dev_open; dev_class->close = ma_dev_close; dev_class->enroll = ma_enroll; + dev_class->verify = ma_verify; dev_class->identify = ma_identify; dev_class->delete = ma_delete; From c87915b2174501f0c9c195acb7d235dd9d46cd82 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:06:16 +0100 Subject: [PATCH 13/26] actually fixed microarray.c file --- src/microarray.c | 394 ++++++++++++++++------------------------------- 1 file changed, 137 insertions(+), 257 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 743d7cf..e643fa2 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -7,9 +7,9 @@ * protocol (same packet framing as the R30X hobbyist module series). * * Endpoints: - * EP 0x03 OUT bulk — commands to device - * EP 0x83 IN bulk — responses from device - * EP 0x82 IN intr — finger-detect events (used for waiting) + * EP 0x03 OUT bulk — commands to device + * EP 0x83 IN bulk — responses from device + * EP 0x82 IN intr — finger-detect events (used for waiting) * * Copyright (C) 2024 * SPDX-License-Identifier: LGPL-2.1-or-later @@ -84,41 +84,21 @@ G_DEFINE_TYPE (FpiDeviceMicroarray, fpi_device_microarray, FP_TYPE_DEVICE) * Packet helpers * -------------------------------------------------------------------------- */ -/* - * Build a framed FPC command packet. - * Returns a newly allocated guint8 buffer (caller must g_free). - * Sets *out_len to the total packet length. - * - * Format: - * EF 01 FF FF FF FF [type=01] [len_hi] [len_lo] [cmd_bytes...] [csum_hi] [csum_lo] - * where len = cmd_len + 2 (payload + 2-byte checksum) - */ static guint8 * ma_build_cmd (const guint8 *cmd, gsize cmd_len, gsize *out_len) { gsize total = MA_OVERHEAD + cmd_len + 2; /* 2 checksum bytes */ guint8 *pkt = g_malloc (total); - /* sync + address */ - pkt[0] = 0xEF; - pkt[1] = 0x01; - pkt[2] = 0xFF; - pkt[3] = 0xFF; - pkt[4] = 0xFF; - pkt[5] = 0xFF; - - /* packet type */ + pkt[0] = 0xEF; pkt[1] = 0x01; pkt[2] = 0xFF; pkt[3] = 0xFF; pkt[4] = 0xFF; pkt[5] = 0xFF; pkt[6] = MA_PKT_CMD; - /* length = cmd_len + 2 (big-endian) */ guint16 len = (guint16)(cmd_len + 2); pkt[7] = (guint8)(len >> 8); pkt[8] = (guint8)(len & 0xFF); - /* command payload */ memcpy (pkt + 9, cmd, cmd_len); - /* 16-bit checksum: sum of bytes [6 .. 9+cmd_len-1] */ guint16 csum = 0; for (gsize i = 6; i < 9 + cmd_len; i++) csum += pkt[i]; @@ -129,50 +109,39 @@ ma_build_cmd (const guint8 *cmd, gsize cmd_len, gsize *out_len) return pkt; } -/* - * Parse an FPC response packet. - * Returns TRUE if header/checksum valid; resp_data and resp_data_len - * point into buf (not copied). - */ static gboolean ma_parse_resp (const guint8 *buf, gsize buf_len, const guint8 **data_out, gsize *data_len_out, GError **error) { if (buf_len < (gsize)(MA_OVERHEAD + 2)) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Response too short"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Response too short"); return FALSE; } if (buf[0] != 0xEF || buf[1] != 0x01) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Bad sync header"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Bad sync header"); return FALSE; } if (buf[6] != MA_PKT_ACK) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Expected ACK (0x07), got 0x%02x", buf[6]); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Expected ACK (0x07), got 0x%02x", buf[6]); return FALSE; } guint16 len = ((guint16)buf[7] << 8) | buf[8]; gsize expected = (gsize)MA_OVERHEAD + len; if (buf_len < expected) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Response truncated"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Response truncated"); return FALSE; } - /* verify checksum: sum of bytes [6 .. expected-3] */ guint16 csum = 0; for (gsize i = 6; i < expected - 2; i++) csum += buf[i]; guint16 got = ((guint16)buf[expected-2] << 8) | buf[expected-1]; if (csum != got) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Checksum mismatch: want 0x%04x got 0x%04x", csum, got); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Checksum mismatch"); return FALSE; } *data_out = buf + MA_OVERHEAD; - *data_len_out = (gsize)(len - 2); /* strip 2 checksum bytes */ + *data_len_out = (gsize)(len - 2); return TRUE; } @@ -187,29 +156,23 @@ enum { }; static void -init_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) +init_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { FpiSsm *ssm = user_data; if (error) { fpi_ssm_mark_failed (ssm, error); return; } - /* Basic handshake response check: just verify header bytes */ - if (transfer->actual_length >= 2 && - transfer->buffer[0] == 0xEF && transfer->buffer[1] == 0x01) { + if (transfer->actual_length >= 2 && transfer->buffer[0] == 0xEF && transfer->buffer[1] == 0x01) { fp_dbg ("Handshake OK"); fpi_ssm_mark_completed (ssm); } else { - fpi_ssm_mark_failed (ssm, - fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Handshake response invalid")); + fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, "Handshake response invalid")); } } static void -init_send_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) +init_send_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { FpiSsm *ssm = user_data; if (error) { @@ -222,26 +185,21 @@ init_send_cb (FpiUsbTransfer *transfer, FpDevice *device, static void init_run_state (FpiSsm *ssm, FpDevice *device) { - FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); FpiUsbTransfer *transfer; - switch (fpi_ssm_get_cur_state (ssm)) { case INIT_SEND_HANDSHAKE: { guint8 *buf = g_memdup2 (MA_HANDSHAKE_PKT, MA_HANDSHAKE_PKT_LEN); transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; - fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_OUT, - buf, MA_HANDSHAKE_PKT_LEN, g_free); - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, NULL, - init_send_cb, ssm); + fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_OUT, buf, MA_HANDSHAKE_PKT_LEN, g_free); + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), init_send_cb, ssm); break; } case INIT_RECV_HANDSHAKE: transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; fpi_usb_transfer_fill_bulk (transfer, MA_EP_IN, MA_HANDSHAKE_RESP_LEN); - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, NULL, - init_recv_cb, ssm); + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), init_recv_cb, ssm); break; default: g_assert_not_reached (); @@ -258,13 +216,10 @@ static void ma_dev_open (FpDevice *device) { GError *error = NULL; - - if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), - 0, 0, &error)) { + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) { fpi_device_open_complete (device, error); return; } - FpiSsm *ssm = fpi_ssm_new (device, init_run_state, INIT_NUM_STATES); fpi_ssm_start (ssm, init_ssm_done); } @@ -273,8 +228,7 @@ static void ma_dev_close (FpDevice *device) { GError *error = NULL; - g_usb_device_release_interface (fpi_device_get_usb_device (device), - 0, 0, &error); + g_usb_device_release_interface (fpi_device_get_usb_device (device), 0, 0, &error); fpi_device_close_complete (device, error); } @@ -282,15 +236,8 @@ ma_dev_close (FpDevice *device) * Generic command send/receive helpers * -------------------------------------------------------------------------- */ -/* - * Sends a framed command and reads back the response into self->resp_buf. - * On completion, calls fpi_ssm_next_state (ssm). - * The caller is responsible for checking self->resp_buf[0] afterward. - */ - static void -cmd_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) +cmd_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { FpiSsm *ssm = user_data; if (error) { @@ -301,8 +248,7 @@ cmd_recv_cb (FpiUsbTransfer *transfer, FpDevice *device, } static void -cmd_send_cb (FpiUsbTransfer *transfer, FpDevice *device, - gpointer user_data, GError *error) +cmd_send_cb (FpiUsbTransfer *transfer, FpDevice *device, gpointer user_data, GError *error) { FpiSsm *ssm = user_data; if (error) { @@ -312,64 +258,37 @@ cmd_send_cb (FpiUsbTransfer *transfer, FpDevice *device, fpi_ssm_next_state (ssm); } -/* Submit a bulk-OUT command with cancellation support */ static void -ma_submit_cmd (FpiSsm *ssm, FpDevice *device, - const guint8 *cmd, gsize cmd_len) +ma_submit_cmd (FpiSsm *ssm, FpDevice *device, const guint8 *cmd, gsize cmd_len) { gsize pkt_len; guint8 *pkt = ma_build_cmd (cmd, cmd_len, &pkt_len); FpiUsbTransfer *transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; - fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_OUT, - pkt, pkt_len, g_free); - - /* Swap NULL out for fpi_device_get_cancellable */ - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, - fpi_device_get_cancellable (device), - cmd_send_cb, ssm); + fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_OUT, pkt, pkt_len, g_free); + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), cmd_send_cb, ssm); } -/* Submit a bulk-IN read with cancellation support */ static void ma_submit_recv (FpiSsm *ssm, FpDevice *device, gsize expect_len) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); FpiUsbTransfer *transfer = fpi_usb_transfer_new (device); transfer->ssm = ssm; - fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_IN, - self->resp_buf, expect_len, NULL); - - /* Swap NULL out for fpi_device_get_cancellable */ - fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, - fpi_device_get_cancellable (device), - cmd_recv_cb, ssm); + fpi_usb_transfer_fill_bulk_full (transfer, MA_EP_IN, self->resp_buf, expect_len, NULL); + fpi_usb_transfer_submit (transfer, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), cmd_recv_cb, ssm); } - /* -------------------------------------------------------------------------- * Enroll state machine - * -------------------------------------------------------------------------- - * - * Pre-enrollment: - * ENROLL_HANDSHAKE / RECV — reset device session - * ENROLL_READ_INDEX_PRE / RECV — CMD 0x1F: read FID bitmap - * ENROLL_EMPTY / RECV — CMD 0x0D: only if all 30 slots full - * - * Loop MA_ENROLL_SAMPLES times: - * ENROLL_GET_IMAGE / RECV — CMD 0x01: poll until finger present - * ENROLL_GEN_CHAR / RECV — CMD 0x02: extract features into char buffer - * - * Complete: - * ENROLL_REG_MODEL / RECV — CMD 0x05: merge char buffers into template - * ENROLL_STORE_CHAR / RECV — CMD 0x06: store to self->fid (set in pre-check) - */ + * -------------------------------------------------------------------------- */ + enum { - ENROLL_HANDSHAKE, /* session reset — must call before every enrollment */ + ENROLL_HANDSHAKE, ENROLL_RECV_HANDSHAKE, - ENROLL_READ_INDEX_PRE, /* CMD 0x1F — find free FID slot before starting */ + ENROLL_READ_INDEX_PRE, ENROLL_RECV_READ_INDEX_PRE, - ENROLL_EMPTY, /* CMD 0x0D — only if no free slot found */ + ENROLL_EMPTY, ENROLL_RECV_EMPTY, ENROLL_GET_IMAGE, ENROLL_RECV_IMAGE, @@ -396,51 +315,36 @@ enroll_run_state (FpiSsm *ssm, FpDevice *device) guint8 cmd[8]; switch (fpi_ssm_get_cur_state (ssm)) { - case ENROLL_HANDSHAKE: { guint8 *buf = g_memdup2 (MA_HANDSHAKE_PKT, MA_HANDSHAKE_PKT_LEN); FpiUsbTransfer *t = fpi_usb_transfer_new (device); t->ssm = ssm; - fpi_usb_transfer_fill_bulk_full (t, MA_EP_OUT, - buf, MA_HANDSHAKE_PKT_LEN, g_free); - fpi_usb_transfer_submit (t, MA_TIMEOUT_CMD, NULL, cmd_send_cb, ssm); + fpi_usb_transfer_fill_bulk_full (t, MA_EP_OUT, buf, MA_HANDSHAKE_PKT_LEN, g_free); + fpi_usb_transfer_submit (t, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), cmd_send_cb, ssm); break; } - case ENROLL_RECV_HANDSHAKE: { FpiUsbTransfer *t = fpi_usb_transfer_new (device); t->ssm = ssm; fpi_usb_transfer_fill_bulk (t, MA_EP_IN, MA_HANDSHAKE_RESP_LEN); - fpi_usb_transfer_submit (t, MA_TIMEOUT_CMD, NULL, cmd_recv_cb, ssm); + fpi_usb_transfer_submit (t, MA_TIMEOUT_CMD, fpi_device_get_cancellable (device), cmd_recv_cb, ssm); break; } - case ENROLL_READ_INDEX_PRE: - cmd[0] = MA_CMD_READ_INDEX; - cmd[1] = 0x00; + cmd[0] = MA_CMD_READ_INDEX; cmd[1] = 0x00; ma_submit_cmd (ssm, device, cmd, 2); break; - case ENROLL_RECV_READ_INDEX_PRE: ma_submit_recv (ssm, device, MA_OVERHEAD + 35 + 2); break; - case ENROLL_EMPTY: { - /* Check bitmap from pre-enrollment ReadIndex to find a free slot */ const guint8 *resp = self->resp_buf + MA_OVERHEAD; self->fid = -1; - if (resp[0] == 0x00) { for (int byte = 0; byte < 4 && self->fid < 0; byte++) { for (int bit = 0; bit < 8; bit++) { int candidate_slot = byte * 8 + bit; - - /* STRICT CAP: Your chip only supports up to slot 9 (10 fingers total) */ - if (candidate_slot > 9) { - break; - } - - /* If this bit is 0, the slot is empty! Let's claim it. */ + if (candidate_slot > 9) break; if (!(resp[1 + byte] & (1 << bit))) { self->fid = candidate_slot; break; @@ -448,95 +352,53 @@ enroll_run_state (FpiSsm *ssm, FpDevice *device) } } } - - /* If we found a valid free slot (0-9), skip erasing and proceed to scan */ if (self->fid >= 0 && self->fid <= 9) { fp_dbg ("Found free FID slot %d. Proceeding to image capture.", self->fid); fpi_ssm_jump_to_state (ssm, ENROLL_GET_IMAGE); return; } - - /* FALLBACK: If slots 0-9 are completely full, nuke the chip to start fresh */ - fp_dbg ("Storage slots 0-9 are completely full! Clearing all templates."); - self->fid = 0; - cmd[0] = MA_CMD_EMPTY; + fp_dbg ("Storage slots 0-9 full! Clearing all templates."); + self->fid = 0; cmd[0] = MA_CMD_EMPTY; ma_submit_cmd (ssm, device, cmd, 1); break; } - case ENROLL_RECV_EMPTY: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case ENROLL_GET_IMAGE: cmd[0] = MA_CMD_GET_IMAGE; ma_submit_cmd (ssm, device, cmd, 1); break; - case ENROLL_RECV_IMAGE: - /* Response: 3 bytes payload, resp[0]=0 means image captured */ ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case ENROLL_GEN_CHAR: if (self->resp_buf[MA_OVERHEAD] != 0x00) { - /* No image (finger absent or sensor not ready) */ if (self->waiting_for_lift) { - /* Finger was just lifted — ready for next press */ self->waiting_for_lift = FALSE; fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE); fp_dbg ("Finger lifted, waiting for next press"); } - fp_dbg ("GetImage not ready (0x%02x), retrying", - self->resp_buf[MA_OVERHEAD]); g_timeout_add (100, poll_get_image_cb, ssm); return; } if (self->waiting_for_lift) { - /* Finger still down from previous capture — keep waiting */ - fp_dbg ("Waiting for finger lift..."); g_timeout_add (100, poll_get_image_cb, ssm); return; } - /* New finger press with valid image — proceed */ fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); - cmd[0] = MA_CMD_GEN_CHAR; - cmd[1] = (guint8)(self->enroll_stage + 1); + cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = (guint8)(self->enroll_stage + 1); ma_submit_cmd (ssm, device, cmd, 2); break; - - case ENROLL_RECV_GEN_CHAR: { + case ENROLL_RECV_GEN_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); - /* Note: actual check happens in the callback; we use a simple approach - * of checking after state transitions */ - /* Advance stage in next state entry */ break; - } - - /* We need to handle the stage increment after recv; do it via a dummy state - * by just checking here at the start of the NEXT state. But since we can't - * do that cleanly without extra states, we check at GEN_CHAR entry above. - * - * For stage counting: we increment after a successful GEN_CHAR recv. - * Since recv state just does the USB and moves to next state, we increment - * in REG_MODEL entry until stages are done. - * - * Simpler: jump back to GET_IMAGE if stage < SAMPLES, else fall through. - * We do the increment and check at ENROLL_REG_MODEL entry. */ - case ENROLL_REG_MODEL: - /* Check GenChar result and count sample */ - fp_dbg ("GenChar resp[0]=0x%02x stage=%d", - self->resp_buf[MA_OVERHEAD], self->enroll_stage); if (self->resp_buf[MA_OVERHEAD] == 0x00) { self->enroll_stage++; - fp_dbg ("stage %d / %d OK", self->enroll_stage, MA_ENROLL_SAMPLES); fpi_device_enroll_progress (device, self->enroll_stage, NULL, NULL); } else { - g_warning ("microarray: GenChar failed (0x%02x), retrying stage", - self->resp_buf[MA_OVERHEAD]); - fpi_device_enroll_progress (device, self->enroll_stage, NULL, - fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + fpi_device_enroll_progress (device, self->enroll_stage, NULL, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); self->waiting_for_lift = TRUE; fpi_ssm_jump_to_state (ssm, ENROLL_GET_IMAGE); return; @@ -546,40 +408,24 @@ enroll_run_state (FpiSsm *ssm, FpDevice *device) fpi_ssm_jump_to_state (ssm, ENROLL_GET_IMAGE); return; } - fp_dbg ("all samples collected, sending RegModel (CMD 0x05)"); cmd[0] = MA_CMD_REG_MODEL; ma_submit_cmd (ssm, device, cmd, 1); break; - case ENROLL_RECV_REG_MODEL: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case ENROLL_STORE_CHAR: - /* Check RegModel result */ - fp_dbg ("RegModel resp[0]=0x%02x, storing to FID slot %d", - self->resp_buf[MA_OVERHEAD], self->fid); if (self->resp_buf[MA_OVERHEAD] != 0x00) { - g_warning ("microarray: RegModel FAILED with 0x%02x", - self->resp_buf[MA_OVERHEAD]); - fpi_ssm_mark_failed (ssm, - fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "RegModel failed: 0x%02x", - self->resp_buf[MA_OVERHEAD])); + fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "RegModel failed")); return; } - /* self->fid was set during pre-enrollment ReadIndex or after Empty */ - cmd[0] = MA_CMD_STORE_CHAR; - cmd[1] = 0x01; - cmd[2] = (guint8)(self->fid >> 8); - cmd[3] = (guint8)(self->fid & 0xFF); + cmd[0] = MA_CMD_STORE_CHAR; cmd[1] = 0x01; + cmd[2] = (guint8)(self->fid >> 8); cmd[3] = (guint8)(self->fid & 0xFF); ma_submit_cmd (ssm, device, cmd, 4); break; - case ENROLL_RECV_STORE_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - default: g_assert_not_reached (); } @@ -589,20 +435,14 @@ static void enroll_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - if (error) { fpi_device_enroll_complete (device, NULL, error); return; } if (self->resp_buf[MA_OVERHEAD] != 0x00) { - fpi_device_enroll_complete (device, NULL, - fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "StoreChar failed: 0x%02x", - self->resp_buf[MA_OVERHEAD])); + fpi_device_enroll_complete (device, NULL, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "StoreChar failed")); return; } - - /* Build an FpPrint encoding the FID */ FpPrint *print = NULL; fpi_device_get_enroll_data (device, &print); fpi_print_set_type (print, FPI_PRINT_RAW); @@ -619,15 +459,96 @@ static void ma_enroll (FpDevice *device) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - self->enroll_stage = 0; - self->fid = -1; - self->waiting_for_lift = FALSE; + self->enroll_stage = 0; self->fid = -1; self->waiting_for_lift = FALSE; FpiSsm *ssm = fpi_ssm_new (device, enroll_run_state, ENROLL_NUM_STATES); fpi_ssm_start (ssm, enroll_ssm_done); } /* -------------------------------------------------------------------------- - * Identify state machine + * Verify state machine (Explicit single-finger validation) + * -------------------------------------------------------------------------- */ + +enum { + VERIFY_GET_IMAGE, + VERIFY_RECV_IMAGE, + VERIFY_GEN_CHAR, + VERIFY_RECV_GEN_CHAR, + VERIFY_SEARCH, + VERIFY_RECV_SEARCH, + VERIFY_NUM_STATES, +}; + +static void +verify_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); + guint8 cmd[8]; + + switch (fpi_ssm_get_cur_state (ssm)) { + case VERIFY_GET_IMAGE: + cmd[0] = MA_CMD_GET_IMAGE; + ma_submit_cmd (ssm, device, cmd, 1); + break; + case VERIFY_RECV_IMAGE: + ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); + break; + case VERIFY_GEN_CHAR: + if (self->resp_buf[MA_OVERHEAD] != 0x00) { + fpi_ssm_jump_to_state (ssm, VERIFY_GET_IMAGE); + return; + } + fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); + cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = 0x01; + ma_submit_cmd (ssm, device, cmd, 2); + break; + case VERIFY_RECV_GEN_CHAR: + ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); + break; + case VERIFY_SEARCH: { + if (self->resp_buf[MA_OVERHEAD] != 0x00) { + fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + return; + } + FpPrint *print = NULL; + fpi_device_get_verify_data (device, &print); + GVariant *data = NULL; + g_object_get (print, "fpi-data", &data, NULL); + gint fid = 0; + g_variant_get (data, "(i)", &fid); + g_variant_unref (data); + self->fid = fid; + + cmd[0] = MA_CMD_SEARCH; + cmd[1] = (guint8)(self->fid >> 8); cmd[2] = (guint8)(self->fid & 0xFF); + ma_submit_cmd (ssm, device, cmd, 3); + break; + } + case VERIFY_RECV_SEARCH: + ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); + break; + default: + g_assert_not_reached (); + } +} + +static void +verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); + fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE); + + if (error) { + fpi_device_verify_complete (device, error); + return; + } + + gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); + fpi_device_verify_report (device, matched ? FP_VERIFY_MATCH : FP_VERIFY_RETRY, NULL, NULL, NULL); + fpi_device_verify_complete (device, NULL); +} + +/* -------------------------------------------------------------------------- + * Identify state machine (Device-wide global search for sudo) * -------------------------------------------------------------------------- */ enum { @@ -647,16 +568,13 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) guint8 cmd[8]; switch (fpi_ssm_get_cur_state (ssm)) { - case IDENTIFY_GET_IMAGE: cmd[0] = MA_CMD_GET_IMAGE; ma_submit_cmd (ssm, device, cmd, 1); break; - case IDENTIFY_RECV_IMAGE: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case IDENTIFY_GEN_CHAR: if (self->resp_buf[MA_OVERHEAD] != 0x00) { fp_dbg ("GetImage not ready, retrying"); @@ -664,33 +582,23 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) return; } fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); - cmd[0] = MA_CMD_GEN_CHAR; - cmd[1] = 0x01; /* extract image into temporary processing slot 1 */ + cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = 0x01; ma_submit_cmd (ssm, device, cmd, 2); break; - case IDENTIFY_RECV_GEN_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case IDENTIFY_SEARCH: if (self->resp_buf[MA_OVERHEAD] != 0x00) { fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - - /* Global database search command parameter payload */ - cmd[0] = MA_CMD_SEARCH; - cmd[1] = 0x00; /* 0x00 0x00 instructs firmware to scan slots 0-9 */ - cmd[2] = 0x00; + cmd[0] = MA_CMD_SEARCH; cmd[1] = 0x00; cmd[2] = 0x00; ma_submit_cmd (ssm, device, cmd, 3); break; - case IDENTIFY_RECV_SEARCH: - /* Read back response packet payload */ ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - default: g_assert_not_reached (); } @@ -700,7 +608,6 @@ static void identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - fpi_device_report_finger_status (device, FP_FINGER_STATUS_NONE); if (error) { @@ -709,61 +616,42 @@ identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) } guint8 status = self->resp_buf[MA_OVERHEAD]; - if (status == 0x00) { - /* Success! The chip matched a finger. - * The firmware response packet structure contains the matched slot ID: - * resp_buf[MA_OVERHEAD + 1] = High byte of matched slot - * resp_buf[MA_OVERHEAD + 2] = Low byte of matched slot - */ - gint matched_fid = ((gint)self->resp_buf[MA_OVERHEAD + 1] << 8) | - (gint)self->resp_buf[MA_OVERHEAD + 2]; - + gint matched_fid = ((gint)self->resp_buf[MA_OVERHEAD + 1] << 8) | (gint)self->resp_buf[MA_OVERHEAD + 2]; fp_dbg ("Hardware match found on storage slot ID: %d", matched_fid); - /* Search the operating system's internal DB array to find the FpPrint - * file matching this exact hardware storage slot ID. - */ GPtrArray *prints = NULL; FpPrint *match = NULL; - fpi_device_get_identify_data (device, &prints); for (guint i = 0; i < prints->len; i++) { FpPrint *print = g_ptr_array_index (prints, i); GVariant *data = NULL; g_object_get (print, "fpi-data", &data, NULL); - if (data) { gint fid = -1; g_variant_get (data, "(i)", &fid); g_variant_unref (data); - if (fid == matched_fid) { - match = print; /* We found the matching OS template file wrapper! */ + match = print; break; } } } - if (match) { fpi_device_identify_report (device, match, NULL, NULL); } else { - fp_dbg ("Matched hardware slot %d, but no matching local profile file found", matched_fid); fpi_device_identify_report (device, NULL, NULL, NULL); } } else { - fp_dbg ("Global search complete: No matching finger found (0x%02x)", status); fpi_device_identify_report (device, NULL, NULL, NULL); } - fpi_device_identify_complete (device, NULL); } static void ma_verify (FpDevice *device) { - /* Your original verify state machine launcher */ FpiSsm *ssm = fpi_ssm_new (device, verify_run_state, VERIFY_NUM_STATES); fpi_ssm_start (ssm, verify_ssm_done); } @@ -780,7 +668,7 @@ ma_identify (FpDevice *device) * -------------------------------------------------------------------------- */ enum { - DELETE_EMPTY, /* CMD 0x0D — running onboard sensor flash format */ + DELETE_EMPTY, DELETE_RECV_EMPTY, DELETE_NUM_STATES, }; @@ -789,18 +677,14 @@ static void delete_run_state (FpiSsm *ssm, FpDevice *device) { guint8 cmd[1]; - switch (fpi_ssm_get_cur_state (ssm)) { - case DELETE_EMPTY: cmd[0] = MA_CMD_EMPTY; ma_submit_cmd (ssm, device, cmd, 1); break; - case DELETE_RECV_EMPTY: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - default: g_assert_not_reached (); } @@ -810,13 +694,9 @@ static void delete_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - if (!error && self->resp_buf[MA_OVERHEAD] != 0x00) { - error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, - "Delete Empty command failed: 0x%02x", - self->resp_buf[MA_OVERHEAD]); + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Delete Empty command failed"); } - fpi_device_delete_complete (device, error); } @@ -876,4 +756,4 @@ fpi_device_microarray_class_init (FpiDeviceMicroarrayClass *klass) dev_class->delete = ma_delete; fpi_device_class_auto_initialize_features (dev_class); -} +} \ No newline at end of file From 3203812914c5863c7968155361d7eac143ff34d9 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:29:25 +0100 Subject: [PATCH 14/26] error fix in microarray.c --- src/microarray.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/microarray.c b/src/microarray.c index e643fa2..5a76c08 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -542,8 +542,12 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) return; } + /* 0x00 from the chip indicates a successful hardware match */ gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); - fpi_device_verify_report (device, matched ? FP_VERIFY_MATCH : FP_VERIFY_RETRY, NULL, NULL, NULL); + + /* libfprint expects just the device and a boolean (TRUE for match, FALSE for retry) */ + fpi_device_verify_report (device, matched); + fpi_device_verify_complete (device, NULL); } From 1806a4539b01037cea0dde0d36feb83739fd06b6 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:30:58 +0100 Subject: [PATCH 15/26] another fix implemented --- src/microarray.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 5a76c08..e9d5993 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -542,11 +542,14 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) return; } - /* 0x00 from the chip indicates a successful hardware match */ + /* Check if the chip gave us a 0x00 status indicating a hardware match */ gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); - /* libfprint expects just the device and a boolean (TRUE for match, FALSE for retry) */ - fpi_device_verify_report (device, matched); + /* Use libfprint's matching enums instead of booleans */ + FpMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; + + /* Pass the device, the enum result, and NULL for the extra driver data */ + fpi_device_verify_report (device, result, NULL); fpi_device_verify_complete (device, NULL); } From 22278752eab14b1fe6cd57bcd49d705dbd2629d1 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:33:02 +0100 Subject: [PATCH 16/26] yet another change. Hopefully that's it! --- src/microarray.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index e9d5993..9453a13 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -548,8 +548,9 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) /* Use libfprint's matching enums instead of booleans */ FpMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; - /* Pass the device, the enum result, and NULL for the extra driver data */ - fpi_device_verify_report (device, result, NULL); +/* Pass all 4 arguments expected by this libfprint version: + * (device, result, matched_print, gerror) */ + fpi_device_verify_report (device, result, NULL, NULL); fpi_device_verify_complete (device, NULL); } From 3c1f0735aa00503cf69cfcf96c07048d3ea5d70e Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:40:04 +0100 Subject: [PATCH 17/26] just guessing at this point --- src/microarray.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 9453a13..01a56b5 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -548,10 +548,10 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) /* Use libfprint's matching enums instead of booleans */ FpMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; -/* Pass all 4 arguments expected by this libfprint version: + /* Pass all 4 arguments expected by this libfprint version: * (device, result, matched_print, gerror) */ - fpi_device_verify_report (device, result, NULL, NULL); - + fpi_device_verify_report (device, result, NULL); + fpi_device_verify_complete (device, NULL); } From 7264b8f084bc931a4d2e1bd29be0324749ad21e0 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:43:04 +0100 Subject: [PATCH 18/26] another test --- src/microarray.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 01a56b5..d1b3f42 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -545,12 +545,11 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) /* Check if the chip gave us a 0x00 status indicating a hardware match */ gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); - /* Use libfprint's matching enums instead of booleans */ - FpMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; + /* 1. Fix the type name to use 'Fpi' instead of 'Fp' */ + FpiMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; - /* Pass all 4 arguments expected by this libfprint version: - * (device, result, matched_print, gerror) */ - fpi_device_verify_report (device, result, NULL); + /* 2. Fix the argument count: Provide exactly 4 arguments to stop the 'too few' error */ + fpi_device_verify_report (device, result, NULL, NULL); fpi_device_verify_complete (device, NULL); } From a04f52e75556c46815788d2dd23d63a0fd865577 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:49:25 +0100 Subject: [PATCH 19/26] fix that allows all fingerprints to work --- src/microarray.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index d1b3f42..83800f9 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -542,15 +542,18 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) return; } - /* Check if the chip gave us a 0x00 status indicating a hardware match */ gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); - - /* 1. Fix the type name to use 'Fpi' instead of 'Fp' */ FpiMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; - /* 2. Fix the argument count: Provide exactly 4 arguments to stop the 'too few' error */ - fpi_device_verify_report (device, result, NULL, NULL); + /* Retrieve the actual print object being checked in this session */ + FpPrint *print = NULL; + if (matched) { + print = fpi_device_get_verify_print (device); + } + /* Pass the actual print object instead of NULL so PAM/sudo know who matched */ + fpi_device_verify_report (device, result, print, NULL); + fpi_device_verify_complete (device, NULL); } From bfa5f538028c2d049c5f59493e09022acfd863de Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 18:53:24 +0100 Subject: [PATCH 20/26] another fix --- src/microarray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/microarray.c b/src/microarray.c index 83800f9..1b9d632 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -548,7 +548,7 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) /* Retrieve the actual print object being checked in this session */ FpPrint *print = NULL; if (matched) { - print = fpi_device_get_verify_print (device); + print = (FpPrint *) fpi_device_get_current_action_data (device); } /* Pass the actual print object instead of NULL so PAM/sudo know who matched */ From abc030caeddd48f92a2068aa990699a8a11d08f9 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 19:00:44 +0100 Subject: [PATCH 21/26] new fix --- src/microarray.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 1b9d632..c322482 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -109,7 +109,8 @@ ma_build_cmd (const guint8 *cmd, gsize cmd_len, gsize *out_len) return pkt; } -static gboolean +/* Suppressed unused warning so the compiler doesn't fail on -Werror */ +static G_GNUC_UNUSED gboolean ma_parse_resp (const guint8 *buf, gsize buf_len, const guint8 **data_out, gsize *data_len_out, GError **error) @@ -545,10 +546,13 @@ verify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) gboolean matched = (self->resp_buf[MA_OVERHEAD] == 0x00); FpiMatchResult result = matched ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL; - /* Retrieve the actual print object being checked in this session */ FpPrint *print = NULL; if (matched) { - print = (FpPrint *) fpi_device_get_current_action_data (device); + /* USE THE WORKING NATIVE GETTER! + * Since we know fpi_device_get_verify_data works flawlessly in your + * VERIFY_SEARCH block, we can just call it here to retrieve the template! + */ + fpi_device_get_verify_data (device, &print); } /* Pass the actual print object instead of NULL so PAM/sudo know who matched */ From c7289bf08d022679140542d1e2a5818447c0c56c Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 19:08:44 +0100 Subject: [PATCH 22/26] Another fix implemented --- src/microarray.c | 77 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index c322482..8539899 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -74,6 +74,7 @@ struct _FpiDeviceMicroarray guint8 *resp_buf; /* allocated response buffer */ GCancellable *interrupt_cancellable; gboolean waiting_for_lift; /* TRUE after each successful capture */ + guint identify_index; }; G_DECLARE_FINAL_TYPE (FpiDeviceMicroarray, fpi_device_microarray, @@ -572,6 +573,7 @@ enum { IDENTIFY_RECV_GEN_CHAR, IDENTIFY_SEARCH, IDENTIFY_RECV_SEARCH, + IDENTIFY_CHECK_MATCH, IDENTIFY_NUM_STATES, }; @@ -602,17 +604,60 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) case IDENTIFY_RECV_GEN_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; - case IDENTIFY_SEARCH: + case IDENTIFY_SEARCH: { if (self->resp_buf[MA_OVERHEAD] != 0x00) { fpi_ssm_mark_failed (ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); return; } - cmd[0] = MA_CMD_SEARCH; cmd[1] = 0x00; cmd[2] = 0x00; + + GPtrArray *prints = NULL; + fpi_device_get_identify_data (device, &prints); + if (!prints || self->identify_index >= prints->len) { + fpi_ssm_mark_failed (ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "No templates to check")); + return; + } + + /* Pull the fingerprint template profile at our current iteration index */ + FpPrint *print = g_ptr_array_index (prints, self->identify_index); + GVariant *data = NULL; + g_object_get (print, "fpi-data", &data, NULL); + gint fid = 0; + if (data) { + g_variant_get (data, "(i)", &fid); + g_variant_unref (data); + } + self->fid = fid; + fp_dbg ("Searching hardware memory slot ID: %d (Index %d/%d)", self->fid, self->identify_index + 1, prints->len); + + cmd[0] = MA_CMD_SEARCH; + cmd[1] = (guint8)(self->fid >> 8); + cmd[2] = (guint8)(self->fid & 0xFF); ma_submit_cmd (ssm, device, cmd, 3); break; + } case IDENTIFY_RECV_SEARCH: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; + case IDENTIFY_CHECK_MATCH: { + guint8 status = self->resp_buf[MA_OVERHEAD]; + if (status == 0x00) { + fp_dbg ("Hardware match verified on slot ID %d!", self->fid); + fpi_ssm_mark_completed (ssm); + } else { + self->identify_index++; + GPtrArray *prints = NULL; + fpi_device_get_identify_data (device, &prints); + + /* If there are more registered prints left, loop back to search without re-imaging */ + if (prints && self->identify_index < prints->len) { + fpi_ssm_jump_to_state (ssm, IDENTIFY_SEARCH); + } else { + fp_dbg ("Scan completed: No matching enrolled prints found."); + fpi_ssm_mark_completed (ssm); + } + } + break; + } default: g_assert_not_reached (); } @@ -629,30 +674,12 @@ identify_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) return; } - guint8 status = self->resp_buf[MA_OVERHEAD]; - if (status == 0x00) { - gint matched_fid = ((gint)self->resp_buf[MA_OVERHEAD + 1] << 8) | (gint)self->resp_buf[MA_OVERHEAD + 2]; - fp_dbg ("Hardware match found on storage slot ID: %d", matched_fid); - + if (self->resp_buf[MA_OVERHEAD] == 0x00) { GPtrArray *prints = NULL; - FpPrint *match = NULL; fpi_device_get_identify_data (device, &prints); - - for (guint i = 0; i < prints->len; i++) { - FpPrint *print = g_ptr_array_index (prints, i); - GVariant *data = NULL; - g_object_get (print, "fpi-data", &data, NULL); - if (data) { - gint fid = -1; - g_variant_get (data, "(i)", &fid); - g_variant_unref (data); - if (fid == matched_fid) { - match = print; - break; - } - } - } - if (match) { + + if (prints && self->identify_index < prints->len) { + FpPrint *match = g_ptr_array_index (prints, self->identify_index); fpi_device_identify_report (device, match, NULL, NULL); } else { fpi_device_identify_report (device, NULL, NULL, NULL); @@ -673,6 +700,8 @@ ma_verify (FpDevice *device) static void ma_identify (FpDevice *device) { + FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); + self->identify_index = 0; /* Clear previous iteration trackers */ FpiSsm *ssm = fpi_ssm_new (device, identify_run_state, IDENTIFY_NUM_STATES); fpi_ssm_start (ssm, identify_ssm_done); } From a82068786ad6dc5d5cf5d3a4b6ae67df8136b16e Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 19:16:47 +0100 Subject: [PATCH 23/26] final fix? --- src/microarray.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 8539899..88a0e4c 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -592,12 +592,17 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; case IDENTIFY_GEN_CHAR: - if (self->resp_buf[MA_OVERHEAD] != 0x00) { - fp_dbg ("GetImage not ready, retrying"); - fpi_ssm_jump_to_state (ssm, IDENTIFY_GET_IMAGE); - return; + /* FIX 1: Only check for image readiness if we actually just came from RECV_IMAGE. + * If we looped back here from CHECK_MATCH, resp_buf contains the SEARCH status instead! */ + if (fpi_ssm_get_prev_state (ssm) == IDENTIFY_RECV_IMAGE) { + if (self->resp_buf[MA_OVERHEAD] != 0x00) { + fp_dbg ("GetImage not ready, retrying"); + fpi_ssm_jump_to_state (ssm, IDENTIFY_GET_IMAGE); + return; + } + fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); } - fpi_device_report_finger_status (device, FP_FINGER_STATUS_PRESENT); + cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = 0x01; ma_submit_cmd (ssm, device, cmd, 2); break; @@ -617,12 +622,13 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) return; } - /* Pull the fingerprint template profile at our current iteration index */ FpPrint *print = g_ptr_array_index (prints, self->identify_index); GVariant *data = NULL; g_object_get (print, "fpi-data", &data, NULL); gint fid = 0; if (data) { + /* NOTE: Check your enroll/verify functions. If they pack data as a plain + * integer instead of a tuple, change "(i)" to "i" here so fid doesn't default to 0. */ g_variant_get (data, "(i)", &fid); g_variant_unref (data); } @@ -648,9 +654,12 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) GPtrArray *prints = NULL; fpi_device_get_identify_data (device, &prints); - /* If there are more registered prints left, loop back to search without re-imaging */ + /* FIX 2: Loop back to IDENTIFY_GEN_CHAR instead of IDENTIFY_SEARCH. + * This forces the hardware to re-extract features from its persistent image buffer + * to clear out the wiped character cache before testing the next slot ID. */ if (prints && self->identify_index < prints->len) { - fpi_ssm_jump_to_state (ssm, IDENTIFY_SEARCH); + fp_dbg ("Slot %d match failed. Re-extracting characteristics for next index...", self->fid); + fpi_ssm_jump_to_state (ssm, IDENTIFY_GEN_CHAR); } else { fp_dbg ("Scan completed: No matching enrolled prints found."); fpi_ssm_mark_completed (ssm); From b50d983d545f03e918cf9c134b5b477feb4d0540 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 19:19:30 +0100 Subject: [PATCH 24/26] small fix applied --- src/microarray.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/microarray.c b/src/microarray.c index 88a0e4c..0e5802f 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -592,9 +592,10 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; case IDENTIFY_GEN_CHAR: - /* FIX 1: Only check for image readiness if we actually just came from RECV_IMAGE. - * If we looped back here from CHECK_MATCH, resp_buf contains the SEARCH status instead! */ - if (fpi_ssm_get_prev_state (ssm) == IDENTIFY_RECV_IMAGE) { + /* FIX: libfprint doesn't track previous states natively. + * Instead, check our loop tracker: if identify_index is 0, this is the + * initial run coming straight from a brand-new image capture. */ + if (self->identify_index == 0) { if (self->resp_buf[MA_OVERHEAD] != 0x00) { fp_dbg ("GetImage not ready, retrying"); fpi_ssm_jump_to_state (ssm, IDENTIFY_GET_IMAGE); @@ -606,6 +607,9 @@ identify_run_state (FpiSsm *ssm, FpDevice *device) cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = 0x01; ma_submit_cmd (ssm, device, cmd, 2); break; + cmd[0] = MA_CMD_GEN_CHAR; cmd[1] = 0x01; + ma_submit_cmd (ssm, device, cmd, 2); + break; case IDENTIFY_RECV_GEN_CHAR: ma_submit_recv (ssm, device, MA_OVERHEAD + 3 + 2); break; From eec0eb9cfccd0ca3ae90d34d572bbec833a67bde Mon Sep 17 00:00:00 2001 From: JadegamesUK <39485724+jadegamesuk@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:34:13 +0100 Subject: [PATCH 25/26] Fix for latest Ubuntu version --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 650ec1a..6ce5f21 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ sudo apt install -y git build-essential meson ninja-build \ libgirepository1.0-dev gtk-doc-tools \ fprintd libpam-fprintd ``` +If you are on Ubuntu 25.4 or later you also need to run ```sudo apt install systemd-dev``` 3. Clone the official libfprint source code to ~/libfprint directory From a948285331fbbad3bc3aaf36bf9fa951655f6262 Mon Sep 17 00:00:00 2001 From: JadegamesUK Date: Wed, 3 Jun 2026 20:42:36 +0100 Subject: [PATCH 26/26] fix for Ubuntu 26.04 --- src/microarray.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/microarray.c b/src/microarray.c index 0e5802f..5fca54a 100644 --- a/src/microarray.c +++ b/src/microarray.c @@ -714,7 +714,23 @@ static void ma_identify (FpDevice *device) { FpiDeviceMicroarray *self = FPI_DEVICE_MICROARRAY (device); - self->identify_index = 0; /* Clear previous iteration trackers */ + GPtrArray *prints = NULL; + + /* Fetch the array of templates passed down by the system */ + fpi_device_get_identify_data (device, &prints); + + /* FIX: If this is a fresh install or the user has no enrolled prints, + * short-circuit the hardware state machine entirely. Report a clean + * "No Match" so fprintd's de-duplication check passes successfully. */ + if (!prints || prints->len == 0) { + fp_dbg ("No templates enrolled on the system. Skipping hardware search loop."); + fpi_device_identify_report (device, NULL, NULL, NULL); + fpi_device_identify_complete (device, NULL); + return; + } + + /* Reset loop index and start the state machine for actual hardware matching */ + self->identify_index = 0; FpiSsm *ssm = fpi_ssm_new (device, identify_run_state, IDENTIFY_NUM_STATES); fpi_ssm_start (ssm, identify_ssm_done); }