Compare commits

...

27 Commits

Author SHA1 Message Date
Matthew Mirvish
b50cba1fa4 fixup! elanspi: preliminary support for 04f3:3104 2022-05-23 13:16:21 -04:00
Matthew Mirvish
46591d7bfa elanspi: preliminary support for 04f3:3104
Also adds "quirk" flags to driver_data so other special case things
can be handled with less effort going forwards.
2022-05-03 21:02:26 -04:00
Josh Chen
eda8d13927 elan: add PID 0x0c4b 2022-04-14 17:21:52 +08:00
Aris Lin
5ba7ff8be9 synaptics: Add new PID 0x0168 2022-04-11 12:01:17 +00:00
Benjamin Berg
da1a56a600 context: Log version number at startup
Having this should at least give us a slightly better idea about the
version that the user has installed. Obviously it is still not very
accurate (maybe a git hash would be good if available?), but it should
still be helpful overall.
2022-04-10 13:58:58 +02:00
ArronYen
2b760dfa38 elanmoc: add PID 0x0c82 2022-03-02 10:43:20 +08:00
Benjamin Berg
f1a61c060f device: Clear the critical section source on destruction 2022-02-17 10:20:55 +01:00
Benjamin Berg
5fb3b8b43a tests: Avoid -Wdangling-pointer warning
The code is correct, but gcc thinks the pointer is still NULL after the
call. As obvious workaround don't seem to work, just disable the warning
for now.
2022-02-14 17:57:59 +01:00
Benjamin Berg
8fad2652ee Release 1.94.3 2022-02-11 19:36:43 +01:00
Benjamin Berg
6f5ba3cbb5 udev-hwdb: Update unsupported device list 2022-02-11 19:36:22 +01:00
doomsdayrs
754ccfb865 Convert README to markdown
Just a minor change, but makes the file a bit more readable.
2022-02-11 19:36:22 +01:00
Doomsdayrs
d3014f1684 Delete TODO 2022-02-11 19:36:22 +01:00
Devyn Cairns
3568051686 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.
2022-02-11 18:08:53 +00:00
Benjamin Berg
9ce6ed4164 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
2022-02-03 14:49:49 +01:00
Benjamin Berg
e0fd178bec goodixmoc: Log which the ID that produced the duplicate 2022-02-03 14:49:49 +01:00
Benjamin Berg
168ab98021 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.
2022-02-03 14:49:49 +01:00
Benjamin Berg
ae5696a9bb goodixmoc: Change error message for corrupted headers
Otherwise you can't tell from the log whether parsing the body or header
failed.
2022-02-03 14:49:49 +01:00
Benjamin Berg
038c7108a6 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
2022-02-03 14:49:44 +01:00
Aris Lin
eb1013cdb6 synaptics: Remove PID 0xC9 2022-01-28 19:25:24 +08:00
mincrmatt12
5beac0ded7 elanspi: Try to avoid cancellation problems 2021-12-23 05:35:38 +00:00
mincrmatt12
7565562903 elanspi: Adjust register tables (fixes #438)
New values taken from a newer version of the official driver.
2021-12-22 00:20:57 -05:00
Dmitrii Shcherbakov
999bca076c 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.
2021-12-16 12:53:41 +01:00
Benjamin Berg
e198b04222 elanspi: Silence some SSMs that may log excessively otherwise 2021-12-02 13:54:59 +01:00
Benjamin Berg
3981c42d3e ssm: Add API to silence most state change debug messages
Otherwise tightly looping SSMs (primarily SPI transfers), will flood the
logs in inappropriate ways.
2021-12-02 13:54:59 +01:00
Benjamin Berg
31afd3ba5c 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.
2021-12-02 13:53:54 +01:00
Benjamin Berg
05fd2c58cb 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
2021-12-01 15:29:18 +00:00
Matthew Mirvish
a033154b2e doc: Fix broken documentation for FpiDeviceUdevSubtypeFlags enum
Added description and fixed incorrect name in comment, so now gtkdoc
actually shows useful information.
2021-11-15 17:10:44 -05:00
32 changed files with 742 additions and 187 deletions

10
NEWS
View File

@@ -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:

View File

@@ -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

30
TODO
View File

@@ -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

View File

@@ -133,6 +133,7 @@ usb:v04F3p0C32*
usb:v04F3p0C33*
usb:v04F3p0C3D*
usb:v04F3p0C42*
usb:v04F3p0C4B*
usb:v04F3p0C4D*
usb:v04F3p0C4F*
usb:v04F3p0C63*
@@ -144,6 +145,7 @@ usb:v04F3p0C58*
# Supported by libfprint driver elanmoc
usb:v04F3p0C7D*
usb:v04F3p0C7E*
usb:v04F3p0C82*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -181,13 +183,13 @@ usb:v06CBp00DF*
usb:v06CBp00F9*
usb:v06CBp00FC*
usb:v06CBp00C2*
usb:v06CBp00C9*
usb:v06CBp0100*
usb:v06CBp00F0*
usb:v06CBp0103*
usb:v06CBp0123*
usb:v06CBp0126*
usb:v06CBp0129*
usb:v06CBp0168*
ID_AUTOSUSPEND=1
ID_PERSIST=0
@@ -257,7 +259,6 @@ usb:v138Ap0091*
# Known unsupported devices
usb:v04F3p036B*
usb:v04F3p0C00*
usb:v04F3p0C4B*
usb:v04F3p0C4C*
usb:v04F3p0C57*
usb:v04F3p0C5E*
@@ -268,15 +269,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:v06CBp00FD*
usb:v0A5Cp5801*
usb:v0A5Cp5805*
usb:v0A5Cp5834*
@@ -288,6 +293,7 @@ usb:v0A5Cp5844*
usb:v0A5Cp5845*
usb:v0BDAp5812*
usb:v10A5p0007*
usb:v10A5p9200*
usb:v1188p9545*
usb:v138Ap0007*
usb:v138Ap003A*
@@ -305,6 +311,7 @@ usb:v1491p0088*
usb:v16D1p1027*
usb:v1C7Ap0300*
usb:v1C7Ap0575*
usb:v1C7Ap0576*
usb:v27C6p5042*
usb:v27C6p5110*
usb:v27C6p5117*
@@ -324,7 +331,9 @@ usb:v27C6p55A2*
usb:v27C6p55A4*
usb:v27C6p55B4*
usb:v27C6p5740*
usb:v27C6p5E0A*
usb:v2808p9338*
usb:v298Dp2020*
usb:v298Dp2033*
usb:v3538p0930*
ID_AUTOSUSPEND=1

View File

@@ -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

View File

@@ -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 ();

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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] = "<unknown>";
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),

