From a033154b2eaae1cba6259fb414887a7865c8bb80 Mon Sep 17 00:00:00 2001 From: Matthew Mirvish Date: Mon, 15 Nov 2021 17:10:44 -0500 Subject: [PATCH 01/19] doc: Fix broken documentation for FpiDeviceUdevSubtypeFlags enum Added description and fixed incorrect name in comment, so now gtkdoc actually shows useful information. --- libfprint/fpi-device.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h index 25e79c91..ab154bc1 100644 --- a/libfprint/fpi-device.h +++ b/libfprint/fpi-device.h @@ -25,9 +25,11 @@ #include "fpi-print.h" /** - * FpiDeviceUdevSubtype: + * FpiDeviceUdevSubtypeFlags: * @FPI_DEVICE_UDEV_SUBTYPE_SPIDEV: The device requires an spidev node * @FPI_DEVICE_UDEV_SUBTYPE_HIDRAW: The device requires a hidraw node + * + * Bitfield of required hardware resources for a udev-backed device. */ typedef enum { FPI_DEVICE_UDEV_SUBTYPE_SPIDEV = 1 << 0, From 05fd2c58cb9965b60e3b39de93e06abfb4f286da Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Wed, 24 Nov 2021 12:09:18 +0100 Subject: [PATCH 02/19] context: Ensure mainloop is idle before enumeration completes This ensures that we have processed all hotplug events before considering enumeration to be complete. This is important due to USB persist being turned off. At resume time, devices will disappear and immediately re-appear. In this situatoin, enumerate could first see the old state with a removed device resulting in it to not be discovered. As a hotplug event is semingly emitted by the kernel immediately, we can simply make sure to process this hotplug event before returning from enumerate. Closes: fprintd#119 --- libfprint/fp-context.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c index d4bd0000..1f322157 100644 --- a/libfprint/fp-context.c +++ b/libfprint/fp-context.c @@ -426,6 +426,7 @@ void fp_context_enumerate (FpContext *context) { FpContextPrivate *priv = fp_context_get_instance_private (context); + gboolean dispatched; gint i; g_return_if_fail (FP_IS_CONTEXT (context)); @@ -564,8 +565,19 @@ fp_context_enumerate (FpContext *context) } #endif - while (priv->pending_devices) - g_main_context_iteration (NULL, TRUE); + /* Iterate until 1. we have no pending devices, and 2. the mainloop is idle + * This takes care of processing hotplug events that happened during + * enumeration. + * This is important due to USB `persist` being turned off. At resume time, + * devices will disappear and immediately re-appear. In this situation, + * enumerate could first see the old state with a removed device resulting + * in it to not be discovered. + * As a hotplug event is seemingly emitted by the kernel immediately, we can + * simply make sure to process all events before returning from enumerate. + */ + dispatched = TRUE; + while (priv->pending_devices || dispatched) + dispatched = g_main_context_iteration (NULL, !!priv->pending_devices); } /** From 31afd3ba5c24974c6030220154a8f93dd85429c2 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Dec 2021 13:28:12 +0100 Subject: [PATCH 03/19] elanspi: Move debug print so that it contains all information Two of the printed variables were only calculated after the message was printed, making the logged information less useful than it could be. --- libfprint/drivers/elanspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libfprint/drivers/elanspi.c b/libfprint/drivers/elanspi.c index daea67c3..6ecd21ba 100644 --- a/libfprint/drivers/elanspi.c +++ b/libfprint/drivers/elanspi.c @@ -1219,8 +1219,6 @@ elanspi_guess_image (FpiDeviceElanSpi *self, guint16 *raw_image) sq_stddev /= (frame_width * frame_height); - fp_dbg (" stddev=%" G_GUINT64_FORMAT "d, ip=%d, is_fp=%d, is_empty=%d", sq_stddev, invalid_percent, is_fp, is_empty); - if (invalid_percent < ELANSPI_MAX_REAL_INVALID_PERCENT) is_fp += 1; if (invalid_percent > ELANSPI_MIN_EMPTY_INVALID_PERCENT) @@ -1231,6 +1229,8 @@ elanspi_guess_image (FpiDeviceElanSpi *self, guint16 *raw_image) if (sq_stddev < ELANSPI_MAX_EMPTY_STDDEV) is_empty += 1; + fp_dbg (" stddev=%" G_GUINT64_FORMAT "d, ip=%d, is_fp=%d, is_empty=%d", sq_stddev, invalid_percent, is_fp, is_empty); + if (is_fp > is_empty) return ELANSPI_GUESS_FINGERPRINT; else if (is_empty > is_fp) From 3981c42d3e26e3cbae808533963d14910b427ca5 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Dec 2021 13:44:57 +0100 Subject: [PATCH 04/19] ssm: Add API to silence most state change debug messages Otherwise tightly looping SSMs (primarily SPI transfers), will flood the logs in inappropriate ways. --- doc/libfprint-2-sections.txt | 1 + libfprint/fpi-ssm.c | 32 +++++++++++++++++++++++++------- libfprint/fpi-ssm.h | 2 ++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/doc/libfprint-2-sections.txt b/doc/libfprint-2-sections.txt index 91258b62..0fb0cfab 100644 --- a/doc/libfprint-2-sections.txt +++ b/doc/libfprint-2-sections.txt @@ -260,6 +260,7 @@ fpi_ssm_get_device fpi_ssm_get_error fpi_ssm_dup_error fpi_ssm_get_cur_state +fpi_ssm_silence_debug fpi_ssm_spi_transfer_cb fpi_ssm_spi_transfer_with_weak_pointer_cb fpi_ssm_usb_transfer_cb diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c index 8ceab674..c34498ab 100644 --- a/libfprint/fpi-ssm.c +++ b/libfprint/fpi-ssm.c @@ -81,6 +81,7 @@ struct _FpiSsm int start_cleanup; int cur_state; gboolean completed; + gboolean silence; GSource *timeout; GError *error; FpiSsmCompletedCallback callback; @@ -245,10 +246,11 @@ fpi_ssm_free (FpiSsm *machine) /* Invoke the state handler */ static void -__ssm_call_handler (FpiSsm *machine) +__ssm_call_handler (FpiSsm *machine, gboolean force_msg) { - fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev), - machine->name, machine->cur_state); + if (force_msg || !machine->silence) + fp_dbg ("[%s] %s entering state %d", fp_device_get_driver (machine->dev), + machine->name, machine->cur_state); machine->handler (machine, machine->dev); } @@ -275,7 +277,7 @@ fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback) ssm->cur_state = 0; ssm->completed = FALSE; ssm->error = NULL; - __ssm_call_handler (ssm); + __ssm_call_handler (ssm, TRUE); } static void @@ -346,7 +348,7 @@ fpi_ssm_mark_completed (FpiSsm *machine) if (next_state < machine->nr_states) { machine->cur_state = next_state; - __ssm_call_handler (machine); + __ssm_call_handler (machine, TRUE); return; } @@ -460,7 +462,7 @@ fpi_ssm_next_state (FpiSsm *machine) if (machine->cur_state == machine->nr_states) fpi_ssm_mark_completed (machine); else - __ssm_call_handler (machine); + __ssm_call_handler (machine, FALSE); } void @@ -537,7 +539,7 @@ fpi_ssm_jump_to_state (FpiSsm *machine, int state) if (machine->cur_state == machine->nr_states) fpi_ssm_mark_completed (machine); else - __ssm_call_handler (machine); + __ssm_call_handler (machine, FALSE); } typedef struct @@ -642,6 +644,22 @@ fpi_ssm_dup_error (FpiSsm *machine) return NULL; } +/** + * fpi_ssm_silence_debug: + * @machine: an #FpiSsm state machine + * + * Turn off state change debug messages from this SSM. This does not disable + * all messages, as e.g. the initial state, SSM completion and cleanup states + * are still printed out. + * + * Use if the SSM loops and would flood the debug log otherwise. + */ +void +fpi_ssm_silence_debug (FpiSsm *machine) +{ + machine->silence = TRUE; +} + /** * fpi_ssm_usb_transfer_cb: * @transfer: a #FpiUsbTransfer diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h index 235e84ad..d2601c88 100644 --- a/libfprint/fpi-ssm.h +++ b/libfprint/fpi-ssm.h @@ -96,6 +96,8 @@ GError * fpi_ssm_get_error (FpiSsm *machine); GError * fpi_ssm_dup_error (FpiSsm *machine); int fpi_ssm_get_cur_state (FpiSsm *machine); +void fpi_ssm_silence_debug (FpiSsm *machine); + /* Callbacks to be used by the driver instead of implementing their own * logic. */ From e198b0422220f14831de799e7a9bf7f0696df95f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 2 Dec 2021 13:46:07 +0100 Subject: [PATCH 05/19] elanspi: Silence some SSMs that may log excessively otherwise --- libfprint/drivers/elanspi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libfprint/drivers/elanspi.c b/libfprint/drivers/elanspi.c index 6ecd21ba..c3ddea9b 100644 --- a/libfprint/drivers/elanspi.c +++ b/libfprint/drivers/elanspi.c @@ -606,6 +606,7 @@ elanspi_calibrate_old_handler (FpiSsm *ssm, FpDevice *dev) case ELANSPI_CALIBOLD_CHECKFIN_CAPTURE: case ELANSPI_CALIBOLD_DACFINE_CAPTURE: chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); + fpi_ssm_silence_debug (chld); fpi_ssm_start_subsm (ssm, chld); return; @@ -860,6 +861,7 @@ elanspi_calibrate_hv_handler (FpiSsm *ssm, FpDevice *dev) case ELANSPI_CALIBHV_CAPTURE: chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); + fpi_ssm_silence_debug (chld); fpi_ssm_start_subsm (ssm, chld); return; @@ -1115,6 +1117,7 @@ do_sw_reset: chld = fpi_ssm_new_full (dev, elanspi_calibrate_hv_handler, ELANSPI_CALIBHV_NSTATES, ELANSPI_CALIBHV_PROTECT, "HV calibrate"); else chld = fpi_ssm_new_full (dev, elanspi_calibrate_old_handler, ELANSPI_CALIBOLD_NSTATES, ELANSPI_CALIBOLD_PROTECT, "old calibrate"); + fpi_ssm_silence_debug (chld); fpi_ssm_start_subsm (ssm, chld); return; @@ -1123,6 +1126,7 @@ do_sw_reset: chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); else chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); + fpi_ssm_silence_debug (chld); fpi_ssm_start_subsm (ssm, chld); return; @@ -1495,6 +1499,7 @@ elanspi_fp_capture_ssm_handler (FpiSsm *ssm, FpDevice *dev) chld = fpi_ssm_new (dev, elanspi_capture_hv_handler, ELANSPI_CAPTHV_NSTATES); else chld = fpi_ssm_new (dev, elanspi_capture_old_handler, ELANSPI_CAPTOLD_NSTATES); + fpi_ssm_silence_debug (chld); fpi_ssm_start_subsm (ssm, chld); return; From 999bca076cd69a6ae4df6dd668bcf45a6565fc44 Mon Sep 17 00:00:00 2001 From: Dmitrii Shcherbakov Date: Sun, 29 Aug 2021 14:18:29 +0300 Subject: [PATCH 06/19] Allow FpPrint data to be extended on enrollment. * Allow FPI_PRINT_NBIS to be extended rather than overridden if a user supplies an existing FpPrint template with data; * Prints will only be extended if a device has the required feature. For image-based devices this feature is added by default since they typically do not have storage (this can be overridden at the child class level). Extending an existing FpPrint requires passing a template print with existing data during the enrollment process. This is done because the caller is responsible for maintaining the fingerprint database and doing the necessary deserialization operations if needed. The existing example program is updated to show how to do that. --- examples/enroll.c | 63 +++++++++++++++--- examples/storage.c | 54 ++++++++++------ examples/storage.h | 8 ++- libfprint/fp-device.c | 32 +++++++--- libfprint/fp-device.h | 2 + libfprint/fp-image-device.c | 6 +- libfprint/fp-print.c | 23 +++++++ tests/test-fpi-device.c | 124 +++++++++++++++++++++++++++++++++++- tests/virtual-image.py | 45 ++++++++++--- 9 files changed, 305 insertions(+), 52 deletions(-) diff --git a/examples/enroll.c b/examples/enroll.c index f133c8b8..10dfa5da 100644 --- a/examples/enroll.c +++ b/examples/enroll.c @@ -35,6 +35,7 @@ typedef struct _EnrollData unsigned int sigint_handler; FpFinger finger; int ret_value; + gboolean update_fingerprint; } EnrollData; static void @@ -84,7 +85,8 @@ on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data) /* Even if the device has storage, it may not be able to save all the * metadata that the print contains, so we can always save a local copy * containing the handle to the device print */ - int r = print_data_save (print, enroll_data->finger); + int r = print_data_save (print, enroll_data->finger, + enroll_data->update_fingerprint); if (r < 0) { g_warning ("Data save failed, code %d", r); @@ -124,6 +126,40 @@ on_enroll_progress (FpDevice *device, fp_device_get_nr_enroll_stages (device)); } +static gboolean +should_update_fingerprint (void) +{ + int update_choice; + gboolean update_fingerprint = FALSE; + + printf ("Should an existing fingerprint be updated instead of being replaced (if present)? " + "Enter Y/y or N/n to make a choice.\n"); + update_choice = getchar (); + if (update_choice == EOF) + { + g_warning ("EOF encountered while reading a character"); + return EXIT_FAILURE; + } + + switch (update_choice) + { + case 'y': + case 'Y': + update_fingerprint = TRUE; + break; + + case 'n': + case 'N': + update_fingerprint = FALSE; + break; + + default: + g_warning ("Invalid choice %c, should be Y/y or N/n.", update_choice); + return EXIT_FAILURE; + } + return update_fingerprint; +} + static void on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) { @@ -139,13 +175,26 @@ on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data) return; } - printf ("Opened device. It's now time to enroll your finger.\n\n"); + printf ("Opened device.\n"); + + if (fp_device_has_feature (dev, FP_DEVICE_FEATURE_UPDATE_PRINT)) + { + printf ("The device supports fingerprint updates.\n"); + enroll_data->update_fingerprint = should_update_fingerprint (); + } + else + { + printf ("The device doesn't support fingerprint updates. Old prints will be erased.\n"); + enroll_data->update_fingerprint = FALSE; + } + + printf ("It's now time to enroll your finger.\n\n"); printf ("You will need to successfully scan your %s finger %d times to " "complete the process.\n\n", finger_to_string (enroll_data->finger), fp_device_get_nr_enroll_stages (dev)); printf ("Scan your finger now.\n"); - print_template = print_create_template (dev, enroll_data->finger); + print_template = print_create_template (dev, enroll_data->finger, enroll_data->update_fingerprint); fp_device_enroll (dev, print_template, enroll_data->cancellable, on_enroll_progress, NULL, NULL, (GAsyncReadyCallback) on_enroll_completed, @@ -171,11 +220,9 @@ main (void) FpDevice *dev; FpFinger finger; - g_print ("This program will enroll the selected finger, unconditionally " - "overwriting any print for the same finger that was enrolled " - "previously. If you want to continue, press enter, otherwise hit " - "Ctrl+C\n"); - getchar (); + g_print ("This program will enroll the selected finger overwriting any print for the same" + " finger that was enrolled previously. Fingerprint updates without erasing old data" + " are possible on devices supporting that. Ctrl+C interrupts program execution.\n"); g_print ("Choose the finger to enroll:\n"); finger = finger_chooser (); diff --git a/examples/storage.c b/examples/storage.c index 6c92dba6..b84c72d5 100644 --- a/examples/storage.c +++ b/examples/storage.c @@ -102,8 +102,23 @@ save_data (GVariant *data) return 0; } +static FpPrint * +load_print_from_data (GVariant *data) +{ + const guchar *stored_data = NULL; + gsize stored_len; + FpPrint *print; + + g_autoptr(GError) error = NULL; + stored_data = (const guchar *) g_variant_get_fixed_array (data, &stored_len, 1); + print = fp_print_deserialize (stored_data, stored_len, &error); + if (error) + g_warning ("Error deserializing data: %s", error->message); + return print; +} + int -print_data_save (FpPrint *print, FpFinger finger) +print_data_save (FpPrint *print, FpFinger finger, gboolean update_fingerprint) { g_autofree gchar *descr = get_print_data_descriptor (print, NULL, finger); @@ -137,25 +152,12 @@ print_data_load (FpDevice *dev, FpFinger finger) g_autoptr(GVariant) val = NULL; g_autoptr(GVariantDict) dict = NULL; - const guchar *stored_data = NULL; - gsize stored_len; dict = load_data (); val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay")); if (val) - { - FpPrint *print; - g_autoptr(GError) error = NULL; - - stored_data = (const guchar *) g_variant_get_fixed_array (val, &stored_len, 1); - print = fp_print_deserialize (stored_data, stored_len, &error); - - if (error) - g_warning ("Error deserializing data: %s", error->message); - - return print; - } + return load_print_from_data (val); return NULL; } @@ -207,16 +209,30 @@ gallery_data_load (FpDevice *dev) } FpPrint * -print_create_template (FpDevice *dev, FpFinger finger) +print_create_template (FpDevice *dev, FpFinger finger, gboolean load_existing) { + g_autoptr(GVariantDict) dict = NULL; g_autoptr(GDateTime) datetime = NULL; g_autoptr(GDate) date = NULL; + g_autoptr(GVariant) existing_val = NULL; + g_autofree gchar *descr = get_print_data_descriptor (NULL, dev, finger); FpPrint *template = NULL; gint year, month, day; - template = fp_print_new (dev); - fp_print_set_finger (template, finger); - fp_print_set_username (template, g_get_user_name ()); + if (load_existing) + { + dict = load_data (); + existing_val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay")); + if (existing_val != NULL) + template = load_print_from_data (existing_val); + } + if (template == NULL) + { + template = fp_print_new (dev); + fp_print_set_finger (template, finger); + fp_print_set_username (template, g_get_user_name ()); + } + datetime = g_date_time_new_now_local (); g_date_time_get_ymd (datetime, &year, &month, &day); date = g_date_new_dmy (day, month, year); diff --git a/examples/storage.h b/examples/storage.h index 81390bd0..0922b902 100644 --- a/examples/storage.h +++ b/examples/storage.h @@ -21,12 +21,14 @@ #pragma once int print_data_save (FpPrint *print, - FpFinger finger); + FpFinger finger, + gboolean update_fingerprint); FpPrint * print_data_load (FpDevice *dev, FpFinger finger); GPtrArray * gallery_data_load (FpDevice *dev); -FpPrint * print_create_template (FpDevice *dev, - FpFinger finger); +FpPrint * print_create_template (FpDevice *dev, + FpFinger finger, + const gboolean load_existing); gboolean print_image_save (FpPrint *print, const char *path); gboolean save_image_to_pgm (FpImage *img, diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c index 35e2f2b8..82f309e8 100644 --- a/libfprint/fp-device.c +++ b/libfprint/fp-device.c @@ -1189,10 +1189,11 @@ fp_device_resume_finish (FpDevice *device, * fp_device_enroll_finish(). * * The @template_print parameter is a #FpPrint with available metadata filled - * in. The driver may make use of this metadata, when e.g. storing the print on - * device memory. It is undefined whether this print is filled in by the driver - * and returned, or whether the driver will return a newly created print after - * enrollment succeeded. + * in and, optionally, with existing fingerprint data to be updated with newly + * enrolled fingerprints if a device driver supports it. The driver may make use + * of the metadata, when e.g. storing the print on device memory. It is undefined + * whether this print is filled in by the driver and returned, or whether the + * driver will return a newly created print after enrollment succeeded. */ void fp_device_enroll (FpDevice *device, @@ -1229,19 +1230,30 @@ fp_device_enroll (FpDevice *device, if (!FP_IS_PRINT (template_print)) { - g_warning ("User did not pass a print template!"); g_task_return_error (task, - fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "User did not pass a print template!")); return; } g_object_get (template_print, "fpi-type", &print_type, NULL); if (print_type != FPI_PRINT_UNDEFINED) { - g_warning ("Passed print template must be newly created and blank!"); - g_task_return_error (task, - fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); - return; + if (!fp_device_has_feature (device, FP_DEVICE_FEATURE_UPDATE_PRINT)) + { + g_task_return_error (task, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "A device does not support print updates!")); + return; + } + if (!fp_print_compatible (template_print, device)) + { + g_task_return_error (task, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_INVALID, + "The print and device must have a matching driver and device id" + " for a fingerprint update to succeed")); + return; + } } priv->current_action = FPI_DEVICE_ACTION_ENROLL; diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h index 85be34c4..9dda747c 100644 --- a/libfprint/fp-device.h +++ b/libfprint/fp-device.h @@ -59,6 +59,7 @@ typedef enum { * @FP_DEVICE_FEATURE_STORAGE_CLEAR: Supports clearing the whole storage * @FP_DEVICE_FEATURE_DUPLICATES_CHECK: Natively supports duplicates detection * @FP_DEVICE_FEATURE_ALWAYS_ON: Whether the device can run continuously + * @FP_DEVICE_FEATURE_UPDATE_PRINT: Supports updating an existing print record using new scans */ typedef enum /*< flags >*/ { FP_DEVICE_FEATURE_NONE = 0, @@ -71,6 +72,7 @@ typedef enum /*< flags >*/ { FP_DEVICE_FEATURE_STORAGE_CLEAR = 1 << 6, FP_DEVICE_FEATURE_DUPLICATES_CHECK = 1 << 7, FP_DEVICE_FEATURE_ALWAYS_ON = 1 << 8, + FP_DEVICE_FEATURE_UPDATE_PRINT = 1 << 9, } FpDeviceFeature; /** diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c index 82e69f99..519ad754 100644 --- a/libfprint/fp-image-device.c +++ b/libfprint/fp-image-device.c @@ -101,6 +101,7 @@ fp_image_device_start_capture_action (FpDevice *device) FpImageDevice *self = FP_IMAGE_DEVICE (device); FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self); FpiDeviceAction action; + FpiPrintType print_type; /* There is just one action that we cannot support out * of the box, which is a capture without first waiting @@ -124,7 +125,9 @@ fp_image_device_start_capture_action (FpDevice *device) FpPrint *enroll_print = NULL; fpi_device_get_enroll_data (device, &enroll_print); - fpi_print_set_type (enroll_print, FPI_PRINT_NBIS); + g_object_get (enroll_print, "fpi-type", &print_type, NULL); + if (print_type != FPI_PRINT_NBIS) + fpi_print_set_type (enroll_print, FPI_PRINT_NBIS); } priv->enroll_stage = 0; @@ -221,6 +224,7 @@ fp_image_device_class_init (FpImageDeviceClass *klass) fp_device_class->cancel = fp_image_device_cancel_action; fpi_device_class_auto_initialize_features (fp_device_class); + fp_device_class->features |= FP_DEVICE_FEATURE_UPDATE_PRINT; /* Default implementations */ klass->activate = fp_image_device_default_activate; diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c index c8a1b071..8532b6cc 100644 --- a/libfprint/fp-print.c +++ b/libfprint/fp-print.c @@ -61,6 +61,7 @@ enum { /* Private property*/ PROP_FPI_TYPE, PROP_FPI_DATA, + PROP_FPI_PRINTS, N_PROPS }; @@ -133,6 +134,10 @@ fp_print_get_property (GObject *object, g_value_set_variant (value, self->data); break; + case PROP_FPI_PRINTS: + g_value_set_pointer (value, self->prints); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -188,6 +193,11 @@ fp_print_set_property (GObject *object, self->data = g_value_dup_variant (value); break; + case PROP_FPI_PRINTS: + g_clear_pointer (&self->prints, g_ptr_array_unref); + self->prints = g_value_get_pointer (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -299,6 +309,19 @@ fp_print_class_init (FpPrintClass *klass) NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + /** + * FpPrint::fpi-prints: (skip) + * + * This property is only for internal purposes. + * + * Stability: private + */ + properties[PROP_FPI_PRINTS] = + g_param_spec_pointer ("fpi-prints", + "Prints", + "Prints for internal use only", + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + g_object_class_install_properties (object_class, N_PROPS, properties); } diff --git a/tests/test-fpi-device.c b/tests/test-fpi-device.c index 408e2f92..ff658e97 100644 --- a/tests/test-fpi-device.c +++ b/tests/test-fpi-device.c @@ -27,6 +27,7 @@ #include "fpi-compat.h" #include "fpi-log.h" #include "test-device-fake.h" +#include "fp-print-private.h" /* Utility functions */ @@ -142,6 +143,16 @@ make_fake_print (FpDevice *device, return enrolled_print; } +static FpPrint * +make_fake_nbis_print (FpDevice *device) +{ + FpPrint *enrolled_print = fp_print_new (device); + + fpi_print_set_type (enrolled_print, FPI_PRINT_NBIS); + + return enrolled_print; +} + static FpPrint * make_fake_print_reffed (FpDevice *device, GVariant *print_data) @@ -1045,7 +1056,6 @@ test_driver_enroll_error_no_print (void) out_print = fp_device_enroll_sync (device, fp_print_new (device), NULL, NULL, NULL, &error); - g_test_assert_expected_messages (); g_assert (fake_dev->last_called_function == dev_class->enroll); g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL); g_assert_null (out_print); @@ -1053,6 +1063,111 @@ test_driver_enroll_error_no_print (void) g_clear_error (&error); } +static void +test_driver_enroll_update_nbis (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) template_print = NULL; + FpiDeviceFake *fake_dev = NULL; + FpPrint *out_print = NULL; + + dev_class->features |= FP_DEVICE_FEATURE_UPDATE_PRINT; + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + template_print = make_fake_nbis_print (device); + fake_dev->ret_print = template_print; + + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->enroll); + g_assert (fake_dev->action_data == template_print); + + g_assert_no_error (error); + g_assert (out_print == template_print); +} + +static void +test_driver_enroll_update_nbis_wrong_device (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) template_print = NULL; + FpiDeviceFake *fake_dev = NULL; + FpPrint *out_print = NULL; + + dev_class->features |= FP_DEVICE_FEATURE_UPDATE_PRINT; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + template_print = make_fake_nbis_print (device); + template_print->device_id = g_strdup ("wrong_device"); + fake_dev->ret_print = template_print; + + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_assert (out_print == NULL); +} + +static void +test_driver_enroll_update_nbis_wrong_driver (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) template_print = NULL; + FpiDeviceFake *fake_dev = NULL; + FpPrint *out_print = NULL; + + dev_class->features |= FP_DEVICE_FEATURE_UPDATE_PRINT; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + template_print = make_fake_nbis_print (device); + template_print->driver = g_strdup ("wrong_driver"); + fake_dev->ret_print = template_print; + + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_assert (out_print == NULL); +} + +static void +test_driver_enroll_update_nbis_missing_feature (void) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FpAutoResetClass) dev_class = auto_reset_device_class (); + g_autoptr(FpAutoCloseDevice) device = NULL; + g_autoptr(FpPrint) template_print = NULL; + FpiDeviceFake *fake_dev = NULL; + FpPrint *out_print = NULL; + + device = auto_close_fake_device_new (); + fake_dev = FPI_DEVICE_FAKE (device); + + template_print = make_fake_nbis_print (device); + fake_dev->ret_print = template_print; + + out_print = + fp_device_enroll_sync (device, template_print, NULL, NULL, NULL, &error); + + g_assert (fake_dev->last_called_function == dev_class->open); + g_assert (fake_dev->action_data == NULL); + + g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_DATA_INVALID); + g_assert (out_print == NULL); +} + typedef struct { gint completed_stages; @@ -3306,6 +3421,13 @@ main (int argc, char *argv[]) g_test_add_func ("/driver/enroll/error", test_driver_enroll_error); g_test_add_func ("/driver/enroll/error/no_print", test_driver_enroll_error_no_print); g_test_add_func ("/driver/enroll/progress", test_driver_enroll_progress); + g_test_add_func ("/driver/enroll/update_nbis", test_driver_enroll_update_nbis); + g_test_add_func ("/driver/enroll/update_nbis_wrong_device", + test_driver_enroll_update_nbis_wrong_device); + g_test_add_func ("/driver/enroll/update_nbis_wrong_driver", + test_driver_enroll_update_nbis_wrong_driver); + g_test_add_func ("/driver/enroll/update_nbis_missing_feature", + test_driver_enroll_update_nbis_missing_feature); g_test_add_func ("/driver/verify", test_driver_verify); g_test_add_func ("/driver/verify/fail", test_driver_verify_fail); g_test_add_func ("/driver/verify/retry", test_driver_verify_retry); diff --git a/tests/virtual-image.py b/tests/virtual-image.py index 7605b583..e4a464eb 100755 --- a/tests/virtual-image.py +++ b/tests/virtual-image.py @@ -136,6 +136,7 @@ class VirtualImage(unittest.TestCase): self.assertTrue(self.dev.has_feature(FPrint.DeviceFeature.CAPTURE)) self.assertTrue(self.dev.has_feature(FPrint.DeviceFeature.IDENTIFY)) self.assertTrue(self.dev.has_feature(FPrint.DeviceFeature.VERIFY)) + self.assertTrue(self.dev.has_feature(FPrint.DeviceFeature.UPDATE_PRINT)) self.assertFalse(self.dev.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK)) self.assertFalse(self.dev.has_feature(FPrint.DeviceFeature.STORAGE)) self.assertFalse(self.dev.has_feature(FPrint.DeviceFeature.STORAGE_LIST)) @@ -144,7 +145,8 @@ class VirtualImage(unittest.TestCase): self.assertEqual(self.dev.get_features(), FPrint.DeviceFeature.CAPTURE | FPrint.DeviceFeature.IDENTIFY | - FPrint.DeviceFeature.VERIFY) + FPrint.DeviceFeature.VERIFY | + FPrint.DeviceFeature.UPDATE_PRINT) def test_capture_prevents_close(self): cancel = Gio.Cancellable() @@ -167,7 +169,7 @@ class VirtualImage(unittest.TestCase): while not self._cancelled: ctx.iteration(True) - def enroll_print(self, image): + def enroll_print(self, image, template=None): self._step = 0 self._enrolled = None @@ -181,14 +183,15 @@ class VirtualImage(unittest.TestCase): self.assertEqual(self.dev.get_finger_status(), FPrint.FingerStatusFlags.NONE) self._enrolled = fp - template = FPrint.Print.new(self.dev) - template.props.finger = FPrint.Finger.LEFT_THUMB - template.props.username = "testuser" - template.props.description = "test print" - datetime = GLib.DateTime.new_now_local() - date = GLib.Date() - date.set_dmy(*datetime.get_ymd()[::-1]) - template.props.enroll_date = date + if template is None: + template = FPrint.Print.new(self.dev) + template.props.finger = FPrint.Finger.LEFT_THUMB + template.props.username = "testuser" + template.props.description = "test print" + datetime = GLib.DateTime.new_now_local() + date = GLib.Date() + date.set_dmy(*datetime.get_ymd()[::-1]) + template.props.enroll_date = date self.assertEqual(self.dev.get_finger_status(), FPrint.FingerStatusFlags.NONE) self.dev.enroll(template, None, progress_cb, tuple(), done_cb) @@ -264,6 +267,28 @@ class VirtualImage(unittest.TestCase): ctx.iteration(True) assert(not self._verify_match) + # Test fingerprint updates + # Enroll a second print + fp_whorl_tended_arch = self.enroll_print('tented_arch', fp_whorl) + + # Make sure the first print verifies successfully after the update + self._verify_match = None + self._verify_fp = None + self.dev.verify(fp_whorl_tended_arch, callback=verify_cb) + self.send_image('whorl') + while self._verify_match is None: + ctx.iteration(True) + assert(self._verify_match) + + # Make sure the second print verifies successfully after the update + self._verify_match = None + self._verify_fp = None + self.dev.verify(fp_whorl_tended_arch, callback=verify_cb) + self.send_image('tented_arch') + while self._verify_match is None: + ctx.iteration(True) + assert(self._verify_match) + # Test verify error cases self._verify_fp = None self._verify_error = None From 7565562903544f22d4eb0688330a6efd43f04c05 Mon Sep 17 00:00:00 2001 From: mincrmatt12 Date: Tue, 21 Dec 2021 03:19:01 -0500 Subject: [PATCH 07/19] elanspi: Adjust register tables (fixes #438) New values taken from a newer version of the official driver. --- libfprint/drivers/elanspi.h | 38 +++++++++++++++++++++++++++++++++---- tests/elanspi/capture.ioctl | 7 +++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/libfprint/drivers/elanspi.h b/libfprint/drivers/elanspi.h index c9e17b8c..ffe34160 100644 --- a/libfprint/drivers/elanspi.h +++ b/libfprint/drivers/elanspi.h @@ -97,7 +97,37 @@ static const struct elanspi_reg_entry elanspi_calibration_table_default[] = { {0xff, 0xff} }; -static const struct elanspi_reg_entry elanspi_calibration_table_id567[] = { +static const struct elanspi_reg_entry elanspi_calibration_table_id6[] = { + {0x2A, 0x07}, + {0x1, 0x00}, + {0x2, 0x5f}, + {0x3, 0x00}, + {0x4, 0x5f}, + {0x5, 0x60}, + {0x6, 0xC0}, + {0x7, 0x80}, + {0x8, 0x04}, + {0xA, 0x97}, + {0xB, 0x72}, + {0xC, 0x69}, + {0xF, 0x2A}, + {0x11, 0x2A}, + {0x13, 0x27}, + {0x15, 0x67}, + {0x18, 0x04}, + {0x21, 0x20}, + {0x22, 0x36}, + {0x29, 0x02}, + {0x2A, 0x03}, + {0x2A, 0x5F}, + {0x2B, 0xC0}, + {0x2C, 0x10}, + {0x2E, 0xFF}, + + {0xff, 0xff} +}; + +static const struct elanspi_reg_entry elanspi_calibration_table_id57[] = { {0x2A, 0x07}, {0x5, 0x60}, {0x6, 0xC0}, @@ -143,9 +173,9 @@ static const struct elanspi_regtable elanspi_calibration_table_old = { .other = elanspi_calibration_table_default, .entries = { { .sid = 0x0, .table = elanspi_calibration_table_id0 }, - { .sid = 0x5, .table = elanspi_calibration_table_id567 }, - { .sid = 0x6, .table = elanspi_calibration_table_id567 }, - { .sid = 0x7, .table = elanspi_calibration_table_id567 }, + { .sid = 0x5, .table = elanspi_calibration_table_id57 }, + { .sid = 0x6, .table = elanspi_calibration_table_id6 }, + { .sid = 0x7, .table = elanspi_calibration_table_id57 }, { .sid = 0x0, .table = NULL } } }; diff --git a/tests/elanspi/capture.ioctl b/tests/elanspi/capture.ioctl index dcd5821e..fc292d36 100644 --- a/tests/elanspi/capture.ioctl +++ b/tests/elanspi/capture.ioctl @@ -24,6 +24,10 @@ TW 8c62 TW 805a TW 04 TW aa07 +TW 8100 +TW 825f +TW 8300 +TW 845f TW 8560 TW 86c0 TW 8780 @@ -38,8 +42,11 @@ TW 9567 TW 9804 TW a120 TW a236 +TW a902 +TW aa03 TW aa5f TW abc0 +TW ac10 TW aeff TW 01 TW 03ff From 5beac0ded7992b107893a155f5b6b0362d7faf11 Mon Sep 17 00:00:00 2001 From: mincrmatt12 Date: Wed, 22 Dec 2021 20:35:30 -0500 Subject: [PATCH 08/19] elanspi: Try to avoid cancellation problems --- libfprint/drivers/elanspi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/elanspi.c b/libfprint/drivers/elanspi.c index c3ddea9b..9338013d 100644 --- a/libfprint/drivers/elanspi.c +++ b/libfprint/drivers/elanspi.c @@ -439,6 +439,12 @@ elanspi_capture_old_line_handler (FpiSpiTransfer *transfer, FpDevice *dev, gpoin } else { + /* check for termination */ + if (fpi_device_get_current_action (dev) == FPI_DEVICE_ACTION_NONE) + { + fpi_ssm_mark_completed (transfer->ssm); + return; + } /* check for cancellation */ if (fpi_device_action_is_cancelled (dev)) { @@ -1486,11 +1492,12 @@ elanspi_fp_capture_ssm_handler (FpiSsm *ssm, FpDevice *dev) if (self->deactivating) { fp_dbg (" got deactivate; exiting"); + + self->deactivating = FALSE; fpi_ssm_mark_completed (ssm); /* mark deactivate done */ fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), NULL); - self->deactivating = FALSE; return; } From eb1013cdb6bcb22e52c22973eeac60b2c668d9ae Mon Sep 17 00:00:00 2001 From: Aris Lin Date: Thu, 27 Jan 2022 11:14:31 +0800 Subject: [PATCH 09/19] synaptics: Remove PID 0xC9 --- data/autosuspend.hwdb | 2 +- libfprint/drivers/synaptics/synaptics.c | 1 - libfprint/fprint-list-udev-hwdb.c | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index cbc6109f..8bbc52c8 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -181,7 +181,6 @@ usb:v06CBp00DF* usb:v06CBp00F9* usb:v06CBp00FC* usb:v06CBp00C2* -usb:v06CBp00C9* usb:v06CBp0100* usb:v06CBp00F0* usb:v06CBp0103* @@ -277,6 +276,7 @@ usb:v06CBp00D8* usb:v06CBp00DA* usb:v06CBp00E7* usb:v06CBp00E9* +usb:v06CBp00C9* usb:v0A5Cp5801* usb:v0A5Cp5805* usb:v0A5Cp5834* diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c index 98627ed7..7a2c6ebe 100644 --- a/libfprint/drivers/synaptics/synaptics.c +++ b/libfprint/drivers/synaptics/synaptics.c @@ -36,7 +36,6 @@ static const FpIdEntry id_table[] = { { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F9, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00FC, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C2, }, - { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00C9, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0100, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x00F0, }, { .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0103, }, diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index f5674574..19b49e83 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -51,6 +51,7 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x06cb, .pid = 0x00da }, { .vid = 0x06cb, .pid = 0x00e7 }, { .vid = 0x06cb, .pid = 0x00e9 }, + { .vid = 0x06cb, .pid = 0x00c9 }, { .vid = 0x0a5c, .pid = 0x5801 }, { .vid = 0x0a5c, .pid = 0x5805 }, { .vid = 0x0a5c, .pid = 0x5834 }, From 038c7108a66fb9d69b7bfbe2e22b987f5de41573 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 27 Dec 2021 13:44:16 +0100 Subject: [PATCH 10/19] goodixmoc: Further template parsing fixes In commit 5c28654d9 ("goodixmoc: Fix print template parsing") the length check for the verify and duplicate check responses by requiring two extra bytes at the end of the message. There were also issues in other places where the length was not checked correctly, including a scenario that could cause a read beyond the end of the buffer. Related: #444 --- libfprint/drivers/goodixmoc/goodix_proto.c | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libfprint/drivers/goodixmoc/goodix_proto.c b/libfprint/drivers/goodixmoc/goodix_proto.c index 01044a94..b615dbaa 100644 --- a/libfprint/drivers/goodixmoc/goodix_proto.c +++ b/libfprint/drivers/goodixmoc/goodix_proto.c @@ -259,12 +259,9 @@ gx_proto_parse_fingerid ( if (buffer[Offset++] != 67) return -1; - fid_buffer_size--; template->type = buffer[Offset++]; - fid_buffer_size--; template->finger_index = buffer[Offset++]; - fid_buffer_size--; Offset++; memcpy (template->accountid, &buffer[Offset], sizeof (template->accountid)); Offset += sizeof (template->accountid); @@ -273,6 +270,8 @@ gx_proto_parse_fingerid ( template->payload.size = buffer[Offset++]; if (template->payload.size > sizeof (template->payload.data)) return -1; + if (template->payload.size + Offset > fid_buffer_size) + return -1; memset (template->payload.data, 0, template->payload.size); memcpy (template->payload.data, &buffer[Offset], template->payload.size); @@ -365,9 +364,12 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_c if (buffer_len < 3) return -1; uint16_t tid_size = GUINT16_FROM_LE (*(uint16_t *) (buffer + 1)); - if ((buffer_len < tid_size + 3) || (buffer_len > sizeof (template_format_t)) + 3) + offset += 3; + + if (buffer_len < tid_size + offset) + return -1; + if (gx_proto_parse_fingerid (buffer + offset, tid_size, &presp->check_duplicate_resp.template) != 0) return -1; - memcpy (&presp->check_duplicate_resp.template, buffer + 3, tid_size); } break; @@ -380,9 +382,12 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_c fingerlist = buffer + 2; for(uint8_t num = 0; num < presp->finger_list_resp.finger_num; num++) { - uint16_t fingerid_length = GUINT16_FROM_LE (*(uint16_t *) (fingerlist + offset)); + uint16_t fingerid_length; + if (buffer_len < offset + 2) + return -1; + fingerid_length = GUINT16_FROM_LE (*(uint16_t *) (fingerlist + offset)); offset += 2; - if (buffer_len < fingerid_length + offset + 2) + if (buffer_len < fingerid_length + offset) return -1; if (gx_proto_parse_fingerid (fingerlist + offset, fingerid_length, @@ -405,7 +410,7 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_c presp->verify.match = (buffer[0] == 0) ? true : false; if (presp->verify.match) { - if (buffer_len < sizeof (template_format_t) + 10) + if (buffer_len < 10) return -1; offset += 1; presp->verify.rejectdetail = GUINT16_FROM_LE (*(uint16_t *) (buffer + offset)); @@ -416,6 +421,8 @@ gx_proto_parse_body (uint16_t cmd, uint8_t *buffer, uint16_t buffer_len, pgxfp_c offset += 1; fingerid_size = GUINT16_FROM_LE (*(uint16_t *) (buffer + offset)); offset += 2; + if (buffer_len < fingerid_size + offset) + return -1; if (gx_proto_parse_fingerid (buffer + offset, fingerid_size, &presp->verify.template) != 0) { presp->result = GX_FAILED; From ae5696a9bb5cf2fbc589430853239586f7bcd54f Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 28 Jan 2022 17:29:53 +0100 Subject: [PATCH 11/19] goodixmoc: Change error message for corrupted headers Otherwise you can't tell from the log whether parsing the body or header failed. --- libfprint/drivers/goodixmoc/goodix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 15a85323..ad5a58d4 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -159,7 +159,7 @@ fp_cmd_receive_cb (FpiUsbTransfer *transfer, { fpi_ssm_mark_failed (transfer->ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, - "Corrupted message received")); + "Corrupted message header received")); return; } From 168ab980219a3265b0f70e6f19edd65e6d9fc59c Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 28 Jan 2022 17:30:37 +0100 Subject: [PATCH 12/19] examples: Check whether the returned date is valid Prints may have an invalid date. Extend the checks so that this is also caught in addition to a NULL date. --- examples/identify.c | 4 +++- examples/manage-prints.c | 2 +- examples/verify.c | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/identify.c b/examples/identify.c index bc2fe00d..dc6a5d5c 100644 --- a/examples/identify.c +++ b/examples/identify.c @@ -143,6 +143,7 @@ on_identify_cb (FpDevice *dev, FpPrint *match, FpPrint *print, if (match) { g_autoptr(FpPrint) matched_print = g_object_ref (match); + const GDate *date; char date_str[128] = {}; identify_data->ret_value = EXIT_SUCCESS; @@ -155,7 +156,8 @@ on_identify_cb (FpDevice *dev, FpPrint *match, FpPrint *print, matched_print = g_steal_pointer (&stored_print); } - if (fp_print_get_enroll_date (matched_print)) + date = fp_print_get_enroll_date (matched_print); + if (date && g_date_valid (date)) g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", fp_print_get_enroll_date (matched_print)); else diff --git a/examples/manage-prints.c b/examples/manage-prints.c index 4d206cc9..88200a35 100644 --- a/examples/manage-prints.c +++ b/examples/manage-prints.c @@ -161,7 +161,7 @@ on_list_completed (FpDevice *dev, finger_to_string (fp_print_get_finger (print)), fp_print_get_username (print)); - if (date) + if (date && g_date_valid (date)) { g_date_strftime (buf, G_N_ELEMENTS (buf), "%Y-%m-%d\0", date); g_print (", enrolled on %s", buf); diff --git a/examples/verify.c b/examples/verify.c index 4b16323d..6892ef0e 100644 --- a/examples/verify.c +++ b/examples/verify.c @@ -130,12 +130,14 @@ on_match_cb (FpDevice *dev, FpPrint *match, FpPrint *print, if (match) { - char date_str[128]; + const GDate *date = fp_print_get_enroll_date (match); + char date_str[128] = ""; verify_data->ret_value = EXIT_SUCCESS; - g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", - fp_print_get_enroll_date (match)); + if (date && g_date_valid (date)) + g_date_strftime (date_str, G_N_ELEMENTS (date_str), "%Y-%m-%d\0", + fp_print_get_enroll_date (match)); g_debug ("Match report: device %s matched finger %s successifully " "with print %s, enrolled on date %s by user %s", fp_device_get_name (dev), From e0fd178bec9b887386fd6e6d7829a9756f82e3d8 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 28 Jan 2022 18:14:05 +0100 Subject: [PATCH 13/19] goodixmoc: Log which the ID that produced the duplicate --- libfprint/drivers/goodixmoc/goodix.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index ad5a58d4..8ca6a366 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -764,9 +764,14 @@ fp_enroll_check_duplicate_cb (FpiDeviceGoodixMoc *self, } if (resp->check_duplicate_resp.duplicate) { + g_autoptr(FpPrint) print = NULL; + + print = g_object_ref_sink (fp_print_from_template (self, &resp->check_duplicate_resp.template)); + fpi_ssm_mark_failed (self->task_ssm, fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_DUPLICATE, - "Finger has already enrolled")); + "Finger was already enrolled as '%s'", + fp_print_get_description (print))); return; } From 9ce6ed4164399de36695349f95602e3bece33b7e Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 3 Feb 2022 14:46:40 +0100 Subject: [PATCH 14/19] goodixmoc: Report recognized print after a match failure The API should return the recognized print, even if none of the prints given in the gallery (or the one passed to verify) matched. Without this the garbage-collection of left-over prints does not work, causing issues after reinstall. Fixes: #444 --- libfprint/drivers/goodixmoc/goodix.c | 49 ++++++++++------------------ 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 8ca6a366..43f63259 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -420,12 +420,9 @@ fp_verify_cb (FpiDeviceGoodixMoc *self, gxfp_cmd_response_t *resp, GError *error) { - g_autoptr(GPtrArray) templates = NULL; FpDevice *device = FP_DEVICE (self); - FpPrint *match = NULL; - FpPrint *print = NULL; - gint cnt = 0; - gboolean find = false; + FpPrint *new_scan = NULL; + FpPrint *matching = NULL; if (error) { @@ -434,46 +431,34 @@ fp_verify_cb (FpiDeviceGoodixMoc *self, } if (resp->verify.match) { - match = fp_print_from_template (self, &resp->verify.template); + new_scan = fp_print_from_template (self, &resp->verify.template); if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) { - templates = g_ptr_array_sized_new (1); - fpi_device_get_verify_data (device, &print); - g_ptr_array_add (templates, print); + fpi_device_get_verify_data (device, &matching); + if (!fp_print_equal (matching, new_scan)) + matching = NULL; } else { + GPtrArray *templates = NULL; fpi_device_get_identify_data (device, &templates); - g_ptr_array_ref (templates); - } - for (cnt = 0; cnt < templates->len; cnt++) - { - print = g_ptr_array_index (templates, cnt); - if (fp_print_equal (print, match)) + for (gint i = 0; i < templates->len; i++) { - find = true; - break; + if (fp_print_equal (g_ptr_array_index (templates, i), new_scan)) + { + matching = g_ptr_array_index (templates, i); + break; + } } - - } - if (find) - { - if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) - fpi_device_verify_report (device, FPI_MATCH_SUCCESS, match, error); - else - fpi_device_identify_report (device, print, match, error); } } - if (!find) - { - if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) - fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, error); - else - fpi_device_identify_report (device, NULL, NULL, error); - } + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_VERIFY) + fpi_device_verify_report (device, matching ? FPI_MATCH_SUCCESS : FPI_MATCH_FAIL, new_scan, error); + else + fpi_device_identify_report (device, matching, new_scan, error); fpi_ssm_next_state (self->task_ssm); From 356805168637666c758fbc2a2f436d6418c7e5f4 Mon Sep 17 00:00:00 2001 From: Devyn Cairns Date: Fri, 5 Nov 2021 07:27:56 -0700 Subject: [PATCH 15/19] goodixmoc: support for clear_storage The internal storage of this device can get messed up by other operating systems, so it's handy to be able to clear it. I'm not 100% sure whether the commands I've sent to the device are exactly what is supposed to be used (just a guess), but it did seem to work, and it even fixed another issue I had. --- libfprint/drivers/goodixmoc/goodix.c | 40 ++++++++++++++++++++++++++++ tests/goodixmoc/custom.py | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/goodixmoc/goodix.c b/libfprint/drivers/goodixmoc/goodix.c index 43f63259..4eeb7215 100644 --- a/libfprint/drivers/goodixmoc/goodix.c +++ b/libfprint/drivers/goodixmoc/goodix.c @@ -1160,6 +1160,32 @@ fp_template_delete_cb (FpiDeviceGoodixMoc *self, fp_info ("Successfully deleted enrolled user"); fpi_device_delete_complete (device, NULL); } + +static void +fp_template_delete_all_cb (FpiDeviceGoodixMoc *self, + gxfp_cmd_response_t *resp, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + if (error) + { + fpi_device_clear_storage_complete (device, error); + return; + } + if ((resp->result >= GX_FAILED) && (resp->result != GX_ERROR_FINGER_ID_NOEXIST)) + { + fpi_device_clear_storage_complete (FP_DEVICE (self), + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Failed clear storage, result: 0x%x", + resp->result)); + return; + } + + fp_info ("Successfully cleared storage"); + fpi_device_clear_storage_complete (device, NULL); +} + /****************************************************************************** * * fp_template_list Function @@ -1483,6 +1509,19 @@ gx_fp_template_delete (FpDevice *device) } +static void +gx_fp_template_delete_all (FpDevice *device) +{ + FpiDeviceGoodixMoc *self = FPI_DEVICE_GOODIXMOC (device); + + goodix_sensor_cmd (self, MOC_CMD0_DELETETEMPLATE, MOC_CMD1_DELETE_ALL, + false, + NULL, + 0, + fp_template_delete_all_cb); + +} + static void fpi_device_goodixmoc_init (FpiDeviceGoodixMoc *self) { @@ -1526,6 +1565,7 @@ fpi_device_goodixmoc_class_init (FpiDeviceGoodixMocClass *klass) dev_class->probe = gx_fp_probe; dev_class->enroll = gx_fp_enroll; dev_class->delete = gx_fp_template_delete; + dev_class->clear_storage = gx_fp_template_delete_all; dev_class->list = gx_fp_template_list; dev_class->verify = gx_fp_verify_identify; dev_class->identify = gx_fp_verify_identify; diff --git a/tests/goodixmoc/custom.py b/tests/goodixmoc/custom.py index 2fe6edd9..1fb513a1 100755 --- a/tests/goodixmoc/custom.py +++ b/tests/goodixmoc/custom.py @@ -21,7 +21,7 @@ assert d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) assert d.has_feature(FPrint.DeviceFeature.STORAGE) assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) -assert not d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) +assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) d.open_sync() From d3014f1684eb9f8124dc0e1d22b7f6f7c8a8462d Mon Sep 17 00:00:00 2001 From: Doomsdayrs Date: Fri, 24 Dec 2021 18:31:03 +0000 Subject: [PATCH 16/19] Delete TODO --- TODO | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index 5d1034d2..00000000 --- a/TODO +++ /dev/null @@ -1,30 +0,0 @@ -LIBRARY -======= -test suite against NFIQ compliance set -make library optionally asynchronous and maybe thread-safe -nbis cleanups -API function to determine if img device supports uncond. capture -race-free way of saying "save this print but don't overwrite" - -NEW DRIVERS -=========== -Sunplus 895 driver -AES3400/3500 driver -ID Mouse driver -Support for 2nd generation MS devices -Support for 2nd generation UPEK devices - -IMAGING -======= -ignore first frame or two with aes2501 -aes2501: increase threshold "sum" for end-of-image detection -aes2501 gain calibration -aes4000 gain calibration -aes4000 resampling -PPMM parameter to get_minutiae seems to have no effect -nbis minutiae should be stored in endian-independent format - -PORTABILITY -=========== -OpenBSD can't do -Wshadow or visibility -OpenBSD: add compat codes for ENOTSUP ENODATA and EPROTO From 754ccfb8651fcd8453a39c1236473e88fe4e254b Mon Sep 17 00:00:00 2001 From: doomsdayrs Date: Thu, 23 Dec 2021 23:12:54 -0500 Subject: [PATCH 17/19] Convert README to markdown Just a minor change, but makes the file a bit more readable. --- README => README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename README => README.md (98%) diff --git a/README b/README.md similarity index 98% rename from README rename to README.md index fbf76705..7f59b4e8 100644 --- a/README +++ b/README.md @@ -1,9 +1,10 @@ -libfprint -========= +# libfprint libfprint is part of the fprint project: https://fprint.freedesktop.org/ +## History + libfprint was originally developed as part of an academic project at the University of Manchester with the aim of hiding differences between different consumer fingerprint scanners and providing a single uniform API to application @@ -15,6 +16,8 @@ from this one, although I try to keep them as similar as possible (I'm not hiding anything in the academic branch, it's just the open source release contains some commits excluded from the academic project). +## License + THE UNIVERSITY OF MANCHESTER DOES NOT ENDORSE THIS THIS SOFTWARE RELEASE AND IS IN NO WAY RESPONSIBLE FOR THE CODE CONTAINED WITHIN, OR ANY DAMAGES CAUSED BY USING OR DISTRIBUTING THE SOFTWARE. Development does not happen on From 6f5ba3cbb5925691b09dbc5cd9852cb95f2def37 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 11 Feb 2022 19:34:59 +0100 Subject: [PATCH 18/19] udev-hwdb: Update unsupported device list --- data/autosuspend.hwdb | 9 ++++++++- libfprint/fprint-list-udev-hwdb.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 8bbc52c8..91a14ebd 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -267,16 +267,19 @@ usb:v06CBp008A* usb:v06CBp009A* usb:v06CBp009B* usb:v06CBp00A2* +usb:v06CBp00A8* usb:v06CBp00B7* usb:v06CBp00BB* usb:v06CBp00BE* usb:v06CBp00C4* usb:v06CBp00CB* +usb:v06CBp00C9* usb:v06CBp00D8* usb:v06CBp00DA* +usb:v06CBp00DC* usb:v06CBp00E7* usb:v06CBp00E9* -usb:v06CBp00C9* +usb:v06CBp00FD* usb:v0A5Cp5801* usb:v0A5Cp5805* usb:v0A5Cp5834* @@ -288,6 +291,7 @@ usb:v0A5Cp5844* usb:v0A5Cp5845* usb:v0BDAp5812* usb:v10A5p0007* +usb:v10A5p9200* usb:v1188p9545* usb:v138Ap0007* usb:v138Ap003A* @@ -305,6 +309,7 @@ usb:v1491p0088* usb:v16D1p1027* usb:v1C7Ap0300* usb:v1C7Ap0575* +usb:v1C7Ap0576* usb:v27C6p5042* usb:v27C6p5110* usb:v27C6p5117* @@ -324,7 +329,9 @@ usb:v27C6p55A2* usb:v27C6p55A4* usb:v27C6p55B4* usb:v27C6p5740* +usb:v27C6p5E0A* usb:v2808p9338* +usb:v298Dp2020* usb:v298Dp2033* usb:v3538p0930* ID_AUTOSUSPEND=1 diff --git a/libfprint/fprint-list-udev-hwdb.c b/libfprint/fprint-list-udev-hwdb.c index 19b49e83..ad9cdd07 100644 --- a/libfprint/fprint-list-udev-hwdb.c +++ b/libfprint/fprint-list-udev-hwdb.c @@ -42,16 +42,19 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x06cb, .pid = 0x009a }, { .vid = 0x06cb, .pid = 0x009b }, { .vid = 0x06cb, .pid = 0x00a2 }, + { .vid = 0x06cb, .pid = 0x00a8 }, { .vid = 0x06cb, .pid = 0x00b7 }, { .vid = 0x06cb, .pid = 0x00bb }, { .vid = 0x06cb, .pid = 0x00be }, { .vid = 0x06cb, .pid = 0x00c4 }, { .vid = 0x06cb, .pid = 0x00cb }, + { .vid = 0x06cb, .pid = 0x00c9 }, { .vid = 0x06cb, .pid = 0x00d8 }, { .vid = 0x06cb, .pid = 0x00da }, + { .vid = 0x06cb, .pid = 0x00dc }, { .vid = 0x06cb, .pid = 0x00e7 }, { .vid = 0x06cb, .pid = 0x00e9 }, - { .vid = 0x06cb, .pid = 0x00c9 }, + { .vid = 0x06cb, .pid = 0x00fd }, { .vid = 0x0a5c, .pid = 0x5801 }, { .vid = 0x0a5c, .pid = 0x5805 }, { .vid = 0x0a5c, .pid = 0x5834 }, @@ -63,6 +66,7 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x0a5c, .pid = 0x5845 }, { .vid = 0x0bda, .pid = 0x5812 }, { .vid = 0x10a5, .pid = 0x0007 }, + { .vid = 0x10a5, .pid = 0x9200 }, { .vid = 0x1188, .pid = 0x9545 }, { .vid = 0x138a, .pid = 0x0007 }, { .vid = 0x138a, .pid = 0x003a }, @@ -80,6 +84,7 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x16d1, .pid = 0x1027 }, { .vid = 0x1c7a, .pid = 0x0300 }, { .vid = 0x1c7a, .pid = 0x0575 }, + { .vid = 0x1c7a, .pid = 0x0576 }, { .vid = 0x27c6, .pid = 0x5042 }, { .vid = 0x27c6, .pid = 0x5110 }, { .vid = 0x27c6, .pid = 0x5117 }, @@ -99,7 +104,9 @@ static const FpIdEntry whitelist_id_table[] = { { .vid = 0x27c6, .pid = 0x55a4 }, { .vid = 0x27c6, .pid = 0x55b4 }, { .vid = 0x27c6, .pid = 0x5740 }, + { .vid = 0x27c6, .pid = 0x5e0a }, { .vid = 0x2808, .pid = 0x9338 }, + { .vid = 0x298d, .pid = 0x2020 }, { .vid = 0x298d, .pid = 0x2033 }, { .vid = 0x3538, .pid = 0x0930 }, { .vid = 0 }, From 8fad2652ee75836e5a60c73af0f06cc8b57a07f5 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 11 Feb 2022 18:35:15 +0100 Subject: [PATCH 19/19] Release 1.94.3 --- NEWS | 10 ++++++++++ meson.build | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index eacd99c9..2e0a2c6a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ This file lists notable changes in each release. For the full history of all changes, see ChangeLog. +2021-11-02: v1.94.3 release + +Highlights: + * Ensure idle mainloop before completing enumeration (fprintd#119) + * It is now possible to extend already enrolled prints + * elanspi: Fix timeout error with some hardware (#438) + * elanspi: Fix cancellation issues + * goodixmoc: Return matching device print; fixes duplicate checking (#444) + * goodixmoc: Support clearing the storage (usually unused) + 2021-11-02: v1.94.2 release Highlights: diff --git a/meson.build b/meson.build index f2750519..c937d281 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('libfprint', [ 'c', 'cpp' ], - version: '1.94.2', + version: '1.94.3', license: 'LGPLv2.1+', default_options: [ 'buildtype=debugoptimized',