View File

@@ -215,6 +215,7 @@ static const FpIdEntry elan_id_table[] = {
{.vid = ELAN_VEND_ID, .pid = 0x0c33, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c3d, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c42, .driver_data = ELAN_0C42},
{.vid = ELAN_VEND_ID, .pid = 0x0c4b, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c4d, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c4f, .driver_data = ELAN_ALL_DEV},
{.vid = ELAN_VEND_ID, .pid = 0x0c63, .driver_data = ELAN_ALL_DEV},

View File

@@ -27,6 +27,7 @@ G_DEFINE_TYPE (FpiDeviceElanmoc, fpi_device_elanmoc, FP_TYPE_DEVICE)
static const FpIdEntry id_table[] = {
{ .vid = 0x04f3, .pid = 0x0c7d, },
{ .vid = 0x04f3, .pid = 0x0c7e, },
{ .vid = 0x04f3, .pid = 0x0c82, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};

View File

@@ -63,6 +63,7 @@ struct _FpiDeviceElanSpi
guint16 gdac_step;
guint16 best_gdac;
guint16 best_meandiff;
guint16 target_mean;
} hv_data;
};
@@ -408,6 +409,10 @@ elanspi_determine_sensor (FpiDeviceElanSpi *self, GError **err)
self->frame_width = self->sensor_width;
self->frame_height = self->sensor_height > ELANSPI_MAX_FRAME_HEIGHT ? ELANSPI_MAX_FRAME_HEIGHT : self->sensor_height;
}
/* check x571 flag */
if (self->sensor_id == 0xe)
self->hv_data.target_mean = (fpi_device_get_driver_data (FP_DEVICE (self)) & ELANSPI_QUIRK_X571 ? ELANSPI_HV_X571_CALIBRATION_TARGET_MEAN : ELANSPI_HV_CALIBRATION_TARGET_MEAN);
}
static void
@@ -439,6 +444,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))
{
@@ -536,7 +547,7 @@ elanspi_write_regtable (FpiDeviceElanSpi *self, const struct elanspi_regtable *
for (int i = 0; table->entries[i].table; i += 1)
{
if (table->entries[i].sid == self->sensor_id)
if (table->entries[i].sid == self->sensor_id && table->entries[i].quirk == (fpi_device_get_driver_data (FP_DEVICE (self)) & ELANSPI_QUIRK_MASK))
{
starting_entry = table->entries[i].table;
break;
@@ -606,6 +617,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;
@@ -685,6 +697,8 @@ static void
elanspi_capture_hv_image_handler (FpiSpiTransfer *transfer, FpDevice *dev, gpointer unused_data, GError *error)
{
FpiDeviceElanSpi *self = FPI_DEVICE_ELANSPI (dev);
ptrdiff_t outptr = 0, inptr = 0;
guint16 value = 0;
if (error)
{
@@ -692,24 +706,39 @@ elanspi_capture_hv_image_handler (FpiSpiTransfer *transfer, FpDevice *dev, gpoin
return;
}
int i, outptr;
guint16 value = 0;
for (i = 0, outptr = 0; i < transfer->length_rd && outptr < (self->sensor_height * self->sensor_width * 2); i += 1)
if (fpi_device_get_driver_data (dev) & ELANSPI_QUIRK_X571)
{
if (transfer->buffer_rd[i] != 0xff)
/* decode image in x571 mode (just copy and fix endian) */
for (int y = 0; y < self->sensor_height; y += 1)
{
if (outptr % 2)
inptr += 2; /* 2 dummy bytes per line at start */
for (int x = 0; x < self->sensor_width; x += 1)
{
value <<= 8;
value |= transfer->buffer_rd[i];
self->last_image[outptr / 2] = value;
self->last_image[outptr / 2] = transfer->buffer_rd[inptr] + transfer->buffer_rd[inptr + 1] * 0x100;
inptr += 2;
outptr += 2;
}
else
}
}
else
{
/* decode image (normal case; with weird 0xff checks) */
for (inptr = 0, outptr = 0; inptr < transfer->length_rd && outptr < (self->sensor_height * self->sensor_width * 2); inptr += 1)
{
if (transfer->buffer_rd[inptr] != 0xff)
{
value = transfer->buffer_rd[i];
if (outptr % 2)
{
value <<= 8;
value |= transfer->buffer_rd[inptr];
self->last_image[outptr / 2] = value;
}
else
{
value = transfer->buffer_rd[inptr];
}
outptr += 1;
}
outptr += 1;
}
}
@@ -767,12 +796,17 @@ elanspi_capture_hv_handler (FpiSsm *ssm, FpDevice *dev)
return;
}
/* otherwise, read the image
* the hv sensors seem to use 128 bytes of padding(?) this is only tested on the 0xe sensors */
* the hv sensors seem to use variable-length padding(?) this is only tested on the 0xe sensors
*
* in the x571 mode, the padding appears to be disabled and it just has 2 bytes of junk */
xfer = fpi_spi_transfer_new (dev, self->spi_fd);
xfer->ssm = ssm;
fpi_spi_transfer_write (xfer, 2);
xfer->buffer_wr[0] = 0x10; /* receieve line */
fpi_spi_transfer_read (xfer, self->sensor_height * (self->sensor_width * 2 + 48));
xfer->buffer_wr[0] = 0x10; /* receieve line/image */
if (fpi_device_get_driver_data (dev) & ELANSPI_QUIRK_X571)
fpi_spi_transfer_read (xfer, self->sensor_height * (self->sensor_width * 2 + 2) - 2);
else
fpi_spi_transfer_read (xfer, self->sensor_height * (self->sensor_width * 2 + 48));
fpi_spi_transfer_submit (xfer, fpi_device_get_cancellable (dev), elanspi_capture_hv_image_handler, NULL);
return;
}
@@ -860,12 +894,13 @@ 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;
case ELANSPI_CALIBHV_PROCESS:
/* compute mean */
mean_diff = abs (elanspi_mean_image (self, self->last_image) - ELANSPI_HV_CALIBRATION_TARGET_MEAN);
mean_diff = abs (elanspi_mean_image (self, self->last_image) - self->hv_data.target_mean);
if (mean_diff < 100)
{
fp_dbg ("<calibhv> calibration ok (mdiff < 100 w/ gdac=%04x)", self->hv_data.gdac_value);
@@ -888,7 +923,7 @@ elanspi_calibrate_hv_handler (FpiSsm *ssm, FpDevice *dev)
return;
}
/* update gdac */
if (elanspi_mean_image (self, self->last_image) < ELANSPI_HV_CALIBRATION_TARGET_MEAN)
if (elanspi_mean_image (self, self->last_image) < self->hv_data.target_mean)
self->hv_data.gdac_value -= self->hv_data.gdac_step;
else
self->hv_data.gdac_value += self->hv_data.gdac_step;
@@ -1115,6 +1150,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 +1159,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;
@@ -1219,8 +1256,6 @@ elanspi_guess_image (FpiDeviceElanSpi *self, guint16 *raw_image)
sq_stddev /= (frame_width * frame_height);
fp_dbg ("<guess> 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 +1266,8 @@ elanspi_guess_image (FpiDeviceElanSpi *self, guint16 *raw_image)
if (sq_stddev < ELANSPI_MAX_EMPTY_STDDEV)
is_empty += 1;
fp_dbg ("<guess> 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)
@@ -1482,11 +1519,12 @@ elanspi_fp_capture_ssm_handler (FpiSsm *ssm, FpDevice *dev)
if (self->deactivating)
{
fp_dbg ("<capture> 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;
}
@@ -1495,6 +1533,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;

View File

@@ -71,6 +71,7 @@ struct elanspi_regtable
struct
{
unsigned char sid;
unsigned quirk;
const struct elanspi_reg_entry *table;
} entries[];
};
@@ -97,7 +98,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},
@@ -142,11 +173,11 @@ static const struct elanspi_reg_entry elanspi_calibration_table_id0[] = {
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 = 0x0, .table = NULL }
{ .sid = 0x0, .quirk = 0, .table = elanspi_calibration_table_id0 },
{ .sid = 0x5, .quirk = 0, .table = elanspi_calibration_table_id57 },
{ .sid = 0x6, .quirk = 0, .table = elanspi_calibration_table_id6 },
{ .sid = 0x7, .quirk = 0, .table = elanspi_calibration_table_id57 },
{ .sid = 0x0, .quirk = 0, .table = NULL }
}
};
@@ -282,20 +313,133 @@ static const struct elanspi_reg_entry elanspi_calibration_table_page1_id14[] = {
{0xff, 0xff}
};
static const struct elanspi_regtable elanspi_calibration_table_new_page0 = {
.other = NULL,
.entries = {
{ .sid = 0xe, .table = elanspi_calibration_table_page0_id14 },
{ .sid = 0x0, .table = NULL }
}
static const struct elanspi_reg_entry elanspi_calibration_table_page0_id14_x571[] = {
{0x00, 0x5a},
{0x01, 0x00},
{0x02, 0x4f},
{0x03, 0x00},
{0x09, 0x04},
{0x04, 0x4f},
{0x05, 0xa0},
{0x06, 0x57},
{0x07, 0x02},
{0x08, 0x00},
{0x0a, 0x63},
{0x0b, 0x01},
{0x0c, 0x08},
{0x0d, 0x00},
{0x0e, 0x00},
{0x0f, 0x13},
{0x10, 0x38},
{0x11, 0x01},
{0x12, 0x04},
{0x13, 0x00},
{0x14, 0x00},
{0x15, 0x04},
{0x16, 0x02},
{0x17, 0x00},
{0x18, 0x01},
{0x19, 0xf4},
{0x1a, 0x00},
{0x1b, 0x00},
{0x1c, 0x00},
{0x1d, 0x00},
{0x1e, 0x00},
{0x1f, 0x00},
{0x20, 0x00},
{0x21, 0x00},
{0x22, 0x06},
{0x23, 0x00},
{0x24, 0x00},
{0x25, 0x00},
{0x26, 0x00},
{0x27, 0xff},
{0x28, 0x00},
{0x29, 0x04},
{0x2b, 0xe2},
{0x2e, 0xd0},
{0x2f, 0x40},
{0x30, 0x01},
{0x31, 0x38},
{0x32, 0x00},
{0x33, 0x00},
{0x34, 0x00},
{0x35, 0x1f},
{0x36, 0xff},
{0x37, 0x00},
{0x38, 0x00},
{0x39, 0x00},
{0x3a, 0x00},
{0x2a, 0x5f},
{0x2c, 0x10},
{0xff, 0xff}
};
static const struct elanspi_regtable elanspi_calibration_table_new_page1 = {
.other = NULL,
.entries = {
{ .sid = 0xe, .table = elanspi_calibration_table_page1_id14 },
{ .sid = 0x0, .table = NULL }
}
static const struct elanspi_reg_entry elanspi_calibration_table_page1_id14_x571[] = {
{0x00, 0xfb},
{0x01, 0xff},
{0x02, 0x7f},
{0x03, 0xd4},
{0x04, 0x7d},
{0x05, 0x19},
{0x06, 0x80},
{0x07, 0x40},
{0x08, 0x11},
{0x09, 0x00},
{0x0a, 0x00},
{0x0b, 0x00},
{0x0c, 0x00},
{0x0d, 0x00},
{0x0e, 0x32},
{0x0f, 0x00},
{0x10, 0x00},
{0x11, 0x32},
{0x12, 0x02},
{0x13, 0x08},
{0x14, 0x5c},
{0x15, 0x01},
{0x16, 0x15},
{0x17, 0x01},
{0x18, 0x14},
{0x19, 0x01},
{0x1a, 0x14},
{0x1b, 0x01},
{0x1c, 0x16},
{0x1d, 0x01},
{0x1e, 0x0b},
{0x1f, 0x01},
{0x20, 0x0b},
{0x21, 0x02},
{0x22, 0x08},
{0x23, 0x29},
{0x24, 0x00},
{0x25, 0x0c},
{0x26, 0x1b},
{0x27, 0x15},
{0x28, 0x1b},
{0x29, 0x15},
{0x2a, 0x00},
{0x2b, 0x00},
{0x2c, 0x01},
{0x2d, 0x16},
{0x2e, 0x01},
{0x2f, 0x16},
{0x30, 0x04},
{0x31, 0x44},
{0x32, 0x04},
{0x33, 0x44},
{0x34, 0x14},
{0x35, 0x00},
{0x36, 0x00},
{0x37, 0x00},
{0x38, 0x00},
{0x39, 0x03},
{0x3a, 0xfe},
{0x3b, 0x00},
{0x3c, 0x00},
{0x3d, 0x02},
{0x3e, 0x00},
{0x3f, 0x00}
};
#define ELANSPI_NO_ROTATE 0
@@ -305,10 +449,33 @@ static const struct elanspi_regtable elanspi_calibration_table_new_page1 = {
#define ELANSPI_HV_FLIPPED 1
#define ELANSPI_ROTATE_MASK 3
#define ELANSPI_QUIRK_X571 (1 << 2)
#define ELANSPI_QUIRK_MASK (ELANSPI_QUIRK_X571)
#define ELANSPI_UDEV_TYPES FPI_DEVICE_UDEV_SUBTYPE_SPIDEV | FPI_DEVICE_UDEV_SUBTYPE_HIDRAW
#define ELANSPI_TP_VID 0x04f3
// using checkargs ACPI:HIDPID
static const struct elanspi_regtable elanspi_calibration_table_new_page0 = {
.other = NULL,
.entries = {
{ .sid = 0xe, .quirk = 0, .table = elanspi_calibration_table_page0_id14 },
{ .sid = 0xe, .quirk = ELANSPI_QUIRK_X571, .table = elanspi_calibration_table_page0_id14_x571 },
{ .sid = 0x0, .table = NULL }
}
};
static const struct elanspi_regtable elanspi_calibration_table_new_page1 = {
.other = NULL,
.entries = {
{ .sid = 0xe, .quirk = 0, .table = elanspi_calibration_table_page1_id14 },
{ .sid = 0xe, .quirk = ELANSPI_QUIRK_X571, .table = elanspi_calibration_table_page1_id14_x571 },
{ .sid = 0x0, .table = NULL }
}
};
static const FpIdEntry elanspi_id_table[] = {
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3057}, .driver_data = ELANSPI_180_ROTATE},
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3087}, .driver_data = ELANSPI_180_ROTATE},
@@ -318,6 +485,7 @@ static const FpIdEntry elanspi_id_table[] = {
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x30b2}, .driver_data = ELANSPI_NO_ROTATE},
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN70A1", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x30b2}, .driver_data = ELANSPI_NO_ROTATE},
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x309f}, .driver_data = ELANSPI_180_ROTATE},
{.udev_types = ELANSPI_UDEV_TYPES, .spi_acpi_id = "ELAN7001", .hid_id = {.vid = ELANSPI_TP_VID, .pid = 0x3104}, .driver_data = ELANSPI_QUIRK_X571 | ELANSPI_90RIGHT_ROTATE},
{.udev_types = 0}
};
@@ -327,6 +495,7 @@ static const FpIdEntry elanspi_id_table[] = {
#define ELANSPI_MAX_OLD_STAGE2_CALBIRATION_MEAN 8000
#define ELANSPI_HV_CALIBRATION_TARGET_MEAN 3000
#define ELANSPI_HV_X571_CALIBRATION_TARGET_MEAN 7000
#define ELANSPI_MIN_EMPTY_INVALID_PERCENT 6
#define ELANSPI_MAX_REAL_INVALID_PERCENT 3

View File

@@ -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;
}
@@ -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);
@@ -764,9 +749,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;
}
@@ -1170,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
@@ -1493,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)
{
@@ -1536,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;

View File

@@ -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;

View File

@@ -36,13 +36,13 @@ 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, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0123, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0126, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0129, },
{ .vid = SYNAPTICS_VENDOR_ID, .pid = 0x0168, },
{ .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */
};

View File

@@ -361,6 +361,8 @@ fp_context_init (FpContext *self)
FpContextPrivate *priv = fp_context_get_instance_private (self);
guint i;
g_debug ("Initializing FpContext (libfprint version " LIBFPRINT_VERSION ")");
priv->drivers = fpi_get_driver_types ();
if (get_drivers_whitelist_env ())
@@ -426,6 +428,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 +567,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);
}
/**

View File

@@ -225,6 +225,7 @@ fp_device_finalize (GObject *object)
g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
g_clear_pointer (&priv->current_task_idle_return_source, g_source_destroy);
g_clear_pointer (&priv->critical_section_flush_source, g_source_destroy);
g_clear_pointer (&priv->device_id, g_free);
g_clear_pointer (&priv->device_name, g_free);
@@ -1189,10 +1190,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 +1231,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;

View File

@@ -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;
/**

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -31,7 +31,6 @@ static const FpIdEntry whitelist_id_table[] = {
*/
{ .vid = 0x04f3, .pid = 0x036b },
{ .vid = 0x04f3, .pid = 0x0c00 },
{ .vid = 0x04f3, .pid = 0x0c4b },
{ .vid = 0x04f3, .pid = 0x0c4c },
{ .vid = 0x04f3, .pid = 0x0c57 },
{ .vid = 0x04f3, .pid = 0x0c5e },
@@ -42,15 +41,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 = 0x00fd },
{ .vid = 0x0a5c, .pid = 0x5801 },
{ .vid = 0x0a5c, .pid = 0x5805 },
{ .vid = 0x0a5c, .pid = 0x5834 },
@@ -62,6 +65,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 },
@@ -79,6 +83,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 },
@@ -98,7 +103,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 },

View File

@@ -1,5 +1,5 @@
project('libfprint', [ 'c', 'cpp' ],
version: '1.94.2',
version: '1.94.3',
license: 'LGPLv2.1+',
default_options: [
'buildtype=debugoptimized',
@@ -11,6 +11,7 @@ project('libfprint', [ 'c', 'cpp' ],
gnome = import('gnome')
libfprint_conf = configuration_data()
libfprint_conf.set_quoted('LIBFPRINT_VERSION', meson.project_version())
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')

View File

@@ -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

View File

@@ -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()

View File

@@ -27,6 +27,11 @@
#include "fpi-compat.h"
#include "fpi-log.h"
#include "test-device-fake.h"
#include "fp-print-private.h"
/* gcc 12.0.1 is complaining about dangling pointers in the auto_close* functions */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-pointer"
/* Utility functions */
@@ -64,6 +69,8 @@ auto_close_fake_device_free (FpAutoCloseDevice *device)
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpAutoCloseDevice, auto_close_fake_device_free)
#pragma GCC diagnostic pop
typedef FpDeviceClass FpAutoResetClass;
static FpAutoResetClass default_fake_dev_class = {0};
@@ -142,6 +149,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 +1062,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 +1069,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 +3427,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);

View File

@@ -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