diff --git a/doc/advanced-topics.xml b/doc/advanced-topics.xml
index 5af6f71..18a1289 100644
--- a/doc/advanced-topics.xml
+++ b/doc/advanced-topics.xml
@@ -60,21 +60,18 @@
In summary, libfprint represents fingerprints in several internal structures
and each representation will offer you a way of determining the
- driver ID and devtype of the print in
+ driver and device ID of the print in
question. Prints are only compatible if the driver ID and devtypes
match. libfprint does offer you some "is this print compatible?" helper
functions, so you don't have to worry about these details too much.
-
- Driver IDs
+
+ Driver
- Each driver is assigned a unique ID by the project maintainer. These
- assignments are
-
- documented in the sources and will never change.
+ Each driver is assigned a unique string identifier by the project maintainer.
@@ -89,22 +86,23 @@
-
- Device types
+
+ Device ID
- Internally, the driver behind a device assigns a 32-bit
- devtype identifier to the device. This cannot be used as a unique
- ID for a specific device as many devices under the same range may share
- the same devtype. The devtype may even be 0 in all cases.
+ Internally, the behind a device assigns a string identifier to the device
+ This cannot be used as a unique ID for a specific device as many devices
+ under the same range may share the same devtype. The device ID may even
+ be the same string in all cases. It is guaranteed to have a non-zero length
+ and be a valid file name. It defaults to "0".
- The only reason you may be interested in retrieving the devtype for a
+ The only reason you may be interested in retrieving the device ID for a
device is for the purpose of checking if some print data is compatible
- with a device. libfprint uses the devtype as one way of checking that the
+ with a device. libfprint uses the device ID as one way of checking that the
print you are verifying is compatible with the device in question - the
- devtypes must be equal. This effectively allows drivers to support more
+ device ID must be equal. This effectively allows drivers to support more
than one type of device where the data from each one is not compatible with
the other. Note that libfprint does provide you with helper functions to
determine whether a print is compatible with a device, so under most
diff --git a/doc/getting-started.xml b/doc/getting-started.xml
index 8ed8592..369e563 100644
--- a/doc/getting-started.xml
+++ b/doc/getting-started.xml
@@ -13,12 +13,12 @@
Usually the first thing you want to do is determine which fingerprint
- devices are present. This is done through device discovery.
+ devices are present. This is done using the FpContext object.
Once you have found a device you would like to operate, you should open it.
- Refer to device operations. This section also details enrollment,
+ Refer to device operations. This section also details enrollment,
image capture, and verification.
diff --git a/doc/libfprint-docs.xml b/doc/libfprint-docs.xml
index 52041ad..b866aab 100644
--- a/doc/libfprint-docs.xml
+++ b/doc/libfprint-docs.xml
@@ -25,39 +25,43 @@
Library API Documentation
-
-
-
-
-
-
-
+
+
+
+
+
Writing Drivers
-
- Logging, and async machinery
-
-
-
+
+ Device methods for drivers
+
+
-
- Device and driver structures
-
-
-
-
+
+ USB and State Machine helpers
+
+
+
Image manipulation
-
+
-
+
+ Print handling
+
+
+
+
+ Listing drivers
+
+
diff --git a/doc/libfprint-sections.txt b/doc/libfprint-sections.txt
index cc9b3ae..814ab80 100644
--- a/doc/libfprint-sections.txt
+++ b/doc/libfprint-sections.txt
@@ -1,125 +1,191 @@
-
-fprint.h
-events
-Initialisation and events handling
-LIBFPRINT_DEPRECATED
-fp_init
-fp_exit
-fp_pollfd
-fp_handle_events_timeout
-fp_handle_events
-fp_get_next_timeout
-fp_get_pollfds
-fp_pollfd_added_cb
-fp_pollfd_removed_cb
-fp_set_pollfd_notifiers
+drivers_api
+
-fprint.h
-discovery
-Device discovery
-fp_dscv_dev
-fp_discover_devs
-fp_dscv_devs_free
-fp_dscv_dev_get_driver
-fp_dscv_dev_get_devtype
-fp_dscv_dev_get_driver_id
-fp_dscv_dev_supports_print_data
+fp-context
+FpContext
+FP_TYPE_CONTEXT
+FpContextClass
+fp_context_new
+fp_context_enumerate
+fp_context_get_devices
+FpContext
-fprint.h
-drv
-fp_driver
-fp_driver_get_name
-fp_driver_get_full_name
-fp_driver_get_driver_id
-fp_driver_get_scan_type
-fp_driver_supports_imaging
+fp-device
+FP_TYPE_DEVICE
+FP_DEVICE_RETRY
+FP_DEVICE_ERROR
+FpDeviceType
+FpScanType
+FpDeviceRetry
+FpDeviceError
+fp_device_retry_quark
+fp_device_error_quark
+FpEnrollProgress
+fp_device_get_driver
+fp_device_get_device_id
+fp_device_get_name
+fp_device_get_scan_type
+fp_device_get_nr_enroll_stages
+fp_device_has_storage
+fp_device_supports_identify
+fp_device_supports_capture
+fp_device_open
+fp_device_close
+fp_device_enroll
+fp_device_verify
+fp_device_identify
+fp_device_capture
+fp_device_delete_print
+fp_device_list_prints
+fp_device_open_finish
+fp_device_close_finish
+fp_device_enroll_finish
+fp_device_verify_finish
+fp_device_identify_finish
+fp_device_capture_finish
+fp_device_delete_print_finish
+fp_device_list_prints_finish
+fp_device_open_sync
+fp_device_close_sync
+fp_device_enroll_sync
+fp_device_verify_sync
+fp_device_identify_sync
+fp_device_capture_sync
+fp_device_delete_print_sync
+fp_device_list_prints_sync
+FpDevice
-fprint.h
-dev
-fp_dev
-fp_scan_type
-fp_capture_result
-fp_enroll_result
-fp_verify_result
-
-fp_dev_get_driver
-fp_dev_get_nr_enroll_stages
-fp_dev_get_devtype
-fp_dev_supports_print_data
-fp_dev_supports_imaging
-fp_dev_supports_identification
-fp_dev_get_img_width
-fp_dev_get_img_height
-
-fp_operation_stop_cb
-fp_img_operation_cb
-fp_dev_open_cb
-fp_enroll_stage_cb
-fp_identify_cb
-
-fp_dev_open
-fp_async_dev_open
-
-fp_dev_close
-fp_async_dev_close
-
-fp_enroll_finger
-fp_enroll_finger_img
-fp_async_enroll_start
-fp_async_enroll_stop
-
-fp_verify_finger
-fp_verify_finger_img
-fp_async_verify_start
-fp_async_verify_stop
-
-fp_identify_finger
-fp_identify_finger_img
-fp_async_identify_start
-fp_async_identify_stop
-
-fp_dev_img_capture
-fp_async_capture_start
-fp_async_capture_stop
-
-
-
-fprint.h
-print_data
-fp_finger
-fp_print_data
-fp_print_data_get_data
-fp_print_data_from_data
-fp_print_data_free
-fp_print_data_get_driver_id
-fp_print_data_get_devtype
-
-
-
-fprint.h
-img
-fp_img
-fp_minutia
-fp_img_free
-fp_img_get_height
-fp_img_get_width
-fp_img_get_data
-fp_img_save_to_file
-fp_img_standardize
-fp_img_binarize
-fp_img_get_minutiae
+fp-image
+FP_TYPE_IMAGE
+FpMinutia
+fp_image_new
+fp_image_get_width
+fp_image_get_height
+fp_image_get_ppmm
+fp_image_get_minutiae
+fp_image_detect_minutiae
+fp_image_detect_minutiae_finish
+fp_image_get_data
+fp_image_get_binarized
fp_minutia_get_coords
+FpImage
+
+
+
+fp-image-device
+FP_TYPE_IMAGE_DEVICE
+FpImageDevice
+
+
+
+fp-print
+FP_TYPE_PRINT
+FpFinger
+FpPrint
+fp_print_new
+fp_print_new_from_data
+fp_print_to_data
+fp_print_get_driver
+fp_print_get_device_id
+fp_print_get_device_stored
+fp_print_get_image
+fp_print_get_finger
+fp_print_get_username
+fp_print_get_description
+fp_print_get_enroll_date
+fp_print_set_finger
+fp_print_set_username
+fp_print_set_description
+fp_print_set_enroll_date
+fp_print_compatible
+fp_print_equal
+fp_print_serialize
+fp_print_deserialize
+
+
+
+fpi-assembling
+fpi_frame
+fpi_frame_asmbl_ctx
+fpi_do_movement_estimation
+fpi_assemble_frames
+fpi_line_asmbl_ctx
+fpi_assemble_lines
+
+
+
+fpi-context
+fpi_get_driver_types
+
+
+
+fpi-device
+FpDeviceClass
+FpTimeoutFunc
+FpDeviceAction
+FpIdEntry
+fpi_device_get_usb_device
+fpi_device_get_virtual_env
+fpi_device_get_current_action
+fpi_device_retry_new
+fpi_device_error_new
+fpi_device_retry_new_msg
+fpi_device_error_new_msg
+fpi_device_get_driver_data
+fpi_device_get_enroll_data
+fpi_device_get_capture_data
+fpi_device_get_verify_data
+fpi_device_get_identify_data
+fpi_device_get_delete_data
+fpi_device_get_cancellable
+fpi_device_action_is_cancelled
+fpi_device_add_timeout
+fpi_device_set_nr_enroll_stages
+fpi_device_set_scan_type
+fpi_device_action_error
+fpi_device_probe_complete
+fpi_device_open_complete
+fpi_device_close_complete
+fpi_device_enroll_complete
+fpi_device_verify_complete
+fpi_device_identify_complete
+fpi_device_capture_complete
+fpi_device_delete_complete
+fpi_device_enroll_progress
+
+
+
+fpi-image
+FpiImageFlags
+FpImage
+fpi_std_sq_dev
+fpi_mean_sq_diff_norm
+fpi_image_resize
+
+
+
+fpi-image-device
+FpImageDevice
+FpImageDeviceState
+FpImageDeviceClass
+fpi_image_device_session_error
+fpi_image_device_open_complete
+fpi_image_device_close_complete
+fpi_image_device_activate_complete
+fpi_image_device_deactivate_complete
+fpi_image_device_report_finger_status
+fpi_image_device_image_captured
+fpi_image_device_retry_scan
-fpi-log.h
fpi-log
fp_dbg
fp_info
@@ -130,121 +196,57 @@ BUG
-fpi-ssm.h
-fpi-ssm
-fpi_ssm
-ssm_completed_fn
-ssm_handler_fn
+fpi-print
+FpPrintType
+FpiMatchResult
+fpi_print_add_print
+fpi_print_set_type
+fpi_print_set_device_stored
+fpi_print_add_from_image
+fpi_print_bz3_match
+
+
+fpi-ssm
+FpiSsmCompletedCallback
+FpiSsmHandlerCallback
fpi_ssm_new
fpi_ssm_free
fpi_ssm_start
fpi_ssm_start_subsm
-
fpi_ssm_next_state
-fpi_ssm_next_state_timeout_cb
fpi_ssm_jump_to_state
fpi_ssm_mark_completed
fpi_ssm_mark_failed
fpi_ssm_get_user_data
fpi_ssm_get_error
+fpi_ssm_dup_error
fpi_ssm_get_cur_state
+fpi_ssm_next_state_timeout_cb
+fpi_ssm_usb_transfer_cb
+FpiSsm
-fpi-poll.h
-fpi-poll
-fpi_timeout
-fpi_timeout_fn
-fpi_timeout_add
-fpi_timeout_set_name
-fpi_timeout_cancel
+fpi-usb-transfer
+FPI_USB_ENDPOINT_IN
+FPI_USB_ENDPOINT_OUT
+FpiUsbTransferCallback
+FpiTransferType
+FpiUsbTransfer
+fpi_usb_transfer_new
+fpi_usb_transfer_ref
+fpi_usb_transfer_unref
+fpi_usb_transfer_set_short_error
+fpi_usb_transfer_fill_bulk
+fpi_usb_transfer_fill_bulk_full
+fpi_usb_transfer_fill_control
+fpi_usb_transfer_fill_interrupt
+fpi_usb_transfer_fill_interrupt_full
+fpi_usb_transfer_submit
+fpi_usb_transfer_submit_sync
+
+FPI_TYPE_USB_TRANSFER
+fpi_usb_transfer_get_type
-
-fpi-dev.h
-fpi-dev
-fp_img_dev
-
-FP_DEV
-FP_IMG_DEV
-fp_dev_set_instance_data
-FP_INSTANCE_DATA
-
-fpi_dev_get_usb_dev
-fpi_dev_get_verify_data
-fpi_dev_set_nr_enroll_stages
-
-
-
-fpi-dev-img.h
-fpi-dev-img
-fp_imgdev_action
-fp_imgdev_state
-fp_imgdev_enroll_state
-
-fpi_imgdev_abort_scan
-fpi_imgdev_activate_complete
-fpi_imgdev_close_complete
-fpi_imgdev_deactivate_complete
-fpi_imgdev_get_action
-fpi_imgdev_get_action_result
-fpi_imgdev_get_action_state
-fpi_imgdev_image_captured
-fpi_imgdev_open_complete
-fpi_imgdev_report_finger_status
-fpi_imgdev_session_error
-fpi_imgdev_set_action_result
-
-
-
-fpi-core.h
-fpi-core
-usb_id
-fp_driver_type
-
-
-
-fpi-core.h
-fpi-core-img
-FpiImgDriverFlags
-fp_img_driver
-
-
-
-fpi-img.h
-fpi-img
-FpiImgFlags
-
-fpi_img_new
-fpi_img_new_for_imgdev
-fpi_img_realloc
-fpi_img_resize
-
-fpi_std_sq_dev
-fpi_mean_sq_diff_norm
-
-
-
-fpi-assembling.h
-fpi-assembling
-fpi_frame
-fpi_frame_asmbl_ctx
-fpi_line_asmbl_ctx
-
-fpi_do_movement_estimation
-fpi_assemble_frames
-fpi_assemble_lines
-
-
-
-fpi-usb.h
-fpi-usb
-fpi_usb_transfer
-
-fpi_usb_transfer_cb_fn
-fpi_usb_alloc
-fpi_usb_fill_bulk_transfer
-fpi_usb_submit_transfer
-fpi_usb_cancel_transfer
-
diff --git a/doc/libfprint-sections.txt-new-manual b/doc/libfprint-sections.txt-new-manual
new file mode 100644
index 0000000..857425b
--- /dev/null
+++ b/doc/libfprint-sections.txt-new-manual
@@ -0,0 +1,118 @@
+
+
+fprint.h
+context
+Device discovery and hotplugging
+FP_TYPE_CONTEXT
+FpContext
+fp_context_new
+fp_context_enumerate
+fp_context_get_devices
+
+
+
+
+fprint.h
+device
+Device
+FP_TYPE_DEVICE
+FpDevice
+FpDeviceType
+FpScanType
+FpDeviceRetry
+FpDeviceError
+FP_DEVICE_ERROR
+FP_DEVICE_RETRY
+fp_device_get_driver
+fp_device_get_device_id
+fp_device_get_name
+fp_device_get_scan_type
+
+fp_device_open
+fp_device_open_finish
+fp_device_open_sync
+
+fp_device_close
+fp_device_close_finish
+fp_device_close_sync
+
+fp_device_enroll
+fp_device_enroll_finish
+fp_device_enroll_sync
+
+fp_device_identify
+fp_device_identify_finish
+fp_device_identify_sync
+
+fp_device_capture
+fp_device_capture_finish
+fp_device_capture_sync
+
+fp_device_verify
+fp_device_verify_finish
+fp_device_verify_sync
+
+_fp_device_get_cancellable
+
+
+
+
+fprint.h
+print
+Fingerprint handling
+FpPrint
+fp_print_new
+
+
+
+
+fprint.h
+image
+Image handling
+FP_TYPE_IMAGE
+FpImage
+fp_image_new
+fp_image_detect_minutiae
+fp_image_detect_minutiae_finish
+fp_image_device_new
+fp_image_get_binarized
+fp_image_get_data
+fp_image_get_height
+fp_image_get_minutiae
+fp_image_get_ppmm
+fp_image_get_width
+
+
+
+
+internal-image-device
+drivers_api.h
+Base class for image devices
+FpImageDevice
+FpImageDeviceClass
+FpImageDeviceState
+
+
+
+internal-usb-transfers
+drivers_api.h
+USB Transfers
+FpUsbTransfer
+fp_usb_transfer_fill_bulk
+fp_usb_transfer_fill_bulk_full
+fp_usb_transfer_fill_control
+fp_usb_transfer_fill_interrupt
+fp_usb_transfer_fill_interrupt_full
+fp_usb_transfer_get_type
+fp_usb_transfer_new
+fp_usb_transfer_ref
+fp_usb_transfer_set_short_error
+fp_usb_transfer_submit
+fp_usb_transfer_submit_sync
+fp_usb_transfer_unref
+FpUsbTransferCallback
+FP_USB_ENDPOINT_IN
+FP_USB_ENDPOINT_OUT
+
+
+
diff --git a/doc/meson.build b/doc/meson.build
index 37d515d..5418667 100644
--- a/doc/meson.build
+++ b/doc/meson.build
@@ -2,41 +2,13 @@ subdir('xml')
private_headers = [
'config.h',
-
- 'aeslib.h',
- 'assembling.h',
- 'fp_internal.h',
'nbis-helpers.h',
- 'fpi-async.h',
- 'fpi-data.h',
+ 'fprint.h',
+ 'fp_internal.h',
- # Drivers
- 'aes1660.h',
- 'aes2501.h',
- 'aes2550.h',
- 'aes2660.h',
- 'aes3k.h',
- 'aesx660.h',
- 'driver_ids.h',
- 'elan.h',
- 'upek_proto.h',
- 'upeksonly.h',
- 'upektc.h',
- 'upektc_img.h',
- 'vfs0050.h',
- 'vfs301_proto_fragments.h',
- 'vfs301_proto.h',
- 'vfs5011_proto.h',
-
- # NBIS
- 'morph.h',
- 'sunrast.h',
- 'bozorth.h',
- 'defs.h',
- 'log.h',
- 'bz_array.h',
- 'lfs.h',
- 'mytime.h',
+ # Subdirectories to ignore
+ 'drivers',
+ 'nbis',
]
html_images = [
@@ -59,6 +31,7 @@ gnome.gtkdoc('libfprint',
content_files: content_files,
expand_content_files: expand_content_files,
scan_args: [
+ #'--rebuild-sections',
'--ignore-decorators=API_EXPORTED',
'--ignore-headers=' + ' '.join(private_headers),
],
diff --git a/libfprint/drivers_api.h b/libfprint/drivers_api.h
index 7867e34..bb401cd 100644
--- a/libfprint/drivers_api.h
+++ b/libfprint/drivers_api.h
@@ -23,17 +23,12 @@
#include
-#include "fprint.h"
+#include "fp_internal.h"
+
#include "fpi-log.h"
-#include "fpi-dev.h"
-#include "fpi-dev-img.h"
-#include "fpi-core.h"
+#include "fpi-usb-transfer.h"
#include "fpi-ssm.h"
-#include "fpi-poll.h"
-#include "fpi-dev.h"
-#include "fpi-usb.h"
-#include "fpi-img.h"
#include "fpi-assembling.h"
-#include "drivers/driver_ids.h"
+#include "fpi-image-device.h"
#endif
diff --git a/libfprint/fp-context.c b/libfprint/fp-context.c
new file mode 100644
index 0000000..74dda51
--- /dev/null
+++ b/libfprint/fp-context.c
@@ -0,0 +1,364 @@
+/*
+ * FpContext - A FPrint context
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define FP_COMPONENT "context"
+#include
+
+#include "fpi-context.h"
+#include "fpi-device.h"
+#include
+
+/**
+ * SECTION: fp-context
+ * @title: FpContext
+ * @short_description: Discover fingerprint devices
+ *
+ * The #FpContext allows you to discover fingerprint scanning hardware. This
+ * is the starting point when integrating libfprint into your software.
+ *
+ * The device-added and device-removed signals allow you to handle devices
+ * that may be hotplugged at runtime.
+ */
+
+typedef struct
+{
+ GUsbContext *usb_ctx;
+ GCancellable *cancellable;
+
+ gint pending_devices;
+ gboolean enumerated;
+
+ GArray *drivers;
+ GPtrArray *devices;
+} FpContextPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (FpContext, fp_context, G_TYPE_OBJECT)
+
+enum {
+ DEVICE_ADDED_SIGNAL,
+ DEVICE_REMOVED_SIGNAL,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+ FpDevice *device;
+ FpContext *context;
+ FpContextPrivate *priv;
+
+ device = (FpDevice *) g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
+ if (!device)
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+
+ context = FP_CONTEXT (user_data);
+ priv = fp_context_get_instance_private (context);
+ priv->pending_devices--;
+ g_message ("Ignoring device due to initialization error: %s", error->message);
+ return;
+ }
+
+ context = FP_CONTEXT (user_data);
+ priv = fp_context_get_instance_private (context);
+ priv->pending_devices--;
+ g_ptr_array_add (priv->devices, device);
+ g_signal_emit (context, signals[DEVICE_ADDED_SIGNAL], 0, device);
+}
+
+static void
+usb_device_added_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
+{
+ FpContextPrivate *priv = fp_context_get_instance_private (self);
+ GType found_driver = G_TYPE_NONE;
+ const FpIdEntry *found_entry = NULL;
+ gint found_score = 0;
+ gint i;
+ guint16 pid, vid;
+
+ pid = g_usb_device_get_pid (device);
+ vid = g_usb_device_get_vid (device);
+
+ /* Find the best driver to handle this USB device. */
+ for (i = 0; i < priv->drivers->len; i++)
+ {
+ GType driver = g_array_index (priv->drivers, GType, i);
+ FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
+ const FpIdEntry *entry;
+
+ if (cls->type != FP_DEVICE_TYPE_USB)
+ {
+ g_type_class_unref (cls);
+ continue;
+ }
+
+ for (entry = cls->id_table; entry->pid; entry++)
+ {
+ gint driver_score = 50;
+
+ if (entry->pid != pid || entry->vid != vid)
+ continue;
+
+ if (cls->usb_discover)
+ driver_score = cls->usb_discover (device);
+
+ /* Is this driver better than the one we had? */
+ if (driver_score <= found_score)
+ continue;
+
+ found_score = driver_score;
+ found_driver = driver;
+ found_entry = entry;
+ }
+
+ g_type_class_unref (cls);
+ }
+
+ if (found_driver == G_TYPE_NONE)
+ {
+ g_debug ("No driver found for USB device %04X:%04X", pid, vid);
+ return;
+ }
+
+ priv->pending_devices++;
+ g_async_initable_new_async (found_driver,
+ G_PRIORITY_LOW,
+ priv->cancellable,
+ async_device_init_done_cb,
+ self,
+ "fp-usb-device", device,
+ "fp-driver-data", found_entry->driver_data,
+ NULL);
+}
+
+static void
+usb_device_removed_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx)
+{
+ FpContextPrivate *priv = fp_context_get_instance_private (self);
+ gint i;
+
+ /* Do the lazy way and just look at each device. */
+ for (i = 0; i < priv->devices->len; i++)
+ {
+ FpDevice *dev = g_ptr_array_index (priv->devices, i);
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (dev);
+
+ if (cls->type != FP_DEVICE_TYPE_USB)
+ continue;
+
+ if (fpi_device_get_usb_device (dev) == device)
+ {
+ g_signal_emit (self, signals[DEVICE_REMOVED_SIGNAL], 0, dev);
+ g_ptr_array_remove_index_fast (priv->devices, i);
+
+ return;
+ }
+ }
+}
+
+static void
+fp_context_finalize (GObject *object)
+{
+ FpContext *self = (FpContext *) object;
+ FpContextPrivate *priv = fp_context_get_instance_private (self);
+
+ g_clear_pointer (&priv->devices, g_ptr_array_unref);
+
+ g_cancellable_cancel (priv->cancellable);
+ g_clear_object (&priv->cancellable);
+ g_clear_pointer (&priv->drivers, g_array_unref);
+ g_clear_object (&priv->usb_ctx);
+
+ G_OBJECT_CLASS (fp_context_parent_class)->finalize (object);
+}
+
+static void
+fp_context_class_init (FpContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = fp_context_finalize;
+
+ /**
+ * FpContext::device-added:
+ * @context: the #FpContext instance that emitted the signal
+ * @device: A #FpDevice
+ *
+ * This signal is emitted when a fingerprint reader is added.
+ **/
+ signals[DEVICE_ADDED_SIGNAL] = g_signal_new ("device-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FpContextClass, device_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ FP_TYPE_DEVICE);
+
+ /**
+ * FpContext::device-removed:
+ * @context: the #FpContext instance that emitted the signal
+ * @device: A #FpDevice
+ *
+ * This signal is emitted when a fingerprint reader is removed.
+ **/
+ signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FpContextClass, device_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ FP_TYPE_DEVICE);
+}
+
+static void
+fp_context_init (FpContext *self)
+{
+ g_autoptr(GError) error = NULL;
+ FpContextPrivate *priv = fp_context_get_instance_private (self);
+
+ priv->drivers = g_array_new (TRUE, FALSE, sizeof (GType));
+ fpi_get_driver_types (priv->drivers);
+
+ priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
+
+ priv->cancellable = g_cancellable_new ();
+ priv->usb_ctx = g_usb_context_new (&error);
+ if (!priv->usb_ctx)
+ {
+ fp_warn ("Could not initialise USB Subsystem: %s", error->message);
+ }
+ else
+ {
+ g_usb_context_set_debug (priv->usb_ctx, G_LOG_LEVEL_INFO);
+ g_signal_connect_object (priv->usb_ctx,
+ "device-added",
+ G_CALLBACK (usb_device_added_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->usb_ctx,
+ "device-removed",
+ G_CALLBACK (usb_device_removed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ }
+}
+
+/**
+ * fp_context_new:
+ *
+ * Create a new #FpContext.
+ *
+ * Returns: (transfer full): a newly created #FpContext
+ */
+FpContext *
+fp_context_new (void)
+{
+ return g_object_new (FP_TYPE_CONTEXT, NULL);
+}
+
+/**
+ * fp_context_enumerate:
+ * @context: a #FpContext
+ *
+ * Enumerate all devices. You should call this function exactly once
+ * at startup. Please note that it iterates the mainloop until all
+ * devices are enumerated.
+ */
+void
+fp_context_enumerate (FpContext *context)
+{
+ FpContextPrivate *priv = fp_context_get_instance_private (context);
+ gint i;
+
+ g_return_if_fail (FP_IS_CONTEXT (context));
+
+ if (priv->enumerated)
+ return;
+
+ priv->enumerated = TRUE;
+
+ /* USB devices are handled from callbacks */
+ g_usb_context_enumerate (priv->usb_ctx);
+
+ /* Handle Virtual devices based on environment variables */
+ for (i = 0; i < priv->drivers->len; i++)
+ {
+ GType driver = g_array_index (priv->drivers, GType, i);
+ FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
+ const FpIdEntry *entry;
+
+ if (cls->type != FP_DEVICE_TYPE_VIRTUAL)
+ continue;
+
+ for (entry = cls->id_table; entry->pid; entry++)
+ {
+ const gchar *val;
+
+ val = g_getenv (entry->virtual_envvar);
+ if (!val || val[0] == '\0')
+ continue;
+
+ g_debug ("Found virtual environment device: %s, %s", entry->virtual_envvar, val);
+ priv->pending_devices++;
+ g_async_initable_new_async (driver,
+ G_PRIORITY_LOW,
+ priv->cancellable,
+ async_device_init_done_cb,
+ context,
+ "fp-environ", val,
+ "fp-driver-data", entry->driver_data,
+ NULL);
+ g_debug ("created");
+ }
+
+ g_type_class_unref (cls);
+ }
+
+ while (priv->pending_devices)
+ g_main_context_iteration (NULL, TRUE);
+}
+
+/**
+ * fp_context_get_devices:
+ * @context: a #FpContext
+ *
+ * Get all devices. fp_context_enumerate() will be called as needed.
+ *
+ * Returns: (transfer none) (element-type FpDevice): a new #GPtrArray of #GUsbDevice's.
+ */
+GPtrArray *
+fp_context_get_devices (FpContext *context)
+{
+ FpContextPrivate *priv = fp_context_get_instance_private (context);
+
+ g_return_val_if_fail (FP_IS_CONTEXT (context), NULL);
+
+ fp_context_enumerate (context);
+
+ return priv->devices;
+}
diff --git a/libfprint/fp-context.h b/libfprint/fp-context.h
new file mode 100644
index 0000000..aeaeca0
--- /dev/null
+++ b/libfprint/fp-context.h
@@ -0,0 +1,52 @@
+/*
+ * FpContext - A FPrint context
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "fp-device.h"
+
+G_BEGIN_DECLS
+
+#define FP_TYPE_CONTEXT (fp_context_get_type ())
+G_DECLARE_DERIVABLE_TYPE (FpContext, fp_context, FP, CONTEXT, GObject)
+
+/**
+ * FpContextClass:
+ * @device_added: Called when a new device is added
+ * @device_removed: Called when a device is removed
+ *
+ * Class structure for #FpContext instances.
+ */
+struct _FpContextClass
+{
+ GObjectClass parent_class;
+
+ void (*device_added) (FpContext *context,
+ FpDevice *device);
+ void (*device_removed) (FpContext *context,
+ FpDevice *device);
+};
+
+FpContext *fp_context_new (void);
+
+void fp_context_enumerate (FpContext *context);
+
+GPtrArray *fp_context_get_devices (FpContext *context);
+
+G_END_DECLS
diff --git a/libfprint/fp-device.c b/libfprint/fp-device.c
new file mode 100644
index 0000000..fe47798
--- /dev/null
+++ b/libfprint/fp-device.c
@@ -0,0 +1,2574 @@
+/*
+ * FpDevice - A fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define FP_COMPONENT "device"
+#include "fpi-log.h"
+
+#include "fpi-device.h"
+
+/**
+ * SECTION: fp-device
+ * @title: FpDevice
+ * @short_description: Fingerprint device handling
+ *
+ * The #FpDevice object allows you to interact with fingerprint readers.
+ * Befor doing any other operation you need to fp_device_open() the device
+ * and after you are done you need to fp_device_close() it again.
+ */
+
+/**
+ * SECTION: fpi-device
+ * @title: Internal FpDevice
+ * @short_description: Internal device routines
+ *
+ * The methods that are availabe for drivers to manipulate a device. See
+ * #FpDeviceClass for more information. Also note that most of these are
+ * not relevant for image based devices, see #FpImageDeviceClass in that
+ * case.
+ *
+ * Also see the public #FpDevice routines.
+ */
+
+typedef struct
+{
+ FpDeviceType type;
+
+ union
+ {
+ GUsbDevice *usb_device;
+ const gchar *virtual_env;
+ };
+
+ gboolean is_open;
+
+ gchar *device_id;
+ gchar *device_name;
+ FpScanType scan_type;
+
+ guint64 driver_data;
+
+ gint nr_enroll_stages;
+ GSList *sources;
+
+ /* We always make sure that only one task is run at a time. */
+ FpDeviceAction current_action;
+ GTask *current_task;
+ GAsyncReadyCallback current_user_cb;
+ gulong current_cancellable_id;
+ GSource *current_idle_cancel_source;
+
+ /* State for tasks */
+ gboolean wait_for_finger;
+} FpDevicePrivate;
+
+static void fp_device_async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (FpDevice, fp_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+ fp_device_async_initable_iface_init)
+ G_ADD_PRIVATE (FpDevice))
+
+enum {
+ PROP_0,
+ PROP_DRIVER,
+ PROP_DEVICE_ID,
+ PROP_NAME,
+ PROP_NR_ENROLL_STAGES,
+ PROP_SCAN_TYPE,
+ PROP_FPI_ENVIRON,
+ PROP_FPI_USB_DEVICE,
+ PROP_FPI_DRIVER_DATA,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+typedef struct
+{
+ FpPrint *print;
+
+ FpEnrollProgress enroll_progress_cb;
+ gpointer enroll_progress_data;
+ GDestroyNotify enroll_progress_destroy;
+} FpEnrollData;
+
+static void
+enroll_data_free (gpointer free_data)
+{
+ FpEnrollData *data = free_data;
+
+ if (data->enroll_progress_destroy)
+ data->enroll_progress_destroy (data->enroll_progress_data);
+ data->enroll_progress_data = NULL;
+ g_clear_object (&data->print);
+ g_free (data);
+}
+
+/**
+ * fp_device_retry_quark:
+ *
+ * Return value: Quark representing a retryable error.
+ **/
+G_DEFINE_QUARK (fp - device - retry - quark, fp_device_retry)
+
+/**
+ * fp_device_error_quark:
+ *
+ * Return value: Quark representing a device error.
+ **/
+G_DEFINE_QUARK (fp - device - error - quark, fp_device_error)
+
+/**
+ * fpi_device_retry_new:
+ * @error: The #FpDeviceRetry error value describing the issue
+ *
+ * Create a new retry error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_retry_new (FpDeviceRetry error)
+{
+ const gchar *msg;
+
+ switch (error)
+ {
+ case FP_DEVICE_RETRY_GENERAL:
+ msg = "Please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_TOO_SHORT:
+ msg = "The swipe was too short, please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_CENTER_FINGER:
+ msg = "The finger was not centered properly, please try again.";
+ break;
+
+ case FP_DEVICE_RETRY_REMOVE_FINGER:
+ msg = "Please try again after removing the finger first.";
+ break;
+
+ default:
+ g_warning ("Unsupported error, returning general error instead!");
+ error = FP_DEVICE_RETRY_GENERAL;
+ msg = "Please try again.";
+ }
+
+ return g_error_new_literal (FP_DEVICE_RETRY, error, msg);
+}
+
+/**
+ * fpi_device_error_new:
+ * @error: The #FpDeviceRetry error value describing the issue
+ *
+ * Create a new error code for use with fpi_device_verify_complete() and
+ * similar calls.
+ */
+GError *
+fpi_device_error_new (FpDeviceError error)
+{
+ const gchar *msg;
+
+ switch (error)
+ {
+ case FP_DEVICE_ERROR_GENERAL:
+ msg = "An unspecified error occured!";
+ break;
+
+ case FP_DEVICE_ERROR_NOT_SUPPORTED:
+ msg = "The operation is not supported on this device!";
+ break;
+
+ case FP_DEVICE_ERROR_NOT_OPEN:
+ msg = "The device needs to be opened first!";
+ break;
+
+ case FP_DEVICE_ERROR_ALREADY_OPEN:
+ msg = "The device has already been opened!";
+ break;
+
+ case FP_DEVICE_ERROR_BUSY:
+ msg = "The device is still busy with another operation, please try again later.";
+ break;
+
+ case FP_DEVICE_ERROR_PROTO:
+ msg = "The driver encountered a protocol error with the device.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_INVALID:
+ msg = "Passed (print) data is not valid.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_FULL:
+ msg = "On device storage space is full.";
+ break;
+
+ case FP_DEVICE_ERROR_DATA_NOT_FOUND:
+ msg = "Print was not found on the devices storage.";
+ break;
+
+ default:
+ g_warning ("Unsupported error, returning general error instead!");
+ error = FP_DEVICE_ERROR_GENERAL;
+ msg = "An unspecified error occured!";
+ }
+
+ return g_error_new_literal (FP_DEVICE_ERROR, error, msg);
+}
+
+/**
+ * fpi_device_retry_new_msg:
+ * @error: The #FpDeviceRetry error value describing the issue
+ * @msg: Custom message to use
+ *
+ * Create a new retry error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_retry_new_msg (FpDeviceRetry error, const gchar *msg)
+{
+ return g_error_new_literal (FP_DEVICE_RETRY, error, msg);
+}
+
+/**
+ * fpi_device_error_new_msg:
+ * @error: The #FpDeviceRetry error value describing the issue
+ * @msg: Custom message to use
+ *
+ * Create a new error code for use with fpi_device_verify_complete()
+ * and similar calls.
+ */
+GError *
+fpi_device_error_new_msg (FpDeviceError error, const gchar *msg)
+{
+ return g_error_new_literal (FP_DEVICE_ERROR, error, msg);
+}
+
+static gboolean
+fp_device_cancel_in_idle_cb (gpointer user_data)
+{
+ FpDevice *self = user_data;
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (self);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ g_assert (cls->cancel);
+ g_assert (priv->current_action != FP_DEVICE_ACTION_NONE);
+
+ g_debug ("Idle cancelling on ongoing operation!");
+
+ priv->current_idle_cancel_source = NULL;
+
+ cls->cancel (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Notify the class that the task was cancelled; this should be connected
+ * with the GTask as the user_data object for automatic cleanup together
+ * with the task. */
+static void
+fp_device_cancelled_cb (GCancellable *cancellable, FpDevice *self)
+{
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (self);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ if (cls->cancel)
+ {
+ priv->current_idle_cancel_source = g_idle_source_new ();
+ g_source_set_callback (priv->current_idle_cancel_source,
+ fp_device_cancel_in_idle_cb,
+ self,
+ NULL);
+ g_source_attach (priv->current_idle_cancel_source, NULL);
+ g_source_unref (priv->current_idle_cancel_source);
+ }
+}
+
+static void
+fp_device_constructed (GObject *object)
+{
+ FpDevice *self = (FpDevice *) object;
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (self);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ priv->type = cls->type;
+ if (cls->nr_enroll_stages)
+ priv->nr_enroll_stages = cls->nr_enroll_stages;
+ priv->scan_type = cls->scan_type;
+ priv->device_name = g_strdup (cls->full_name);
+ priv->device_id = g_strdup ("0");
+
+ G_OBJECT_CLASS (fp_device_parent_class)->constructed (object);
+}
+
+static void
+fp_device_finalize (GObject *object)
+{
+ FpDevice *self = (FpDevice *) object;
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ g_assert (priv->current_action == FP_DEVICE_ACTION_NONE);
+ g_assert (priv->current_task == NULL);
+ if (priv->is_open)
+ g_warning ("User destroyed open device! Not cleaning up properly!");
+
+ g_slist_free_full (priv->sources, (GDestroyNotify) g_source_destroy);
+
+ g_clear_pointer (&priv->device_id, g_free);
+ g_clear_pointer (&priv->device_name, g_free);
+
+ G_OBJECT_CLASS (fp_device_parent_class)->finalize (object);
+}
+
+static void
+fp_device_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FpDevice *self = FP_DEVICE (object);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_NR_ENROLL_STAGES:
+ g_value_set_int (value, priv->nr_enroll_stages);
+ break;
+
+ case PROP_SCAN_TYPE:
+ g_value_set_enum (value, priv->scan_type);
+ break;
+
+ case PROP_DRIVER:
+ g_value_set_static_string (value, FP_DEVICE_GET_CLASS (priv)->id);
+ break;
+
+ case PROP_DEVICE_ID:
+ g_value_set_string (value, priv->device_id);
+ break;
+
+ case PROP_NAME:
+ g_value_set_string (value, priv->device_name);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_device_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FpDevice *self = FP_DEVICE (object);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (self);
+
+ /* _construct has not run yet, so we cannot use priv->type. */
+ switch (prop_id)
+ {
+ case PROP_FPI_ENVIRON:
+ if (cls->type == FP_DEVICE_TYPE_VIRTUAL)
+ priv->virtual_env = g_value_dup_string (value);
+ else
+ g_assert (g_value_get_string (value) == NULL);
+ break;
+
+ case PROP_FPI_USB_DEVICE:
+ if (cls->type == FP_DEVICE_TYPE_USB)
+ priv->usb_device = g_value_dup_object (value);
+ else
+ g_assert (g_value_get_object (value) == NULL);
+ break;
+
+ case PROP_FPI_DRIVER_DATA:
+ priv->driver_data = g_value_get_uint64 (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_device_async_initable_init_async (GAsyncInitable *initable,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevice *self = FP_DEVICE (initable);
+ FpDevicePrivate *priv = fp_device_get_instance_private (self);
+
+ /* It is next to impossible to call async_init at the wrong time. */
+ g_assert (!priv->is_open);
+ g_assert (!priv->current_task);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!FP_DEVICE_GET_CLASS (self)->probe)
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_PROBE;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ self,
+ NULL);
+ }
+
+ FP_DEVICE_GET_CLASS (self)->probe (self);
+}
+
+static gboolean
+fp_device_async_initable_init_finish (GAsyncInitable *initable,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+fp_device_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = fp_device_async_initable_init_async;
+ iface->init_finish = fp_device_async_initable_init_finish;
+}
+
+static void
+fp_device_class_init (FpDeviceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = fp_device_constructed;
+ object_class->finalize = fp_device_finalize;
+ object_class->get_property = fp_device_get_property;
+ object_class->set_property = fp_device_set_property;
+
+ properties[PROP_NR_ENROLL_STAGES] =
+ g_param_spec_uint ("nr-enroll-stages",
+ "EnrollStages",
+ "Number of enroll stages needed on the device",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_SCAN_TYPE] =
+ g_param_spec_enum ("scan-type",
+ "ScanType",
+ "The scan type of the device",
+ FP_TYPE_SCAN_TYPE, FP_SCAN_TYPE_SWIPE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_DRIVER] =
+ g_param_spec_string ("driver",
+ "Driver",
+ "String describing the driver",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_DEVICE_ID] =
+ g_param_spec_string ("device-id",
+ "Device ID",
+ "String describing the device, often generic but may be a serial number",
+ "",
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_NAME] =
+ g_param_spec_string ("name",
+ "Device Name",
+ "Human readable name for the device",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_FPI_ENVIRON] =
+ g_param_spec_string ("fp-environ",
+ "Virtual Environment",
+ "Private: The environment variable for the virtual device",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_FPI_USB_DEVICE] =
+ g_param_spec_object ("fp-usb-device",
+ "USB Device",
+ "Private: The USB device for the device",
+ G_USB_TYPE_DEVICE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_FPI_DRIVER_DATA] =
+ g_param_spec_uint64 ("fp-driver-data",
+ "Driver Data",
+ "Private: The driver data from the ID table entry",
+ 0,
+ G_MAXUINT64,
+ 0,
+ G_PARAM_STATIC_STRINGS | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+fp_device_init (FpDevice *self)
+{
+}
+
+/**
+ * fp_device_get_driver:
+ * @device: A #FpDevice
+ *
+ * Returns: (transfer none): The ID of the driver
+ */
+const gchar *
+fp_device_get_driver (FpDevice *device)
+{
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+
+ return FP_DEVICE_GET_CLASS (device)->id;
+}
+
+/**
+ * fp_device_get_device_id:
+ * @device: A #FpDevice
+ *
+ * Returns: (transfer none): The ID of the device
+ */
+const gchar *
+fp_device_get_device_id (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+
+ return priv->device_id;
+}
+
+/**
+ * fp_device_get_name:
+ * @device: A #FpDevice
+ *
+ * Returns: (transfer none): The human readable name of the device
+ */
+const gchar *
+fp_device_get_name (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+
+ return priv->device_name;
+}
+
+/**
+ * fp_device_get_scan_type:
+ * @device: A #FpDevice
+ *
+ * Retrieves the scan type of the device.
+ *
+ * Returns: The #FpScanType
+ */
+FpScanType
+fp_device_get_scan_type (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FP_SCAN_TYPE_SWIPE);
+
+ return priv->scan_type;
+}
+
+/**
+ * fp_device_get_nr_enroll_stages:
+ * @device: A #FpDevice
+ *
+ * Retrieves the number of enroll stages for this device.
+ *
+ * Returns: The number of enroll stages
+ */
+gint
+fp_device_get_nr_enroll_stages (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), -1);
+
+ return priv->nr_enroll_stages;
+}
+
+/**
+ * fp_device_supports_identify:
+ * @device: A #FpDevice
+ *
+ * Check whether the device supports identification.
+ *
+ * Returns: Whether the device supports identification
+ */
+gboolean
+fp_device_supports_identify (FpDevice *device)
+{
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ return cls->identify != NULL;
+}
+
+/**
+ * fp_device_supports_capture:
+ * @device: A #FpDevice
+ *
+ * Check whether the device supports capturing images.
+ *
+ * Returns: Whether the device supports image capture
+ */
+gboolean
+fp_device_supports_capture (FpDevice *device)
+{
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ return cls->capture != NULL;
+}
+
+/**
+ * fp_device_has_storage:
+ * @device: A #FpDevice
+ *
+ * Whether the device has on-chip storage. If it has, you can list the
+ * prints stored on the with fp_device_list_prints() and you should
+ * always delete prints from the device again using
+ * fp_device_delete_print().
+ */
+gboolean
+fp_device_has_storage (FpDevice *device)
+{
+ FpDeviceClass *cls = FP_DEVICE_GET_CLASS (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ return cls->list != NULL;
+}
+
+/**
+ * fp_device_open:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to open the device. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * fp_device_open_finish().
+ */
+void
+fp_device_open (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ GError *error = NULL;
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_ALREADY_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ switch (priv->type)
+ {
+ case FP_DEVICE_TYPE_USB:
+ if (!g_usb_device_open (priv->usb_device, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+ break;
+
+ case FP_DEVICE_TYPE_VIRTUAL:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ g_task_return_error (task, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_OPEN;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ FP_DEVICE_GET_CLASS (device)->open (device);
+}
+
+/**
+ * fp_device_open_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to open the device.
+ * See fp_device_open().
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_open_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * fp_device_close:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to close the device. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * fp_device_close_finish().
+ */
+void
+fp_device_close (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_CLOSE;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ FP_DEVICE_GET_CLASS (device)->close (device);
+}
+
+/**
+ * fp_device_close_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to close the device.
+ * See fp_device_close().
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_close_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+
+/**
+ * fp_device_enroll:
+ * @device: a #FpDevice
+ * @template_print: (transfer floating): a #FpPrint
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @progress_cb: (nullable) (scope notified): progress reporting callback
+ * @progress_data: (closure progress_cb): user data for @progress_cb
+ * @progress_destroy: (destroy progress_data): Destroy notify for @progress_data
+ * @callback: (scope async): the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to enroll a print. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * 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 successed.
+ */
+void
+fp_device_enroll (FpDevice *device,
+ FpPrint *template_print,
+ GCancellable *cancellable,
+ FpEnrollProgress progress_cb,
+ gpointer progress_data,
+ GDestroyNotify progress_destroy,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpEnrollData *data;
+ FpPrintType print_type;
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ 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));
+ return;
+ }
+
+ g_object_get (template_print, "fp-type", &print_type, NULL);
+ if (print_type != FP_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;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_ENROLL;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ data = g_new0 (FpEnrollData, 1);
+ data->print = g_object_ref_sink (template_print);
+ data->enroll_progress_cb = progress_cb;
+ data->enroll_progress_data = progress_data;
+
+ // Attach the progress data as task data so that it is destroyed
+ g_task_set_task_data (priv->current_task, data, enroll_data_free);
+
+ FP_DEVICE_GET_CLASS (device)->enroll (device);
+}
+
+/**
+ * fp_device_enroll_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to enroll a print. You should check
+ * for an error of type %FP_DEVICE_RETRY to prompt the user again if there
+ * was an interaction issue.
+ * See fp_device_enroll().
+ *
+ * Returns: (transfer full): The enrolled #FpPrint, or %NULL on error
+ */
+FpPrint *
+fp_device_enroll_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+/**
+ * fp_device_verify:
+ * @device: a #FpDevice
+ * @enrolled_print: a #FpPrint to verify
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to close the device. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * fp_device_verify_finish().
+ */
+void
+fp_device_verify (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_VERIFY;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ g_task_set_task_data (priv->current_task,
+ g_object_ref (enrolled_print),
+ g_object_unref);
+
+ FP_DEVICE_GET_CLASS (device)->verify (device);
+}
+
+/**
+ * fp_device_verify_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @match: (out): Whether the user presented the correct finger
+ * @print: (out) (transfer full) (nullable): Location to store the scanned print, or %NULL to ignore
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to verify an enrolled print. You should check
+ * for an error of type %FP_DEVICE_RETRY to prompt the user again if there
+ * was an interaction issue.
+ *
+ * With @print you can fetch the newly created print and retrieve the image data if available.
+ *
+ * See fp_device_verify().
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_verify_finish (FpDevice *device,
+ GAsyncResult *result,
+ gboolean *match,
+ FpPrint **print,
+ GError **error)
+{
+ gint res;
+
+ res = g_task_propagate_int (G_TASK (result), error);
+
+ if (print)
+ {
+ *print = g_object_get_data (G_OBJECT (result), "print");
+ if (*print)
+ g_object_ref (*print);
+ }
+
+ if (match)
+ *match = res == FPI_MATCH_SUCCESS;
+
+ return res != FPI_MATCH_ERROR;
+}
+
+/**
+ * fp_device_identify:
+ * @device: a #FpDevice
+ * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to identify prints. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * fp_device_identify_finish().
+ */
+void
+fp_device_identify (FpDevice *device,
+ GPtrArray *prints,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_IDENTIFY;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ g_task_set_task_data (priv->current_task,
+ g_ptr_array_ref (prints),
+ (GDestroyNotify) g_ptr_array_unref);
+
+ FP_DEVICE_GET_CLASS (device)->verify (device);
+}
+
+/**
+ * fp_device_identify_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @match: (out) (transfer full) (nullable): Location for the matched #FpPrint, or %NULL
+ * @print: (out) (transfer full) (nullable): Location for the new #FpPrint, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to identify a print. You should check
+ * for an error of type %FP_DEVICE_RETRY to prompt the user again if there
+ * was an interaction issue.
+ *
+ * Use @match to find the print that matched. With @print you can fetch the
+ * newly created print and retrieve the image data if available.
+ *
+ * See fp_device_identify().
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_identify_finish (FpDevice *device,
+ GAsyncResult *result,
+ FpPrint **match,
+ FpPrint **print,
+ GError **error)
+{
+ if (print)
+ {
+ *print = g_object_get_data (G_OBJECT (result), "print");
+ if (*print)
+ g_object_ref (*print);
+ }
+ if (match)
+ {
+ *match = g_object_get_data (G_OBJECT (result), "match");
+ if (*match)
+ g_object_ref (*match);
+ }
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * fp_device_capture:
+ * @device: a #FpDevice
+ * @wait_for_finger: Whether to wait for a finger or not
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to capture an image. The callback will
+ * be called once the operation has finished. Retrieve the result with
+ * fp_device_capture_finish().
+ */
+void
+fp_device_capture (FpDevice *device,
+ gboolean wait_for_finger,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_CAPTURE;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ priv->wait_for_finger = wait_for_finger;
+
+ FP_DEVICE_GET_CLASS (device)->capture (device);
+}
+
+/**
+ * fp_device_capture_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to capture an image. You should check
+ * for an error of type %FP_DEVICE_RETRY to prompt the user again if there
+ * was an interaction issue.
+ *
+ * See fp_device_capture().
+ *
+ * Returns: (transfer full): #FpImage or %NULL on error
+ */
+FpImage *
+fp_device_capture_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+/**
+ * fp_device_delete_print:
+ * @device: a #FpDevice
+ * @enrolled_print: a #FpPrint to delete
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to delete a print from the device.
+ * The callback will be called once the operation has finished. Retrieve
+ * the result with fp_device_delete_print_finish().
+ *
+ * This only makes sense on devices that store prints on-chip, but is safe
+ * to always call.
+ */
+void
+fp_device_delete_print (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ /* Succeed immediately if delete is not implemented. */
+ if (!FP_DEVICE_GET_CLASS (device)->delete)
+ {
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_DELETE;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ g_task_set_task_data (priv->current_task,
+ g_object_ref (enrolled_print),
+ g_object_unref);
+
+ FP_DEVICE_GET_CLASS (device)->delete (device);
+}
+
+/**
+ * fp_device_delete_print_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to delete an enrolled print.
+ *
+ * See fp_device_delete_print().
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_delete_print_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * fp_device_list_prints:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Start an asynchronous operation to list all prints stored on the device.
+ * This only makes sense on devices that store prints on-chip.
+ *
+ * Retrieve the result with fp_device_list_prints_finish().
+ */
+void
+fp_device_list_prints (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ task = g_task_new (device, cancellable, callback, user_data);
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ if (!priv->is_open)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_NOT_OPEN));
+ return;
+ }
+
+ if (priv->current_task)
+ {
+ g_task_return_error (task,
+ fpi_device_error_new (FP_DEVICE_ERROR_BUSY));
+ return;
+ }
+
+ priv->current_action = FP_DEVICE_ACTION_LIST;
+ priv->current_task = g_steal_pointer (&task);
+ if (cancellable)
+ {
+ priv->current_cancellable_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (fp_device_cancelled_cb),
+ device,
+ NULL);
+ }
+
+ FP_DEVICE_GET_CLASS (device)->list (device);
+}
+
+/**
+ * fp_device_list_prints_finish:
+ * @device: A #FpDevice
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish an asynchronous operation to list all device stored prints.
+ *
+ * See fp_device_list_prints().
+ *
+ * Returns: (element-type FpPrint) (transfer container): Array of prints or %NULL on error
+ */
+GPtrArray *
+fp_device_list_prints_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+typedef struct
+{
+ GSource source;
+ FpDevice *device;
+ FpTimeoutFunc callback;
+ gpointer user_data;
+} FpDeviceTimeoutSource;
+
+gboolean
+device_timeout_cb (gpointer user_data)
+{
+ FpDeviceTimeoutSource *source = user_data;
+
+ source->callback (source->device, source->user_data);
+
+ return G_SOURCE_REMOVE;
+}
+
+void
+timeout_finalize (GSource *source)
+{
+ FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
+ FpDevicePrivate *priv;
+
+ priv = fp_device_get_instance_private (timeout_source->device);
+ priv->sources = g_slist_remove (priv->sources, source);
+}
+
+static gboolean
+timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+ FpDeviceTimeoutSource *timeout_source = (FpDeviceTimeoutSource *) source;
+
+ ((FpTimeoutFunc) callback)(timeout_source->device, user_data);
+
+ return G_SOURCE_REMOVE;
+}
+
+static GSourceFuncs timeout_funcs = {
+ NULL, /* prepare */
+ NULL, /* check */
+ timeout_dispatch,
+ timeout_finalize,
+ NULL, NULL
+};
+
+/* Private API functions */
+
+/**
+ * fpi_device_set_nr_enroll_stages:
+ * @device: The #FpDevice
+ * @enroll_stages: The number of enroll stages
+ *
+ * Updates the reported number of enroll stages that the device needs.
+ * If all supported devices have the same number of stages, then the
+ * value can simply be set in the class.
+ */
+void
+fpi_device_set_nr_enroll_stages (FpDevice *device,
+ gint enroll_stages)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+
+ priv->nr_enroll_stages = enroll_stages;
+ g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NR_ENROLL_STAGES]);
+}
+
+/**
+ * fpi_device_set_scan_type:
+ * @device: The #FpDevice
+ * @scan_type: The scan type of the device
+ *
+ * Updates the the scan type of the device from the default.
+ * If all supported devices have the same scan type, then the
+ * value can simply be set in the class.
+ */
+void
+fpi_device_set_scan_type (FpDevice *device,
+ FpScanType scan_type)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+
+ priv->scan_type = scan_type;
+ g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_SCAN_TYPE]);
+}
+
+/**
+ * fpi_device_add_timeout:
+ * @device: The #FpDevice
+ * @interval: The interval in milliseconds
+ * @func: The #FpTimeoutFunc to call on timeout
+ * @user_data: User data to pass to the callback
+ *
+ * Register a timeout to run. Drivers should always make sure that timers are
+ * cancelled when appropriate.
+ *
+ * Returns: (transfer none): A newly created and attached #GSource
+ */
+GSource *
+fpi_device_add_timeout (FpDevice *device,
+ gint interval,
+ FpTimeoutFunc func,
+ gpointer user_data)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpDeviceTimeoutSource *source;
+
+ source = (FpDeviceTimeoutSource *) g_source_new (&timeout_funcs,
+ sizeof (FpDeviceTimeoutSource));
+ source->device = device;
+ source->user_data = user_data;
+
+ g_source_attach (&source->source, NULL);
+ g_source_set_callback (&source->source, (GSourceFunc) func, user_data, NULL);
+ g_source_set_ready_time (&source->source,
+ g_source_get_time (&source->source) + interval * (guint64) 1000);
+ priv->sources = g_slist_prepend (priv->sources, source);
+ g_source_unref (&source->source);
+
+ return &source->source;
+}
+
+/**
+ * fpi_device_get_usb_device:
+ * @device: The #FpDevice
+ *
+ * Get the #GUsbDevice for this #FpDevice. Only permissible to call if the
+ * #FpDevice is of type %FP_DEVICE_TYPE_USB.
+ *
+ * Returns: The #GUsbDevice
+ */
+GUsbDevice *
+fpi_device_get_usb_device (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_USB, NULL);
+
+ return priv->usb_device;
+}
+
+/**
+ * fpi_device_get_virtual_env:
+ * @device: The #FpDevice
+ *
+ * Get the value of the environment variable that caused the virtual #FpDevice to be
+ * generated. Only permissible to call if the #FpDevice is of type %FP_DEVICE_TYPE_VIRTUAL.
+ *
+ * Returns: The value of the environment variable
+ */
+const gchar *
+fpi_device_get_virtual_env (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->type == FP_DEVICE_TYPE_VIRTUAL, NULL);
+
+ return priv->virtual_env;
+}
+
+/**
+ * fpi_device_get_current_action:
+ * @device: The #FpDevice
+ *
+ * Get the currently ongoing action or %FP_DEVICE_ACTION_NONE if there
+ * is no operation at this time.
+ *
+ * This is useful for drivers that might share code paths between different
+ * actions (e.g. verify and identify) and want to find out again later which
+ * action was started in the beginning.
+ *
+ * Returns: The ongoing #FpDeviceAction
+ */
+FpDeviceAction
+fpi_device_get_current_action (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FP_DEVICE_ACTION_NONE);
+
+ return priv->current_action;
+}
+
+/**
+ * fpi_device_action_is_cancelled:
+ * @device: The #FpDevice
+ *
+ * Checks whether the current action has been cancelled by the user.
+ * This is equivalent to first getting the cancellable using
+ * fpi_device_get_cancellable() and then checking whether it has been
+ * cancelled (if it is non-NULL).
+ *
+ * Returns: %TRUE if action should be cancelled
+ */
+gboolean
+fpi_device_action_is_cancelled (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ GCancellable *cancellable;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), TRUE);
+ g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, TRUE);
+
+ cancellable = g_task_get_cancellable (priv->current_task);
+
+ return cancellable ? g_cancellable_is_cancelled (cancellable) : FALSE;
+}
+
+/**
+ * fpi_device_get_driver_data:
+ * @device: The #FpDevice
+ *
+ * Returns: The driver data from the #FpIdEntry table entry
+ */
+guint64
+fpi_device_get_driver_data (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), 0);
+
+ return priv->driver_data;
+}
+
+/**
+ * fpi_device_get_enroll_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The user provided template print
+ *
+ * Get data for enrollment.
+ */
+void
+fpi_device_get_enroll_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpEnrollData *data;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+
+ data = g_task_get_task_data (priv->current_task);
+ g_assert (data);
+
+ if (print)
+ *print = data->print;
+}
+
+/**
+ * fpi_device_get_capture_data:
+ * @device: The #FpDevice
+ * @wait_for_finger: (out): Whether to wait for finger or not
+ *
+ * Get data for capture.
+ */
+void
+fpi_device_get_capture_data (FpDevice *device,
+ gboolean *wait_for_finger)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
+
+ if (wait_for_finger)
+ *wait_for_finger = priv->wait_for_finger;
+}
+
+/**
+ * fpi_device_get_verify_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The enrolled print
+ *
+ * Get data for verify.
+ */
+void
+fpi_device_get_verify_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
+
+ if (print)
+ *print = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_identify_data:
+ * @device: The #FpDevice
+ * @prints: (out) (transfer none) (element-type FpPrint): The gallery of prints
+ *
+ * Get data for identify.
+ */
+void
+fpi_device_get_identify_data (FpDevice *device,
+ GPtrArray **prints)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
+
+ if (prints)
+ *prints = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_delete_data:
+ * @device: The #FpDevice
+ * @print: (out) (transfer none): The print to delete
+ *
+ * Get data for delete.
+ */
+void
+fpi_device_get_delete_data (FpDevice *device,
+ FpPrint **print)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
+
+ if (print)
+ *print = g_task_get_task_data (priv->current_task);
+}
+
+/**
+ * fpi_device_get_cancellable:
+ * @device: The #FpDevice
+ *
+ * Retrieve the #GCancellable that may cancel the currently ongoing operation. This
+ * is primarily useful to pass directly to e.g. fpi_usb_transfer_submit() for cancellable
+ * transfers.
+ * In many cases the cancel vfunc may be more convenient to react to cancellation in some
+ * way.
+ *
+ * Returns: (transfer none): The #GCancellable for the current action.
+ */
+GCancellable *
+fpi_device_get_cancellable (FpDevice *device)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE, NULL);
+
+ return g_task_get_cancellable (priv->current_task);
+}
+
+/**
+ * fpi_device_action_error:
+ * @device: The #FpDevice
+ * @error: The #GError to return
+ *
+ * Finish an ongoing action with an error. This is the same as calling
+ * the corresponding complete function such as fpi_device_open_complete()
+ * with an error set. If possible, use the correct complete function as
+ * that results in improved error detection.
+ */
+void
+fpi_device_action_error (FpDevice *device,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action != FP_DEVICE_ACTION_NONE);
+
+ if (error != NULL)
+ {
+ g_debug ("Device reported generic error during action; action was: %i", priv->current_action);
+ }
+ else
+ {
+ g_warning ("Device failed to pass an error to generic action error function");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, "Device reported error but did not provide an error condition");
+ }
+
+
+ switch (priv->current_action)
+ {
+ case FP_DEVICE_ACTION_PROBE:
+ fpi_device_probe_complete (device, NULL, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_OPEN:
+ fpi_device_open_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_CLOSE:
+ fpi_device_close_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_ENROLL:
+ fpi_device_enroll_complete (device, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_VERIFY:
+ fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_IDENTIFY:
+ fpi_device_identify_complete (device, NULL, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_CAPTURE:
+ fpi_device_capture_complete (device, NULL, error);
+ break;
+
+ case FP_DEVICE_ACTION_DELETE:
+ fpi_device_delete_complete (device, error);
+ break;
+
+ case FP_DEVICE_ACTION_LIST:
+ fpi_device_list_complete (device, NULL, error);
+ break;
+
+ default:
+ case FP_DEVICE_ACTION_NONE:
+ g_return_if_reached ();
+ break;
+ }
+}
+
+/**
+ * fpi_device_probe_complete:
+ * @device: The #FpDevice
+ * @device_id: Unique ID for the device or %NULL
+ * @device_name: Human readable name or %NULL for driver name
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing probe operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_probe_complete (FpDevice *device,
+ const gchar *device_id,
+ const gchar *device_name,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE);
+
+ g_debug ("Device reported probe completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (!error)
+ {
+ if (device_id)
+ {
+ g_clear_pointer (&priv->device_id, g_free);
+ priv->device_id = g_strdup (device_id);
+ g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_DEVICE_ID]);
+ }
+ if (device_name)
+ {
+ g_clear_pointer (&priv->device_name, g_free);
+ priv->device_name = g_strdup (device_name);
+ g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NAME]);
+ }
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ }
+}
+
+/**
+ * fpi_device_open_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing open operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_open_complete (FpDevice *device, GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN);
+
+ g_debug ("Device reported open completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (!error)
+ priv->is_open = TRUE;
+
+ if (!error)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+}
+
+/**
+ * fpi_device_close_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing close operation. If error is %NULL success is assumed.
+ */
+void
+fpi_device_close_complete (FpDevice *device, GError *error)
+{
+ g_autoptr(GTask) task = NULL;
+ GError *nested_error = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CLOSE);
+
+ g_debug ("Device reported close completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+ priv->is_open = FALSE;
+
+ switch (priv->type)
+ {
+ case FP_DEVICE_TYPE_USB:
+ if (!g_usb_device_close (priv->usb_device, &nested_error))
+ {
+ if (error == NULL)
+ error = nested_error;
+ g_task_return_error (task, error);
+ return;
+ }
+ break;
+
+ case FP_DEVICE_TYPE_VIRTUAL:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ g_task_return_error (task, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
+ return;
+ }
+
+ if (!error)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+}
+
+/**
+ * fpi_device_enroll_complete:
+ * @device: The #FpDevice
+ * @print: (nullable) (transfer full): The #FpPrint or %NULL on failure
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing enroll operation. The #FpPrint can be stored by the
+ * caller for later verification.
+ */
+void
+fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+
+ g_debug ("Device reported enroll completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (!error)
+ {
+ if (FP_IS_PRINT (print))
+ {
+ g_task_return_pointer (task, print, g_object_unref);
+ }
+ else
+ {
+ g_warning ("Driver did not provide a valid print and failed to provide an error!");
+ g_task_return_new_error (task,
+ FP_DEVICE_ERROR,
+ FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide print data!");
+ }
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ if (FP_IS_PRINT (print))
+ {
+ g_warning ("Driver passed an error but also provided a print, returning error!");
+ g_object_unref (print);
+ }
+ }
+}
+
+/**
+ * fpi_device_verify_complete:
+ * @device: The #FpDevice
+ * @result: The #FpiMatchResult of the operation
+ * @print: The scanned #FpPrint
+ * @error: A #GError if result is %FPI_MATCH_ERROR
+ *
+ * Finish an ongoing verify operation. The returned print should be
+ * representing the new scan and not the one passed for verification.
+ */
+void
+fpi_device_verify_complete (FpDevice *device,
+ FpiMatchResult result,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
+
+ g_debug ("Device reported verify completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ g_object_set_data_full (G_OBJECT (task),
+ "print",
+ print,
+ g_object_unref);
+
+ if (!error)
+ {
+ if (result != FPI_MATCH_ERROR)
+ {
+ g_task_return_int (task, result);
+ }
+ else
+ {
+ g_warning ("Driver did not provide an error for a failed verify operation!");
+ g_task_return_new_error (task,
+ FP_DEVICE_ERROR,
+ FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide an error!");
+ }
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ if (result != FPI_MATCH_ERROR)
+ {
+ g_warning ("Driver passed an error but also provided a match result, returning error!");
+ g_object_unref (print);
+ }
+ }
+}
+
+/**
+ * fpi_device_identify_complete:
+ * @device: The #FpDevice
+ * @match: The matching #FpPrint from the passed gallery, or %NULL if none matched
+ * @print: The scanned #FpPrint, may be %NULL
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing identify operation. The match that was identified is
+ * returned in @match. The @print parameter returns the newly created scan
+ * that was used for matching.
+ */
+void
+fpi_device_identify_complete (FpDevice *device,
+ FpPrint *match,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
+
+ g_debug ("Device reported identify completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ g_object_set_data_full (G_OBJECT (task),
+ "print",
+ print,
+ g_object_unref);
+ g_object_set_data_full (G_OBJECT (task),
+ "match",
+ match,
+ g_object_unref);
+ if (!error)
+ {
+ g_task_return_boolean (task, TRUE);
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ if (match)
+ {
+ g_warning ("Driver passed an error but also provided a match result, returning error!");
+ g_clear_object (&match);
+ }
+ }
+}
+
+
+/**
+ * fpi_device_capture_complete:
+ * @device: The #FpDevice
+ * @image: The #FpImage, or %NULL on error
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing capture operation.
+ */
+void
+fpi_device_capture_complete (FpDevice *device,
+ FpImage *image,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
+
+ g_debug ("Device reported capture completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (!error)
+ {
+ if (image)
+ {
+ g_task_return_pointer (task, image, g_object_unref);
+ }
+ else
+ {
+ g_warning ("Driver did not provide an error for a failed capture operation!");
+ g_task_return_new_error (task,
+ FP_DEVICE_ERROR,
+ FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide an error!");
+ }
+ }
+ else
+ {
+ g_task_return_error (task, error);
+ if (image)
+ {
+ g_warning ("Driver passed an error but also provided an image, returning error!");
+ g_clear_object (&image);
+ }
+ }
+}
+
+/**
+ * fpi_device_delete_complete:
+ * @device: The #FpDevice
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing delete operation.
+ */
+void
+fpi_device_delete_complete (FpDevice *device,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
+
+ g_debug ("Device reported deletion completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (!error)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+}
+
+/**
+ * fpi_device_list_complete:
+ * @device: The #FpDevice
+ * @prints: (element-type FpPrint) (transfer container): Possibly empty array of prints or %NULL on error
+ * @error: The #GError or %NULL on success
+ *
+ * Finish an ongoing list operation.
+ *
+ * Please note that the @prints array will be free'ed using
+ * g_ptr_array_unref() and the elements are destroyed automatically.
+ * As such, you must use g_ptr_array_new_with_free_func() with
+ * g_object_unref() as free func to create the array.
+ */
+void
+fpi_device_list_complete (FpDevice *device,
+ GPtrArray *prints,
+ GError *error)
+{
+ g_autoptr(GTask) task = NULL;
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_LIST);
+
+ g_debug ("Device reported listing completion");
+
+ task = g_steal_pointer (&priv->current_task);
+ g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
+ if (priv->current_cancellable_id)
+ g_cancellable_disconnect (g_task_get_cancellable (task), priv->current_cancellable_id);
+ priv->current_cancellable_id = 0;
+ priv->current_action = FP_DEVICE_ACTION_NONE;
+
+ if (prints && error)
+ {
+ g_warning ("Driver reported back prints and error, ignoring prints");
+ g_clear_pointer (&prints, g_ptr_array_unref);
+ }
+ else if (!prints && !error)
+ {
+ g_warning ("Driver did not pass array but failed to provide an error");
+ error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "Driver failed to provide a list of prints");
+ }
+
+ if (!error)
+ g_task_return_pointer (task, prints, (GDestroyNotify) g_ptr_array_unref);
+ else
+ g_task_return_error (task, error);
+}
+
+/**
+ * fpi_device_enroll_progress:
+ * @device: The #FpDevice
+ * @completed_stages: The number of stages that are completed at this point
+ * @print: The #FpPrint for the newly completed stage or %NULL on failure
+ * @error: The #GError or %NULL on success
+ *
+ * Notify about the progress of the enroll operation. This is important for UI interaction.
+ * The passed error may be used if a scan needs to be retried, use fpi_device_retry_new().
+ */
+void
+fpi_device_enroll_progress (FpDevice *device,
+ gint completed_stages,
+ FpPrint *print,
+ GError *error)
+{
+ FpDevicePrivate *priv = fp_device_get_instance_private (device);
+ FpEnrollData *data;
+
+ g_return_if_fail (FP_IS_DEVICE (device));
+ g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
+ g_return_if_fail (error == NULL || error->domain == FP_DEVICE_RETRY);
+
+ g_debug ("Device reported enroll progress, reported %i of %i have been completed", completed_stages, priv->nr_enroll_stages);
+
+ if (error && print)
+ {
+ g_warning ("Driver passed an error and also provided a print, returning error!");
+ g_clear_object (&print);
+ }
+
+ data = g_task_get_task_data (priv->current_task);
+
+ if (data->enroll_progress_cb)
+ {
+ data->enroll_progress_cb (device,
+ completed_stages,
+ print,
+ data->enroll_progress_data,
+ error);
+ }
+ if (error)
+ g_error_free (error);
+}
+
+
+static void
+async_result_ready (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GTask **task = user_data;
+
+ *task = g_object_ref (G_TASK (res));
+}
+
+/**
+ * fp_device_open_sync:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Open the device synchronously.
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_open_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_open (device, cancellable, async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_open_finish (device, task, error);
+}
+
+/**
+ * fp_device_close_sync:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Close the device synchronously.
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_close_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_close (device, cancellable, async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_close_finish (device, task, error);
+}
+
+/**
+ * fp_device_enroll_sync:
+ * @device: a #FpDevice
+ * @template_print: (transfer floating): A #FpPrint to fill in or use
+ * as a template.
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @progress_cb: (nullable) (scope call): progress reporting callback
+ * @progress_data: user data for @progress_cb
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Enroll a new print. See fp_device_enroll(). It is undefined whether
+ * @template_print is updated or a newly created #FpPrint is returned.
+ *
+ * Returns:(transfer full): A #FpPrint on success, %NULL otherwise
+ */
+FpPrint *
+fp_device_enroll_sync (FpDevice *device,
+ FpPrint *template_print,
+ GCancellable *cancellable,
+ FpEnrollProgress progress_cb,
+ gpointer progress_data,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_enroll (device, template_print, cancellable,
+ progress_cb, progress_data, NULL,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_enroll_finish (device, task, error);
+}
+
+/**
+ * fp_device_verify_sync:
+ * @device: a #FpDevice
+ * @enrolled_print: a #FpPrint to verify
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @match: (out): Whether the user presented the correct finger
+ * @print: (out) (transfer full) (nullable): Location to store the scanned print, or %NULL to ignore
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Verify a given print synchronously.
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_verify_sync (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ gboolean *match,
+ FpPrint **print,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_verify (device,
+ enrolled_print,
+ cancellable,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_verify_finish (device, task, match, print, error);
+}
+
+/**
+ * fp_device_identify_sync:
+ * @device: a #FpDevice
+ * @prints: (element-type FpPrint) (transfer none): #GPtrArray of #FpPrint
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @match: (out) (transfer full) (nullable): Location for the matched #FpPrint, or %NULL
+ * @print: (out) (transfer full) (nullable): Location for the new #FpPrint, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Identify a print synchronously.
+ *
+ * Returns: (type void): %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_identify_sync (FpDevice *device,
+ GPtrArray *prints,
+ GCancellable *cancellable,
+ FpPrint **match,
+ FpPrint **print,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_identify (device,
+ prints,
+ cancellable,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_identify_finish (device, task, match, print, error);
+}
+
+
+/**
+ * fp_device_capture_sync:
+ * @device: a #FpDevice
+ * @wait_for_finger: Whether to wait for a finger or not
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Start an synchronous operation to capture an image.
+ *
+ * Returns: (transfer full): A new #FpImage or %NULL on error
+ */
+FpImage *
+fp_device_capture_sync (FpDevice *device,
+ gboolean wait_for_finger,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_capture (device,
+ wait_for_finger,
+ cancellable,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_capture_finish (device, task, error);
+}
+
+/**
+ * fp_device_delete_print_sync:
+ * @device: a #FpDevice
+ * @enrolled_print: a #FpPrint to verify
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Delete a given print from the device.
+ *
+ * Returns: %FALSE on error, %TRUE otherwise
+ */
+gboolean
+fp_device_delete_print_sync (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_delete_print (device,
+ enrolled_print,
+ cancellable,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_delete_print_finish (device, task, error);
+}
+
+/**
+ * fp_device_list_prints_sync:
+ * @device: a #FpDevice
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * List device stored prints synchronously.
+ *
+ * Returns: (element-type FpPrint) (transfer container): Array of prints, or %NULL on error
+ */
+GPtrArray *
+fp_device_list_prints_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GAsyncResult) task = NULL;
+
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ fp_device_list_prints (device,
+ NULL,
+ async_result_ready, &task);
+ while (!task)
+ g_main_context_iteration (NULL, TRUE);
+
+ return fp_device_list_prints_finish (device, task, error);
+}
diff --git a/libfprint/fp-device.h b/libfprint/fp-device.h
new file mode 100644
index 0000000..821514d
--- /dev/null
+++ b/libfprint/fp-device.h
@@ -0,0 +1,255 @@
+/*
+ * FpDevice - A fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#pragma once
+
+#include "fp-image.h"
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define FP_TYPE_DEVICE (fp_device_get_type ())
+#define FP_DEVICE_RETRY (fp_device_retry_quark ())
+#define FP_DEVICE_ERROR (fp_device_error_quark ())
+G_DECLARE_DERIVABLE_TYPE (FpDevice, fp_device, FP, DEVICE, GObject)
+
+#include "fp-print.h"
+
+/* NOTE: We keep the class struct private! */
+
+/**
+ * FpDeviceType:
+ * @FP_DEVICE_TYPE_VIRTUAL: The device is a virtual device
+ * @FP_DEVICE_TYPE_USB: The device is a USB device
+ */
+typedef enum {
+ FP_DEVICE_TYPE_VIRTUAL,
+ FP_DEVICE_TYPE_USB,
+} FpDeviceType;
+
+/**
+ * FpScanType:
+ * @FP_SCAN_TYPE_SWIPE: Sensor requires swiping the finger.
+ * @FP_SCAN_TYPE_PRESS: Sensor requires placing/pressing down the finger.
+ */
+typedef enum {
+ FP_SCAN_TYPE_SWIPE,
+ FP_SCAN_TYPE_PRESS,
+} FpScanType;
+
+/**
+ * FpDeviceRetry:
+ * @FP_DEVICE_RETRY_GENERAL: The scan did not succeed due to poor scan quality
+ * or other general user scanning problem.
+ * @FP_DEVICE_RETRY_TOO_SHORT: The scan did not succeed because the finger
+ * swipe was too short.
+ * @FP_DEVICE_RETRY_CENTER_FINGER: The scan did not succeed because the finger
+ * was not centered on the scanner.
+ * @FP_DEVICE_RETRY_REMOVE_FINGER: The scan did not succeed due to quality or
+ * pressure problems; the user should remove their finger from the scanner
+ * before retrying.
+ *
+ * Error codes representing scan failures resulting in the user needing to
+ * retry.
+ */
+typedef enum {
+ FP_DEVICE_RETRY_GENERAL,
+ FP_DEVICE_RETRY_TOO_SHORT,
+ FP_DEVICE_RETRY_CENTER_FINGER,
+ FP_DEVICE_RETRY_REMOVE_FINGER,
+} FpDeviceRetry;
+
+/**
+ * FpDeviceError:
+ * @FP_DEVICE_ERROR_GENERAL: A general error occured.
+ * @FP_DEVICE_ERROR_NOT_SUPPORTED: The device does not support the requested
+ * operation.
+ * @FP_DEVICE_ERROR_NOT_OPEN: The device needs to be opened to start this
+ * operation.
+ * @FP_DEVICE_ERROR_ALREADY_OPEN: The device has already been opened.
+ * @FP_DEVICE_ERROR_BUSY: The device is busy with another request.
+ * @FP_DEVICE_ERROR_PROTO: Protocol error
+ * @FP_DEVICE_ERROR_DATA_INVALID: The passed data is invalid
+ * @FP_DEVICE_ERROR_DATA_NOT_FOUND: Requested print was not found on device
+ * @FP_DEVICE_ERROR_DATA_FULL: No space on device available for operation
+ *
+ * Error codes for device operations. More specific errors from other domains
+ * such as #G_IO_ERROR or #G_USB_DEVICE_ERROR may also be reported.
+ */
+typedef enum {
+ FP_DEVICE_ERROR_GENERAL,
+ FP_DEVICE_ERROR_NOT_SUPPORTED,
+ FP_DEVICE_ERROR_NOT_OPEN,
+ FP_DEVICE_ERROR_ALREADY_OPEN,
+ FP_DEVICE_ERROR_BUSY,
+ FP_DEVICE_ERROR_PROTO,
+ FP_DEVICE_ERROR_DATA_INVALID,
+ FP_DEVICE_ERROR_DATA_NOT_FOUND,
+ FP_DEVICE_ERROR_DATA_FULL,
+} FpDeviceError;
+
+GQuark fp_device_retry_quark (void);
+GQuark fp_device_error_quark (void);
+
+/**
+ * FpEnrollProgress:
+ * @device: a #FpDevice
+ * @completed_stages: Number of completed stages
+ * @print: (nullable): The last scaned print
+ * @user_data: (nullable): User provided data
+ * @error: (nullable) (transfer none): #GError or %NULL
+ *
+ * The passed error is guaranteed to be of type %FP_DEVICE_RETRY if set.
+ */
+typedef void (*FpEnrollProgress) (FpDevice *device,
+ gint completed_stages,
+ FpPrint *print,
+ gpointer user_data,
+ GError *error);
+
+
+const gchar *fp_device_get_driver (FpDevice *device);
+const gchar *fp_device_get_device_id (FpDevice *device);
+const gchar *fp_device_get_name (FpDevice *device);
+FpScanType fp_device_get_scan_type (FpDevice *device);
+gint fp_device_get_nr_enroll_stages (FpDevice *device);
+
+gboolean fp_device_supports_identify (FpDevice *device);
+gboolean fp_device_supports_capture (FpDevice *device);
+gboolean fp_device_has_storage (FpDevice *device);
+
+/* Opening the device */
+void fp_device_open (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_close (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_enroll (FpDevice *device,
+ FpPrint *template_print,
+ GCancellable *cancellable,
+ FpEnrollProgress progress_cb,
+ gpointer progress_data,
+ GDestroyNotify progress_destroy,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_verify (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_identify (FpDevice *device,
+ GPtrArray *prints,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_capture (FpDevice *device,
+ gboolean wait_for_finger,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_delete_print (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void fp_device_list_prints (FpDevice *device,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean fp_device_open_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+gboolean fp_device_close_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+FpPrint *fp_device_enroll_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+gboolean fp_device_verify_finish (FpDevice *device,
+ GAsyncResult *result,
+ gboolean *match,
+ FpPrint **print,
+ GError **error);
+gboolean fp_device_identify_finish (FpDevice *device,
+ GAsyncResult *result,
+ FpPrint **match,
+ FpPrint **print,
+ GError **error);
+FpImage * fp_device_capture_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+gboolean fp_device_delete_print_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+GPtrArray * fp_device_list_prints_finish (FpDevice *device,
+ GAsyncResult *result,
+ GError **error);
+
+
+gboolean fp_device_open_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error);
+gboolean fp_device_close_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error);
+FpPrint * fp_device_enroll_sync (FpDevice *device,
+ FpPrint *template_print,
+ GCancellable *cancellable,
+ FpEnrollProgress progress_cb,
+ gpointer progress_data,
+ GError **error);
+gboolean fp_device_verify_sync (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ gboolean *match,
+ FpPrint **print,
+ GError **error);
+gboolean fp_device_identify_sync (FpDevice *device,
+ GPtrArray *prints,
+ GCancellable *cancellable,
+ FpPrint **match,
+ FpPrint **print,
+ GError **error);
+FpImage * fp_device_capture_sync (FpDevice *device,
+ gboolean wait_for_finger,
+ GCancellable *cancellable,
+ GError **error);
+gboolean fp_device_delete_print_sync (FpDevice *device,
+ FpPrint *enrolled_print,
+ GCancellable *cancellable,
+ GError **error);
+GPtrArray * fp_device_list_prints_sync (FpDevice *device,
+ GCancellable *cancellable,
+ GError **error);
+
+
+G_END_DECLS
diff --git a/libfprint/fp-image-device.c b/libfprint/fp-image-device.c
new file mode 100644
index 0000000..8524e06
--- /dev/null
+++ b/libfprint/fp-image-device.c
@@ -0,0 +1,795 @@
+/*
+ * FpImageDevice - An image based fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define FP_COMPONENT "image_device"
+#include "fpi-log.h"
+
+#include "fpi-image-device.h"
+#include "fpi-print.h"
+#include "fpi-image.h"
+
+#define MIN_ACCEPTABLE_MINUTIAE 10
+#define BOZORTH3_DEFAULT_THRESHOLD 40
+#define IMG_ENROLL_STAGES 5
+
+/**
+ * SECTION: fp-image-device
+ * @title: FpImageDevice
+ * @short_description: Image device subclass
+ *
+ * This is a helper class for the commonly found image based devices.
+ */
+
+/**
+ * SECTION: fpi-image-device
+ * @title: Internal FpImageDevice
+ * @short_description: Internal image device routines
+ *
+ * See #FpImageDeviceClass for more details. Also see the public
+ * #FpImageDevice routines.
+ */
+
+typedef struct
+{
+ FpImageDeviceState state;
+ gboolean active;
+
+ gint enroll_stage;
+
+ guint pending_activation_timeout_id;
+ gboolean pending_activation_timeout_waiting_finger_off;
+
+ gint bz3_threshold;
+} FpImageDevicePrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpImageDevice, fp_image_device, FP_TYPE_DEVICE)
+
+
+/*******************************************************/
+
+/* TODO:
+ * - sanitize_image seems a bit odd, in particular the sizing stuff.
+ **/
+
+/* Static helper functions */
+
+static void
+fp_image_device_change_state (FpImageDevice *self, FpImageDeviceState state)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
+
+ /* Cannot change to inactive using this function. */
+ g_assert (state != FP_IMAGE_DEVICE_STATE_INACTIVE);
+
+ /* We might have been waiting for the finger to go OFF to start the
+ * next operation. */
+ if (priv->pending_activation_timeout_id)
+ {
+ g_source_remove (priv->pending_activation_timeout_id);
+ priv->pending_activation_timeout_id = 0;
+ }
+
+ fp_dbg ("Image device internal state change from %d to %d\n", priv->state, state);
+
+ priv->state = state;
+
+ /* change_state is the only callback which is optional and does not
+ * have a default implementation. */
+ if (cls->change_state)
+ cls->change_state (self, state);
+}
+
+static void
+fp_image_device_activate (FpImageDevice *self)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
+
+ g_assert (!priv->active);
+
+ /* We don't have a neutral ACTIVE state, but we always will
+ * go into WAIT_FINGER_ON afterwards. */
+ priv->state = FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON;
+
+ /* We might have been waiting for deactivation to finish before
+ * starting the next operation. */
+ if (priv->pending_activation_timeout_id)
+ {
+ g_source_remove (priv->pending_activation_timeout_id);
+ priv->pending_activation_timeout_id = 0;
+ }
+
+ fp_dbg ("Activating image device\n");
+ cls->activate (self);
+}
+
+static void
+fp_image_device_deactivate (FpDevice *device)
+{
+ FpImageDevice *self = FP_IMAGE_DEVICE (device);
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
+
+ if (!priv->active)
+ {
+ /* XXX: We currently deactivate both from minutiae scan result
+ * and finger off report. */
+ fp_dbg ("Already deactivated, ignoring request.");
+ return;
+ }
+ priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
+
+ fp_dbg ("Deactivating image device\n");
+ cls->deactivate (self);
+}
+
+static gboolean
+pending_activation_timeout (gpointer user_data)
+{
+ FpImageDevice *self = FP_IMAGE_DEVICE (user_data);
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+
+ priv->pending_activation_timeout_id = 0;
+
+ if (priv->pending_activation_timeout_waiting_finger_off)
+ fpi_device_action_error (FP_DEVICE (self),
+ fpi_device_retry_new_msg (FP_DEVICE_RETRY_REMOVE_FINGER,
+ "Remove finger before requesting another scan operation"));
+ else
+ fpi_device_action_error (FP_DEVICE (self),
+ fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL));
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Callbacks/vfuncs */
+static void
+fp_image_device_open (FpDevice *device)
+{
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (device);
+
+ /* Nothing special about opening an image device, just
+ * forward the request. */
+ cls->img_open (FP_IMAGE_DEVICE (device));
+}
+
+static void
+fp_image_device_close (FpDevice *device)
+{
+ FpImageDevice *self = FP_IMAGE_DEVICE (device);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+
+ /* In the close case we may need to wait/force deactivation first.
+ * Three possible cases:
+ * 1. We are inactive
+ * -> immediately close
+ * 2. We are waiting for finger off
+ * -> imediately deactivate
+ * 3. We are deactivating
+ * -> handled by deactivate_complete */
+
+ if (!priv->active)
+ cls->img_close (self);
+ else if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE)
+ fp_image_device_deactivate (device);
+}
+
+static void
+fp_image_device_cancel_action (FpDevice *device)
+{
+ FpImageDevice *self = FP_IMAGE_DEVICE (device);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (device);
+
+ /* We can only cancel capture operations, in that case, deactivate and return
+ * an error immediately. */
+ if (action == FP_DEVICE_ACTION_ENROLL ||
+ action == FP_DEVICE_ACTION_VERIFY ||
+ action == FP_DEVICE_ACTION_IDENTIFY ||
+ action == FP_DEVICE_ACTION_CAPTURE)
+ {
+ fp_image_device_deactivate (FP_DEVICE (self));
+
+ /* XXX: Some nicer way of doing this would be good. */
+ fpi_device_action_error (FP_DEVICE (self),
+ g_error_new (G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "Device operation was cancelled"));
+ }
+}
+
+static void
+fp_image_device_start_capture_action (FpDevice *device)
+{
+ FpImageDevice *self = FP_IMAGE_DEVICE (device);
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ /* There is just one action that we cannot support out
+ * of the box, which is a capture without first waiting
+ * for a finger to be on the device.
+ */
+ action = fpi_device_get_current_action (device);
+ if (action == FP_DEVICE_ACTION_CAPTURE)
+ {
+ gboolean wait_for_finger;
+
+ fpi_device_get_capture_data (device, &wait_for_finger);
+
+ if (!wait_for_finger)
+ {
+ fpi_device_action_error (device, fpi_device_error_new (FP_DEVICE_ERROR_NOT_SUPPORTED));
+ return;
+ }
+ }
+ else if (action == FP_DEVICE_ACTION_ENROLL)
+ {
+ FpPrint *enroll_print = NULL;
+
+ fpi_device_get_enroll_data (device, &enroll_print);
+ fpi_print_set_type (enroll_print, FP_PRINT_NBIS);
+ }
+
+ priv->enroll_stage = 0;
+
+ /* The device might still be deactivating from a previous call.
+ * In that situation, try to wait for a bit before reporting back an
+ * error (which will usually say that the user should remove the
+ * finger).
+ */
+ if (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE || priv->active)
+ {
+ g_debug ("Got a new request while the device was still active");
+ g_assert (priv->pending_activation_timeout_id == 0);
+ priv->pending_activation_timeout_id =
+ g_timeout_add (100, pending_activation_timeout, device);
+
+ if (priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
+ priv->pending_activation_timeout_waiting_finger_off = TRUE;
+ else
+ priv->pending_activation_timeout_waiting_finger_off = FALSE;
+
+ return;
+ }
+
+ /* And activate the device; we rely on fpi_image_device_activate_complete()
+ * to be called when done (or immediately). */
+ fp_image_device_activate (self);
+}
+
+
+/*********************************************************/
+
+static void
+fp_image_device_finalize (GObject *object)
+{
+ FpImageDevice *self = (FpImageDevice *) object;
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+
+ g_assert (priv->active == FALSE);
+
+ G_OBJECT_CLASS (fp_image_device_parent_class)->finalize (object);
+}
+
+static void
+fp_image_device_default_activate (FpImageDevice *self)
+{
+ fpi_image_device_activate_complete (self, NULL);
+}
+
+static void
+fp_image_device_default_deactivate (FpImageDevice *self)
+{
+ fpi_image_device_deactivate_complete (self, NULL);
+}
+
+static void
+fp_image_device_class_init (FpImageDeviceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ FpDeviceClass *fp_device_class = FP_DEVICE_CLASS (klass);
+
+ object_class->finalize = fp_image_device_finalize;
+
+ fp_device_class->open = fp_image_device_open;
+ fp_device_class->close = fp_image_device_close;
+ fp_device_class->enroll = fp_image_device_start_capture_action;
+ fp_device_class->verify = fp_image_device_start_capture_action;
+ fp_device_class->identify = fp_image_device_start_capture_action;
+ fp_device_class->capture = fp_image_device_start_capture_action;
+
+ fp_device_class->cancel = fp_image_device_cancel_action;
+
+ /* Default implementations */
+ klass->activate = fp_image_device_default_activate;
+ klass->deactivate = fp_image_device_default_deactivate;
+}
+
+static void
+fp_image_device_init (FpImageDevice *self)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
+
+ /* Set default values. */
+ fpi_device_set_nr_enroll_stages (FP_DEVICE (self), IMG_ENROLL_STAGES);
+
+ priv->bz3_threshold = BOZORTH3_DEFAULT_THRESHOLD;
+ if (cls->bz3_threshold > 0)
+ priv->bz3_threshold = cls->bz3_threshold;
+
+}
+
+static void
+fpi_image_device_minutiae_detected (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ g_autoptr(FpImage) image = FP_IMAGE (source_object);
+ GError *error = NULL;
+ FpPrint *print = NULL;
+ FpDevice *device = FP_DEVICE (user_data);
+ FpImageDevicePrivate *priv;
+ FpDeviceAction action;
+
+ /* Note: We rely on the device to not disappear during an operation. */
+
+ if (!fp_image_detect_minutiae_finish (image, res, &error))
+ {
+ /* Cancel operation . */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ fpi_device_action_error (device, g_steal_pointer (&error));
+ fp_image_device_deactivate (device);
+ return;
+ }
+
+ /* Replace error with a retry condition. */
+ g_warning ("Failed to detect minutiae: %s", error->message);
+ g_clear_pointer (&error, g_error_free);
+
+ error = fpi_device_retry_new_msg (FP_DEVICE_RETRY_GENERAL, "Minutiae detection failed, please retry");
+ }
+
+ priv = fp_image_device_get_instance_private (FP_IMAGE_DEVICE (device));
+ action = fpi_device_get_current_action (device);
+
+ if (action == FP_DEVICE_ACTION_CAPTURE)
+ {
+ fpi_device_capture_complete (device, g_steal_pointer (&image), error);
+ fp_image_device_deactivate (device);
+ return;
+ }
+
+ if (!error)
+ {
+ print = fp_print_new (device);
+ fpi_print_set_type (print, FP_PRINT_NBIS);
+ if (!fpi_print_add_from_image (print, image, &error))
+ g_clear_object (&print);
+ }
+
+ if (action == FP_DEVICE_ACTION_ENROLL)
+ {
+ FpPrint *enroll_print;
+ fpi_device_get_enroll_data (device, &enroll_print);
+
+ if (print)
+ {
+ fpi_print_add_print (enroll_print, print);
+ priv->enroll_stage += 1;
+ }
+
+ fpi_device_enroll_progress (device, priv->enroll_stage, print, error);
+
+ if (priv->enroll_stage == IMG_ENROLL_STAGES)
+ {
+ fpi_device_enroll_complete (device, g_object_ref (enroll_print), NULL);
+ fp_image_device_deactivate (device);
+ }
+ }
+ else if (action == FP_DEVICE_ACTION_VERIFY)
+ {
+ FpPrint *template;
+ FpiMatchResult result;
+
+ fpi_device_get_verify_data (device, &template);
+ if (print)
+ result = fpi_print_bz3_match (template, print, priv->bz3_threshold, &error);
+ else
+ result = FPI_MATCH_ERROR;
+
+ fpi_device_verify_complete (device, result, print, error);
+ fp_image_device_deactivate (device);
+ }
+ else if (action == FP_DEVICE_ACTION_IDENTIFY)
+ {
+ gint i;
+ GPtrArray *templates;
+ FpPrint *result = NULL;
+
+ fpi_device_get_identify_data (device, &templates);
+ for (i = 0; !error && i < templates->len; i++)
+ {
+ FpPrint *template = g_ptr_array_index (templates, i);
+
+ if (fpi_print_bz3_match (template, print, priv->bz3_threshold, &error) == FPI_MATCH_SUCCESS)
+ {
+ result = g_object_ref (template);
+ break;
+ }
+ }
+
+ fpi_device_identify_complete (device, result, print, error);
+ fp_image_device_deactivate (device);
+ }
+ else
+ {
+ /* XXX: This can be hit currently due to a race condition in the enroll code!
+ * In that case we scan a further image even though the minutiae for the previous
+ * one have not yet been detected.
+ * We need to keep track on the pending minutiae detection and the fact that
+ * it will finish eventually (or we may need to retry on error and activate the
+ * device again). */
+ g_assert_not_reached ();
+ }
+}
+
+/*********************************************************/
+/* Private API */
+
+/**
+ * fpi_image_device_set_bz3_threshold:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @bz3_threshold: BZ3 threshold to use
+ *
+ * Dynamically adjust the bz3 threshold. This is only needed for drivers
+ * that support devices with different properties. It should generally be
+ * called from the probe callback, but is acceptable to call from the open
+ * callback.
+ */
+void
+fpi_image_device_set_bz3_threshold (FpImageDevice *self,
+ gint bz3_threshold)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+
+ g_return_if_fail (FP_IS_IMAGE_DEVICE (self));
+ g_return_if_fail (bz3_threshold > 0);
+
+ priv->bz3_threshold = bz3_threshold;
+}
+
+/**
+ * fpi_image_device_report_finger_status:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @present: whether the finger is present on the sensor
+ *
+ * Reports from the driver whether the user's finger is on
+ * the sensor.
+ */
+void
+fpi_image_device_report_finger_status (FpImageDevice *self,
+ gboolean present)
+{
+ FpDevice *device = FP_DEVICE (self);
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (device);
+
+ if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
+ {
+ /* Do we really want to always ignore such reports? We could
+ * also track the state in case the user had the finger on
+ * the device at initialisation time and the driver reports
+ * this early.
+ */
+ g_debug ("Ignoring finger presence report as the device is not active!");
+ return;
+ }
+
+ action = fpi_device_get_current_action (device);
+
+ g_assert (action != FP_DEVICE_ACTION_OPEN);
+ g_assert (action != FP_DEVICE_ACTION_CLOSE);
+
+ g_debug ("Image device reported finger status: %s", present ? "on" : "off");
+
+ if (present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON)
+ {
+ fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_CAPTURE);
+ }
+ else if (!present && priv->state == FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF)
+ {
+ /* We need to deactivate or continue to await finger */
+
+ /* There are three possible situations:
+ * 1. We are deactivating the device and the action is still in progress
+ * (minutiae detection).
+ * 2. We are still deactivating the device after an action completed
+ * 3. We were waiting for finger removal to start the new action
+ * Either way, we always end up deactivating except for the enroll case.
+ * XXX: This is not quite correct though, as we assume we need another finger
+ * scan even though we might be processing the last one (successfully).
+ */
+ if (action != FP_DEVICE_ACTION_ENROLL)
+ fp_image_device_deactivate (device);
+ else
+ fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
+ }
+}
+
+/**
+ * fpi_image_device_image_captured:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @image: whether the finger is present on the sensor
+ *
+ * Reports an image capture. Only use this function if the image was
+ * captured successfully. If there was an issue where the user should
+ * retry, use fpi_image_device_retry_scan() to report the retry condition.
+ *
+ * In the event of a fatal error for the operation use
+ * fpi_image_device_session_error(). This will abort the entire operation
+ * including e.g. an enroll operation which captures multiple images during
+ * one session.
+ */
+void
+fpi_image_device_image_captured (FpImageDevice *self, FpImage *image)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ g_return_if_fail (image != NULL);
+ g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_CAPTURE);
+ g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
+ action == FP_DEVICE_ACTION_VERIFY ||
+ action == FP_DEVICE_ACTION_IDENTIFY ||
+ action == FP_DEVICE_ACTION_CAPTURE);
+
+ fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF);
+
+ g_debug ("Image device captured an image");
+
+ /* XXX: We also detect minutiae in capture mode, we solely do this
+ * to normalize the image which will happen as a by-product. */
+ fp_image_detect_minutiae (image,
+ fpi_device_get_cancellable (FP_DEVICE (self)),
+ fpi_image_device_minutiae_detected,
+ self);
+}
+
+/**
+ * fpi_image_device_retry_scan:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @retry: The #FpDeviceRetry error code to report
+ *
+ * Reports a scan failure to the user. This may or may not abort the
+ * current session. It is the equivalent of fpi_image_device_image_captured()
+ * in the case of a retryable error condition (e.g. short swipe).
+ */
+void
+fpi_image_device_retry_scan (FpImageDevice *self, FpDeviceRetry retry)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+ GError *error;
+
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ /* We might be waiting for a finger at this point, so just accept
+ * all but INACTIVE */
+ g_return_if_fail (priv->state != FP_IMAGE_DEVICE_STATE_INACTIVE);
+ g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
+ action == FP_DEVICE_ACTION_VERIFY ||
+ action == FP_DEVICE_ACTION_IDENTIFY ||
+ action == FP_DEVICE_ACTION_CAPTURE);
+
+ error = fpi_device_retry_new (retry);
+
+ if (action == FP_DEVICE_ACTION_ENROLL)
+ {
+ g_debug ("Reporting retry during enroll");
+ fpi_device_enroll_progress (FP_DEVICE (self), priv->enroll_stage, NULL, error);
+ }
+ else
+ {
+ /* We abort the operation and let the surrounding code retry in the
+ * non-enroll case (this is identical to a session error). */
+ g_debug ("Abort current operation due to retry (non-enroll case)");
+ fp_image_device_deactivate (FP_DEVICE (self));
+ fpi_device_action_error (FP_DEVICE (self), error);
+ }
+}
+
+/**
+ * fpi_image_device_session_error:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @error: The #GError to report
+ *
+ * Report an error while interacting with the device. This effectively
+ * aborts the current ongoing action.
+ */
+void
+fpi_image_device_session_error (FpImageDevice *self, GError *error)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+
+ g_return_if_fail (self);
+
+ if (!error)
+ {
+ g_warning ("Driver did not provide an error, generating a generic one");
+ error = g_error_new (FP_DEVICE_ERROR, FP_DEVICE_ERROR_GENERAL, "Driver reported session error without an error");
+ }
+
+ if (!priv->active)
+ {
+ FpDeviceAction action = fpi_device_get_current_action (FP_DEVICE (self));
+ g_warning ("Driver reported session error, but device is inactive.");
+
+ if (action != FP_DEVICE_ACTION_NONE)
+ {
+ g_warning ("Translating to activation failure!");
+ fpi_image_device_activate_complete (self, error);
+ return;
+ }
+ }
+ else if (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE)
+ {
+ g_warning ("Driver reported session error; translating to deactivation failure.");
+ fpi_image_device_deactivate_complete (self, error);
+ return;
+ }
+
+ if (error->domain == FP_DEVICE_RETRY)
+ g_warning ("Driver should report retries using fpi_image_device_retry_scan!");
+
+ fp_image_device_deactivate (FP_DEVICE (self));
+ fpi_device_action_error (FP_DEVICE (self), error);
+}
+
+/**
+ * fpi_image_device_activate_complete:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @error: A #GError or %NULL on success
+ *
+ * Reports completion of device activation.
+ */
+void
+fpi_image_device_activate_complete (FpImageDevice *self, GError *error)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ g_return_if_fail (priv->active == FALSE);
+ g_return_if_fail (action == FP_DEVICE_ACTION_ENROLL ||
+ action == FP_DEVICE_ACTION_VERIFY ||
+ action == FP_DEVICE_ACTION_IDENTIFY ||
+ action == FP_DEVICE_ACTION_CAPTURE);
+
+ if (error)
+ {
+ g_debug ("Image device activation failed");
+ fpi_device_action_error (FP_DEVICE (self), error);
+ return;
+ }
+
+ g_debug ("Image device activation completed");
+
+ priv->active = TRUE;
+
+ /* We always want to capture at this point, move to AWAIT_FINGER
+ * state. */
+ fp_image_device_change_state (self, FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON);
+}
+
+/**
+ * fpi_image_device_deactivate_complete:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @error: A #GError or %NULL on success
+ *
+ * Reports completion of device deactivation.
+ */
+void
+fpi_image_device_deactivate_complete (FpImageDevice *self, GError *error)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpImageDeviceClass *cls = FP_IMAGE_DEVICE_GET_CLASS (self);
+ FpDeviceAction action;
+
+ g_return_if_fail (priv->active == TRUE);
+ g_return_if_fail (priv->state == FP_IMAGE_DEVICE_STATE_INACTIVE);
+
+ g_debug ("Image device deactivation completed");
+
+ priv->active = FALSE;
+
+ /* Deactivation completed. As we deactivate in the background
+ * there may already be a new task pending. Check whether we
+ * need to do anything. */
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ /* Special case, if we should be closing, but didn't due to a running
+ * deactivation, then do so now. */
+ if (action == FP_DEVICE_ACTION_CLOSE)
+ {
+ cls->img_close (self);
+ return;
+ }
+
+ /* We might be waiting to be able to activate again. */
+ if (priv->pending_activation_timeout_id)
+ fp_image_device_activate (self);
+}
+
+/**
+ * fpi_image_device_open_complete:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @error: A #GError or %NULL on success
+ *
+ * Reports completion of open operation.
+ */
+void
+fpi_image_device_open_complete (FpImageDevice *self, GError *error)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ g_return_if_fail (priv->active == FALSE);
+ g_return_if_fail (action == FP_DEVICE_ACTION_OPEN);
+
+ g_debug ("Image device open completed");
+
+ priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
+
+ fpi_device_open_complete (FP_DEVICE (self), error);
+}
+
+/**
+ * fpi_image_device_close_complete:
+ * @self: a #FpImageDevice imaging fingerprint device
+ * @error: A #GError or %NULL on success
+ *
+ * Reports completion of close operation.
+ */
+void
+fpi_image_device_close_complete (FpImageDevice *self, GError *error)
+{
+ FpImageDevicePrivate *priv = fp_image_device_get_instance_private (self);
+ FpDeviceAction action;
+
+ action = fpi_device_get_current_action (FP_DEVICE (self));
+
+ g_debug ("Image device close completed");
+
+ g_return_if_fail (priv->active == FALSE);
+ g_return_if_fail (action == FP_DEVICE_ACTION_CLOSE);
+
+ priv->state = FP_IMAGE_DEVICE_STATE_INACTIVE;
+
+ fpi_device_close_complete (FP_DEVICE (self), error);
+}
diff --git a/libfprint/fpi-data.h b/libfprint/fp-image-device.h
similarity index 57%
rename from libfprint/fpi-data.h
rename to libfprint/fp-image-device.h
index 37a7911..1eda8fb 100644
--- a/libfprint/fpi-data.h
+++ b/libfprint/fp-image-device.h
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
+ * FpImageDevice - An image based fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,18 +17,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __FPI_DATA_H__
-#define __FPI_DATA_H__
+#pragma once
-struct fp_print_data;
-struct fp_print_data_item {
- size_t length;
- unsigned char data[0];
-};
+#include
-struct fp_print_data *fpi_print_data_new(struct fp_dev *dev);
-struct fp_print_data_item *fpi_print_data_item_new(size_t length);
-struct fp_print_data_item *fpi_print_data_get_item(struct fp_print_data *data);
-void fpi_print_data_add_item(struct fp_print_data *data, struct fp_print_data_item *item);
+G_BEGIN_DECLS
-#endif
+#define FP_TYPE_IMAGE_DEVICE (fp_image_device_get_type ())
+G_DECLARE_DERIVABLE_TYPE (FpImageDevice, fp_image_device, FP, IMAGE_DEVICE, FpDevice)
+
+G_END_DECLS
diff --git a/libfprint/fp-image.c b/libfprint/fp-image.c
new file mode 100644
index 0000000..4b8b3cd
--- /dev/null
+++ b/libfprint/fp-image.c
@@ -0,0 +1,610 @@
+/*
+ * FPrint Image
+ * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "fpi-image.h"
+
+#include "nbis/include/lfs.h"
+
+#if HAVE_PIXMAN
+#include
+#endif
+
+/**
+ * SECTION: fp-image
+ * @title: FpImage
+ * @short_description: Internal Image handling routines
+ *
+ * Some devices will provide the image data corresponding to a print
+ * this object allows accessing this data.
+ */
+
+/**
+ * SECTION: fpi-image
+ * @title: Internal FpImage
+ * @short_description: Internal image handling routines
+ *
+ * Internal image handling routines. Also see the public FpImage routines.
+ */
+
+G_DEFINE_TYPE (FpImage, fp_image, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+FpImage *
+fp_image_new (gint width, gint height)
+{
+ return g_object_new (FP_TYPE_IMAGE,
+ "width", width,
+ "height", height,
+ NULL);
+}
+
+static void
+fp_image_finalize (GObject *object)
+{
+ FpImage *self = (FpImage *) object;
+
+ g_clear_pointer (&self->data, g_free);
+ g_clear_pointer (&self->binarized, g_free);
+ g_clear_pointer (&self->minutiae, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (fp_image_parent_class)->finalize (object);
+}
+
+static void
+fp_image_constructed (GObject *object)
+{
+ FpImage *self = (FpImage *) object;
+
+ self->data = g_malloc0 (self->width * self->height);
+}
+
+static void
+fp_image_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FpImage *self = FP_IMAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_WIDTH:
+ g_value_set_uint (value, self->width);
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_uint (value, self->height);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_image_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FpImage *self = FP_IMAGE (object);
+
+ switch (prop_id)
+ {
+ case PROP_WIDTH:
+ self->width = g_value_get_uint (value);
+ break;
+
+ case PROP_HEIGHT:
+ self->height = g_value_get_uint (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_image_class_init (FpImageClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = fp_image_finalize;
+ object_class->constructed = fp_image_constructed;
+ object_class->set_property = fp_image_set_property;
+ object_class->get_property = fp_image_get_property;
+
+ properties[PROP_WIDTH] =
+ g_param_spec_uint ("width",
+ "Width",
+ "The width of the image",
+ 0,
+ G_MAXUINT16,
+ 0,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_HEIGHT] =
+ g_param_spec_uint ("height",
+ "Height",
+ "The height of the image",
+ 0,
+ G_MAXUINT16,
+ 0,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+fp_image_init (FpImage *self)
+{
+}
+
+typedef struct
+{
+ GAsyncReadyCallback user_cb;
+ struct fp_minutiae *minutiae;
+ gint width, height;
+ gdouble ppmm;
+ FpiImageFlags flags;
+ guchar *image;
+ guchar *binarized;
+} DetectMinutiaeData;
+
+static void
+fp_image_detect_minutiae_free (DetectMinutiaeData *data)
+{
+ g_clear_pointer (&data->image, g_free);
+ g_clear_pointer (&data->minutiae, free_minutiae);
+ g_clear_pointer (&data->binarized, g_free);
+ g_free (data);
+}
+
+static void
+fp_image_detect_minutiae_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (res);
+ FpImage *image;
+ DetectMinutiaeData *data = g_task_get_task_data (task);
+ GCancellable *cancellable;
+
+ cancellable = g_task_get_cancellable (task);
+ if (!cancellable || !g_cancellable_is_cancelled (cancellable))
+ {
+ gint i;
+ image = FP_IMAGE (source_object);
+
+ image->flags = data->flags;
+
+ g_clear_pointer (&image->data, g_free);
+ image->data = g_steal_pointer (&data->image);
+
+ g_clear_pointer (&image->binarized, g_free);
+ image->binarized = g_steal_pointer (&data->binarized);
+
+ g_clear_pointer (&image->minutiae, g_ptr_array_unref);
+ image->minutiae = g_ptr_array_new_full (data->minutiae->num,
+ (GDestroyNotify) free_minutia);
+
+ for (i = 0; i < data->minutiae->num; i++)
+ g_ptr_array_add (image->minutiae,
+ g_steal_pointer (&data->minutiae->list[i]));
+
+ /* Don't let it delete anything. */
+ data->minutiae->num = 0;
+ }
+
+ if (data->user_cb)
+ data->user_cb (source_object, res, user_data);
+}
+
+static void
+vflip (guint8 *data, gint width, gint height)
+{
+ int data_len = width * height;
+ unsigned char rowbuf[width];
+ int i;
+
+ for (i = 0; i < height / 2; i++)
+ {
+ int offset = i * width;
+ int swap_offset = data_len - (width * (i + 1));
+
+ /* copy top row into buffer */
+ memcpy (rowbuf, data + offset, width);
+
+ /* copy lower row over upper row */
+ memcpy (data + offset, data + swap_offset, width);
+
+ /* copy buffer over lower row */
+ memcpy (data + swap_offset, rowbuf, width);
+ }
+}
+
+static void
+hflip (guint8 *data, gint width, gint height)
+{
+ unsigned char rowbuf[width];
+ int i, j;
+
+ for (i = 0; i < height; i++)
+ {
+ int offset = i * width;
+
+ memcpy (rowbuf, data + offset, width);
+ for (j = 0; j < width; j++)
+ data[offset + j] = rowbuf[width - j - 1];
+ }
+}
+
+static void
+invert_colors (guint8 *data, gint width, gint height)
+{
+ int data_len = width * height;
+ int i;
+
+ for (i = 0; i < data_len; i++)
+ data[i] = 0xff - data[i];
+}
+
+static void
+fp_image_detect_minutiae_thread_func (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ g_autoptr(GTimer) timer = NULL;
+ DetectMinutiaeData *data = task_data;
+ struct fp_minutiae *minutiae = NULL;
+ g_autofree gint *direction_map = NULL;
+ g_autofree gint *low_contrast_map = NULL;
+ g_autofree gint *low_flow_map = NULL;
+ g_autofree gint *high_curve_map = NULL;
+ g_autofree gint *quality_map = NULL;
+ g_autofree guchar *bdata = NULL;
+ gint map_w, map_h;
+ gint bw, bh, bd;
+ gint r;
+
+ /* Normalize the image first */
+ if (data->flags & FPI_IMAGE_H_FLIPPED)
+ hflip (data->image, data->width, data->height);
+
+ if (data->flags & FPI_IMAGE_V_FLIPPED)
+ vflip (data->image, data->width, data->height);
+
+ if (data->flags & FPI_IMAGE_COLORS_INVERTED)
+ invert_colors (data->image, data->width, data->height);
+
+ data->flags &= ~(FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED | FPI_IMAGE_COLORS_INVERTED);
+
+ timer = g_timer_new ();
+ r = get_minutiae (&minutiae, &quality_map, &direction_map,
+ &low_contrast_map, &low_flow_map, &high_curve_map,
+ &map_w, &map_h, &bdata, &bw, &bh, &bd,
+ data->image, data->width, data->height, 8,
+ data->ppmm, &g_lfsparms_V2);
+ g_timer_stop (timer);
+ fp_dbg ("Minutiae scan completed in %f secs", g_timer_elapsed (timer, NULL));
+
+ data->binarized = g_steal_pointer (&bdata);
+ data->minutiae = minutiae;
+
+ if (r)
+ {
+ fp_err ("get minutiae failed, code %d", r);
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Minutiae scan failed with code %d", r);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/**
+ * fp_image_get_height:
+ * @self: A #FpImage
+ *
+ * Gets the pixel height of an image.
+ *
+ * Returns: the height of the image
+ */
+guint
+fp_image_get_height (FpImage *self)
+{
+ return self->height;
+}
+
+/**
+ * fp_image_get_width:
+ * @self: A #FpImage
+ *
+ * Gets the pixel width of an image.
+ *
+ * Returns: the width of the image
+ */
+guint
+fp_image_get_width (FpImage *self)
+{
+ return self->width;
+}
+
+/**
+ * fp_image_get_ppmm:
+ * @self: A #FpImage
+ *
+ * Gets the resolution of the image. Note that this is assumed to
+ * be fixed to 500 points per inch (~19.685 p/mm) for most drivers.
+ *
+ * Returns: the resolution of the image in points per millimeter
+ */
+gdouble
+fp_image_get_ppmm (FpImage *self)
+{
+ return self->ppmm;
+}
+
+/**
+ * fp_image_get_data:
+ * @self: A #FpImage
+ * @len: (out) (optional): Return location for length or %NULL
+ *
+ * Gets the greyscale data for an image. This data must not be modified or
+ * freed.
+ *
+ * Returns: (transfer none) (array length=len): The image data
+ */
+const guchar *
+fp_image_get_data (FpImage *self, gsize *len)
+{
+ if (len)
+ *len = self->width * self->height;
+
+ return self->data;
+}
+
+/**
+ * fp_image_get_binarized:
+ * @self: A #FpImage
+ * @len: (out) (optional): Return location for length or %NULL
+ *
+ * Gets the binarized data for an image. This data must not be modified or
+ * freed. You need to first detect the minutiae using
+ * fp_image_detect_minutiae().
+ *
+ * Returns: (transfer none) (array length=len): The binarized image data
+ */
+const guchar *
+fp_image_get_binarized (FpImage *self, gsize *len)
+{
+ if (len && self->binarized)
+ *len = self->width * self->height;
+
+ return self->binarized;
+}
+
+/**
+ * fp_image_get_minutiae:
+ * @self: A #FpImage
+ *
+ * Gets the minutiae for an image. This data must not be modified or
+ * freed. You need to first detect the minutiae using
+ * fp_image_detect_minutiae().
+ *
+ * Returns: (transfer none) (element-type FpMinutia): The detected minutiae
+ */
+GPtrArray *
+fp_image_get_minutiae (FpImage *self)
+{
+ return self->minutiae;
+}
+
+/**
+ * fp_image_detect_minutiae:
+ * @self: A #FpImage
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: the function to call on completion
+ * @user_data: the data to pass to @callback
+ *
+ * Detects the minutiae found in an image.
+ */
+void
+fp_image_detect_minutiae (FpImage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ DetectMinutiaeData *data = g_new0 (DetectMinutiaeData, 1);
+
+ task = g_task_new (self, cancellable, fp_image_detect_minutiae_cb, user_data);
+
+ data->image = g_malloc (self->width * self->height);
+ memcpy (data->image, self->data, self->width * self->height);
+ data->flags = self->flags;
+ data->width = self->width;
+ data->height = self->height;
+ data->ppmm = self->ppmm;
+ data->user_cb = callback;
+
+ g_task_set_task_data (task, data, (GDestroyNotify) fp_image_detect_minutiae_free);
+ g_task_run_in_thread (task, fp_image_detect_minutiae_thread_func);
+}
+
+/**
+ * fp_image_detect_minutiae_finish:
+ * @self: A #FpImage
+ * @result: A #GAsyncResult
+ * @error: Return location for errors, or %NULL to ignore
+ *
+ * Finish minutiae detection in an image
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+fp_image_detect_minutiae_finish (FpImage *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+
+
+/**
+ * fpi_std_sq_dev:
+ * @buf: buffer (usually bitmap, one byte per pixel)
+ * @size: size of @buffer
+ *
+ * Calculates the squared standard deviation of the individual
+ * pixels in the buffer, as per the following formula:
+ * |[
+ * mean = sum (buf[0..size]) / size
+ * sq_dev = sum ((buf[0.size] - mean) ^ 2)
+ * ]|
+ * This function is usually used to determine whether image
+ * is empty.
+ *
+ * Returns: the squared standard deviation for @buffer
+ */
+gint
+fpi_std_sq_dev (const guint8 *buf,
+ gint size)
+{
+ guint64 res = 0, mean = 0;
+ gint i;
+
+ for (i = 0; i < size; i++)
+ mean += buf[i];
+
+ mean /= size;
+
+ for (i = 0; i < size; i++)
+ {
+ int dev = (int) buf[i] - mean;
+ res += dev * dev;
+ }
+
+ return res / size;
+}
+
+/**
+ * fpi_mean_sq_diff_norm:
+ * @buf1: buffer (usually bitmap, one byte per pixel)
+ * @buf2: buffer (usually bitmap, one byte per pixel)
+ * @size: buffer size of smallest buffer
+ *
+ * This function calculates the normalized mean square difference of
+ * two buffers, usually two lines, as per the following formula:
+ * |[
+ * sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
+ * ]|
+ *
+ * This functions is usually used to get numerical difference
+ * between two images.
+ *
+ * Returns: the normalized mean squared difference between @buf1 and @buf2
+ */
+gint
+fpi_mean_sq_diff_norm (const guint8 *buf1,
+ const guint8 *buf2,
+ gint size)
+{
+ int res = 0, i;
+
+ for (i = 0; i < size; i++)
+ {
+ int dev = (int) buf1[i] - (int) buf2[i];
+ res += dev * dev;
+ }
+
+ return res / size;
+}
+
+/**
+ * fp_minutia_get_coords:
+ * @min: A #FpMinutia
+ * @x: (out): x position in image
+ * @y: (out): y position in image
+ *
+ * Returns the coordinates of the found minutia. This is only useful for
+ * debugging purposes and the API is not considered stable for production.
+ */
+void
+fp_minutia_get_coords (FpMinutia *min, gint *x, gint *y)
+{
+ if (x)
+ *x = min->x;
+ if (y)
+ *y = min->y;
+}
+
+#if HAVE_PIXMAN
+FpImage *
+fpi_image_resize (FpImage *orig_img,
+ guint w_factor,
+ guint h_factor)
+{
+ int new_width = orig_img->width * w_factor;
+ int new_height = orig_img->height * h_factor;
+ pixman_image_t *orig, *resized;
+ pixman_transform_t transform;
+ FpImage *newimg;
+
+ orig = pixman_image_create_bits (PIXMAN_a8, orig_img->width, orig_img->height, (uint32_t *) orig_img->data, orig_img->width);
+ resized = pixman_image_create_bits (PIXMAN_a8, new_width, new_height, NULL, new_width);
+
+ pixman_transform_init_identity (&transform);
+ pixman_transform_scale (NULL, &transform, pixman_int_to_fixed (w_factor), pixman_int_to_fixed (h_factor));
+ pixman_image_set_transform (orig, &transform);
+ pixman_image_set_filter (orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ orig, /* src */
+ NULL, /* mask */
+ resized, /* dst */
+ 0, 0, /* src x y */
+ 0, 0, /* mask x y */
+ 0, 0, /* dst x y */
+ new_width, new_height /* width height */
+ );
+
+ newimg = fp_image_new (new_width, new_height);
+ newimg->flags = orig_img->flags;
+
+ memcpy (newimg->data, pixman_image_get_data (resized), new_width * new_height);
+
+ pixman_image_unref (orig);
+ pixman_image_unref (resized);
+
+ return newimg;
+}
+#endif
diff --git a/libfprint/fp-image.h b/libfprint/fp-image.h
new file mode 100644
index 0000000..7de9f1a
--- /dev/null
+++ b/libfprint/fp-image.h
@@ -0,0 +1,59 @@
+/*
+ * FPrint Image
+ * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include
+
+G_BEGIN_DECLS
+
+#define FP_TYPE_IMAGE (fp_image_get_type ())
+
+typedef struct fp_minutia FpMinutia;
+
+G_DECLARE_FINAL_TYPE (FpImage, fp_image, FP, IMAGE, GObject)
+
+FpImage *fp_image_new (gint width,
+ gint height);
+
+guint fp_image_get_width (FpImage *self);
+guint fp_image_get_height (FpImage *self);
+gdouble fp_image_get_ppmm (FpImage *self);
+
+GPtrArray * fp_image_get_minutiae (FpImage *self);
+
+void fp_image_detect_minutiae (FpImage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean fp_image_detect_minutiae_finish (FpImage *self,
+ GAsyncResult *result,
+ GError **error);
+
+const guchar * fp_image_get_data (FpImage *self,
+ gsize *len);
+const guchar * fp_image_get_binarized (FpImage *self,
+ gsize *len);
+
+void fp_minutia_get_coords (FpMinutia *min,
+ gint *x,
+ gint *y);
+
+G_END_DECLS
diff --git a/libfprint/fp-print.c b/libfprint/fp-print.c
new file mode 100644
index 0000000..644370d
--- /dev/null
+++ b/libfprint/fp-print.c
@@ -0,0 +1,1118 @@
+/*
+ * FPrint Print handling
+ * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "fpi-print.h"
+#include "fpi-image.h"
+#include "fpi-device.h"
+
+#include "nbis/include/bozorth.h"
+#include "nbis/include/lfs.h"
+
+/**
+ * SECTION: fp-print
+ * @title: FpPrint
+ * @short_description: Fingerprint handling
+ *
+ * Interaction with prints and their storage.
+ */
+
+/**
+ * SECTION: fpi-print
+ * @title: Internal FpPrint
+ * @short_description: Internal fingerprint handling routines
+ *
+ * Interaction with prints and their storage. See also the public
+ * #FpPrint routines.
+ */
+
+struct _FpPrint
+{
+ GInitiallyUnowned parent_instance;
+
+ FpPrintType type;
+
+ gchar *driver;
+ gchar *device_id;
+ gboolean device_stored;
+
+ FpImage *image;
+
+ /* Metadata */
+ FpFinger finger;
+ gchar *username;
+ gchar *description;
+ GDate *enroll_date;
+
+ GVariant *data;
+ GPtrArray *prints;
+};
+
+G_DEFINE_TYPE (FpPrint, fp_print, G_TYPE_INITIALLY_UNOWNED)
+
+enum {
+ PROP_0,
+ PROP_DRIVER,
+ PROP_DEVICE_ID,
+ PROP_DEVICE_STORED,
+ PROP_IMAGE,
+
+ /* The following is metadata that is stored by default for each print.
+ * Drivers may make use of these during enrollment (e.g. to additionaly store
+ * the metadata on the device). */
+ PROP_FINGER,
+ PROP_USERNAME,
+ PROP_DESCRIPTION,
+ PROP_ENROLL_DATE,
+
+ /* Private property*/
+ PROP_FPI_TYPE,
+ PROP_FPI_DATA,
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+fp_print_finalize (GObject *object)
+{
+ FpPrint *self = (FpPrint *) object;
+
+ g_clear_object (&self->image);
+ g_clear_pointer (&self->device_id, g_free);
+ g_clear_pointer (&self->driver, g_free);
+ g_clear_pointer (&self->username, g_free);
+ g_clear_pointer (&self->description, g_free);
+ g_clear_pointer (&self->enroll_date, g_date_free);
+ g_clear_pointer (&self->data, g_variant_unref);
+
+ G_OBJECT_CLASS (fp_print_parent_class)->finalize (object);
+}
+
+static void
+fp_print_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FpPrint *self = FP_PRINT (object);
+
+ switch (prop_id)
+ {
+ case PROP_DRIVER:
+ g_value_set_string (value, self->driver);
+ break;
+
+ case PROP_DEVICE_ID:
+ g_value_set_string (value, self->device_id);
+ break;
+
+ case PROP_DEVICE_STORED:
+ g_value_set_boolean (value, self->device_stored);
+ break;
+
+ case PROP_IMAGE:
+ g_value_set_object (value, self->image);
+ break;
+
+ case PROP_FINGER:
+ g_value_set_enum (value, self->finger);
+ break;
+
+ case PROP_USERNAME:
+ g_value_set_string (value, self->username);
+ break;
+
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, self->description);
+ break;
+
+ case PROP_ENROLL_DATE:
+ g_value_set_boxed (value, self->enroll_date);
+ break;
+
+ case PROP_FPI_TYPE:
+ g_value_set_enum (value, self->type);
+ break;
+
+ case PROP_FPI_DATA:
+ g_value_set_variant (value, self->data);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_print_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FpPrint *self = FP_PRINT (object);
+
+ switch (prop_id)
+ {
+ case PROP_FPI_TYPE:
+ fpi_print_set_type (self, g_value_get_enum (value));
+ break;
+
+ case PROP_DRIVER:
+ self->driver = g_value_dup_string (value);
+ break;
+
+ case PROP_DEVICE_ID:
+ self->device_id = g_value_dup_string (value);
+ break;
+
+ case PROP_DEVICE_STORED:
+ self->device_stored = g_value_get_boolean (value);
+ break;
+
+ case PROP_FINGER:
+ self->finger = g_value_get_enum (value);
+ break;
+
+ case PROP_USERNAME:
+ g_clear_pointer (&self->username, g_free);
+ self->username = g_value_dup_string (value);
+ break;
+
+ case PROP_DESCRIPTION:
+ g_clear_pointer (&self->description, g_free);
+ self->description = g_value_dup_string (value);
+ break;
+
+ case PROP_ENROLL_DATE:
+ g_clear_pointer (&self->enroll_date, g_date_free);
+ self->enroll_date = g_value_dup_boxed (value);
+ break;
+
+ case PROP_FPI_DATA:
+ g_clear_pointer (&self->description, g_variant_unref);
+ self->data = g_value_dup_variant (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+fp_print_constructed (GObject *obj)
+{
+ FpPrint *self = FP_PRINT (obj);
+
+ g_assert (self->driver != NULL);
+ g_assert (self->device_id != NULL);
+}
+
+static void
+fp_print_class_init (FpPrintClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = fp_print_constructed;
+ object_class->finalize = fp_print_finalize;
+ object_class->get_property = fp_print_get_property;
+ object_class->set_property = fp_print_set_property;
+
+ properties[PROP_DRIVER] =
+ g_param_spec_string ("driver",
+ "Driver",
+ "The name of the driver that created the print",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_DEVICE_ID] =
+ g_param_spec_string ("device-id",
+ "Device ID",
+ "Unique ID allowing to check if a device is compatible with the print",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_DEVICE_STORED] =
+ g_param_spec_boolean ("device-stored",
+ "Device Stored",
+ "Whether the print is a handle for data that is stored on the device",
+ FALSE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_IMAGE] =
+ g_param_spec_object ("image",
+ "Image",
+ "The image that was used for the print, only valid for newly enrolled prints on image based devices",
+ FP_TYPE_IMAGE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+
+ properties[PROP_FINGER] =
+ g_param_spec_enum ("finger",
+ "Finger",
+ "The enrolled finger",
+ FP_TYPE_FINGER,
+ FP_FINGER_UNKNOWN,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ properties[PROP_USERNAME] =
+ g_param_spec_string ("username",
+ "Username",
+ "The username that the enrolled print belongs to",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ properties[PROP_DESCRIPTION] =
+ g_param_spec_string ("description",
+ "Description",
+ "A user defined description for the print",
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ properties[PROP_ENROLL_DATE] =
+ g_param_spec_boxed ("enroll-date",
+ "Enroll Date",
+ "The date of enrollment",
+ G_TYPE_DATE,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ properties[PROP_FPI_TYPE] =
+ g_param_spec_enum ("fp-type",
+ "Type",
+ "Private: The type of the print data",
+ FP_TYPE_PRINT_TYPE,
+ FP_PRINT_RAW,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ properties[PROP_FPI_DATA] =
+ g_param_spec_variant ("fp-data",
+ "Raw Data",
+ "The raw data for internal use only",
+ G_VARIANT_TYPE_ANY,
+ NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+fp_print_init (FpPrint *self)
+{
+}
+
+/**
+ * fp_print_new:
+ * @device: A #FpDevice
+ *
+ * Create a new #FpPrint. This is only useful to prepare an enrollment
+ * of a new print using fp_device_enroll(). For this you should first
+ * create a new print, fill in the relevant metadata, and then start
+ * enrollment.
+ *
+ * Returns: (transfer floating): A newyl created #FpPrint
+ */
+FpPrint *
+fp_print_new (FpDevice *device)
+{
+ g_return_val_if_fail (device, NULL);
+
+ return g_object_new (FP_TYPE_PRINT,
+ "driver", fp_device_get_driver (device),
+ "device-id", fp_device_get_device_id (device),
+ NULL);
+}
+
+/**
+ * fp_print_get_driver:
+ * @print: A #FpPrint
+ *
+ * Returns the driver that the print was created for.
+ *
+ * Returns: (transfer none): The driver
+ */
+const gchar *
+fp_print_get_driver (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->driver;
+}
+
+/**
+ * fp_print_get_device_id:
+ * @print: A #FpPrint
+ *
+ * Returns the device ID that the print was created for.
+ *
+ * Returns: (transfer none): The device ID
+ */
+const gchar *
+fp_print_get_device_id (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->device_id;
+}
+
+/**
+ * fp_print_get_device_stored:
+ * @print: A #FpPrint
+ *
+ * Whether the print is actually stored on the device and this is
+ * just a handle to use that references the device stored data.
+ *
+ * Returns: Whether the print is stored on the device
+ */
+gboolean
+fp_print_get_device_stored (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), FALSE);
+
+ return print->device_stored;
+}
+
+/**
+ * fp_print_get_image:
+ * @print: A #FpPrint
+ *
+ * Returns the image that the print was created from, or %NULL
+ *
+ * Returns: (transfer none) (nullable): The #FpImage
+ */
+FpImage *
+fp_print_get_image (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->image;
+}
+
+/**
+ * fp_print_get_finger:
+ * @print: A #FpPrint
+ *
+ * Returns the finger that the print was created for.
+ *
+ * Returns: The #FpFinger
+ */
+FpFinger
+fp_print_get_finger (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), FP_FINGER_UNKNOWN);
+
+ return print->finger;
+}
+
+/**
+ * fp_print_get_username:
+ * @print: A #FpPrint
+ *
+ * Returns the user defined username for the print.
+ *
+ * Returns: (transfer none) (nullable): The username
+ */
+const gchar *
+fp_print_get_username (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->username;
+}
+
+/**
+ * fp_print_get_description:
+ * @print: A #FpPrint
+ *
+ * Returns the user defined description for the print.
+ *
+ * Returns: (transfer none) (nullable): The description
+ */
+const gchar *
+fp_print_get_description (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->description;
+}
+
+/**
+ * fp_print_get_enroll_date:
+ * @print: A #FpPrint
+ *
+ * Returns the user defined enroll date for the print.
+ *
+ * Returns: (transfer none) (nullable): The #GDate
+ */
+const GDate *
+fp_print_get_enroll_date (FpPrint *print)
+{
+ g_return_val_if_fail (FP_IS_PRINT (print), NULL);
+
+ return print->enroll_date;
+}
+
+/**
+ * fp_print_set_finger:
+ * @print: A #FpPrint
+ * @finger: The #FpFinger
+ *
+ * Set the finger that the print is for.
+ */
+void
+fp_print_set_finger (FpPrint *print,
+ FpFinger finger)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+
+ print->finger = finger;
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_FINGER]);
+}
+
+/**
+ * fp_print_set_username:
+ * @print: A #FpPrint
+ * @username: (transfer none): The new username
+ *
+ * Set the username for the print.
+ */
+void
+fp_print_set_username (FpPrint *print,
+ const gchar *username)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+
+ g_clear_pointer (&print->username, g_free);
+ print->username = g_strdup (username);
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_USERNAME]);
+}
+
+/**
+ * fp_print_set_description:
+ * @print: A #FpPrint
+ * @description: (transfer none): The new description
+ *
+ * Set the description for the print.
+ */
+void
+fp_print_set_description (FpPrint *print,
+ const gchar *description)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+
+ g_clear_pointer (&print->description, g_free);
+ print->description = g_strdup (description);
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_DESCRIPTION]);
+}
+
+/**
+ * fp_print_set_enroll_date:
+ * @print: A #FpPrint
+ * @enroll_date: (transfer none): The new enroll date
+ *
+ * Set the enroll date for the print.
+ */
+void
+fp_print_set_enroll_date (FpPrint *print,
+ const GDate *enroll_date)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+
+ g_clear_pointer (&print->enroll_date, g_date_free);
+ if (enroll_date)
+ {
+ /* XXX: Should use g_date_copy, but that is new in 2.56. */
+ print->enroll_date = g_date_new ();
+ *print->enroll_date = *enroll_date;
+ }
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_ENROLL_DATE]);
+}
+
+
+
+/**
+ * fpi_print_add_print:
+ * @print: A #FpPrint
+ * @add: Print to append to @print
+ *
+ * Appends the single #FP_PRINT_NBIS print from @add to the collection of
+ * prints in @print. Both print objects need to be of type #FP_PRINT_NBIS
+ * for this to work.
+ */
+void
+fpi_print_add_print (FpPrint *print, FpPrint *add)
+{
+ g_return_if_fail (print->type == FP_PRINT_NBIS);
+ g_return_if_fail (add->type == FP_PRINT_NBIS);
+
+ g_assert (add->prints->len == 1);
+ g_ptr_array_add (print->prints, g_memdup (add->prints->pdata[0], sizeof (struct xyt_struct)));
+}
+
+/**
+ * fpi_print_set_type:
+ * @print: A #FpPrint
+ * @type: The newly type of the print data
+ *
+ * This function can only be called exactly once. Drivers should
+ * call it after creating a new print, or to initialize the template
+ * print passed during enrollment.
+ */
+void
+fpi_print_set_type (FpPrint *print,
+ FpPrintType type)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+ /* We only allow setting this once! */
+ g_return_if_fail (print->type == FP_PRINT_UNDEFINED);
+
+ print->type = type;
+ if (print->type == FP_PRINT_NBIS)
+ print->prints = g_ptr_array_new_with_free_func (g_free);
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_FPI_TYPE]);
+}
+
+/**
+ * fpi_print_set_device_stored:
+ * @print: A #FpPrint
+ * @device_stored: Whether the print is stored on the device or not
+ *
+ * Drivers must set this to %TRUE for any print that is really a handle
+ * for data that is stored on the device itself.
+ */
+void
+fpi_print_set_device_stored (FpPrint *print,
+ gboolean device_stored)
+{
+ g_return_if_fail (FP_IS_PRINT (print));
+
+ print->device_stored = device_stored;
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_DEVICE_STORED]);
+}
+
+/* XXX: This is the old version, but wouldn't it be smarter to instead
+ * use the highest quality mintutiae? Possibly just using bz_prune from
+ * upstream? */
+static void
+minutiae_to_xyt (struct fp_minutiae *minutiae,
+ int bwidth,
+ int bheight,
+ struct xyt_struct *xyt)
+{
+ int i;
+ struct fp_minutia *minutia;
+ struct minutiae_struct c[MAX_FILE_MINUTIAE];
+
+ /* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
+ int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE);
+
+ for (i = 0; i < nmin; i++)
+ {
+ minutia = minutiae->list[i];
+
+ lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2],
+ minutia, bwidth, bheight);
+ c[i].col[3] = sround (minutia->reliability * 100.0);
+
+ if (c[i].col[2] > 180)
+ c[i].col[2] -= 360;
+ }
+
+ qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct),
+ sort_x_y);
+
+ for (i = 0; i < nmin; i++)
+ {
+ xyt->xcol[i] = c[i].col[0];
+ xyt->ycol[i] = c[i].col[1];
+ xyt->thetacol[i] = c[i].col[2];
+ }
+ xyt->nrows = nmin;
+}
+
+/**
+ * fpi_print_add_from_image:
+ * @print: A #FpPrint
+ * @image: A #FpImage
+ * @error: Return location for error
+ *
+ * Extracts the minutiae from the given image and adds it to @print of
+ * type #FP_PRINT_NBIS.
+ *
+ * The @image will be kept so that API users can get retrieve it e.g.
+ * for debugging purposes.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+fpi_print_add_from_image (FpPrint *print,
+ FpImage *image,
+ GError **error)
+{
+ GPtrArray *minutiae;
+ struct fp_minutiae _minutiae;
+ struct xyt_struct *xyt;
+
+ if (print->type != FP_PRINT_NBIS || !image)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Cannot add print data from image!");
+ return FALSE;
+ }
+
+ minutiae = fp_image_get_minutiae (image);
+ if (!minutiae || minutiae->len == 0)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "No minutiae found in image or not yet detected!");
+ return FALSE;
+ }
+
+ _minutiae.num = minutiae->len;
+ _minutiae.list = (struct fp_minutia **) minutiae->pdata;
+ _minutiae.alloc = minutiae->len;
+
+ xyt = g_new0 (struct xyt_struct, 1);
+ minutiae_to_xyt (&_minutiae, image->width, image->height, xyt);
+ g_ptr_array_add (print->prints, xyt);
+
+ g_clear_object (&print->image);
+ print->image = g_object_ref (image);
+ g_object_notify_by_pspec (G_OBJECT (print), properties[PROP_IMAGE]);
+
+ return TRUE;
+}
+
+/**
+ * fpi_print_bz3_match:
+ * @template: A #FpPrint containing one or more prints
+ * @print: A newly scanned #FpPrint to test
+ * @bz3_threshold: The BZ3 match threshold
+ * @error: Return location for error
+ *
+ * Match the newly scanned @print (containing exactly one print) against the
+ * prints contained in @template which will have been stored during enrollment.
+ *
+ * Both @template and @print need to be of type #FP_PRINT_NBIS for this to
+ * work.
+ *
+ * Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned
+ */
+FpiMatchResult
+fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GError **error)
+{
+ struct xyt_struct *pstruct;
+ gint probe_len;
+ gint i;
+
+ /* XXX: Use a different error type? */
+ if (template->type != FP_PRINT_NBIS || print->type != FP_PRINT_NBIS)
+ {
+ *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
+ "It is only possible to match NBIS type print data");
+ return FPI_MATCH_ERROR;
+ }
+
+ if (print->prints->len != 1)
+ {
+ *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
+ "New print contains more than one print!");
+ return FPI_MATCH_ERROR;
+ }
+
+ pstruct = g_ptr_array_index (print->prints, 0);
+ probe_len = bozorth_probe_init (pstruct);
+
+ for (i = 0; i < template->prints->len; i++)
+ {
+ struct xyt_struct *gstruct;
+ gint score;
+ gstruct = g_ptr_array_index (template->prints, i);
+ score = bozorth_to_gallery (probe_len, pstruct, gstruct);
+ fp_dbg ("score %d", score);
+
+ if (score >= bz3_threshold)
+ return FPI_MATCH_SUCCESS;
+ }
+
+ return FPI_MATCH_FAIL;
+}
+
+/**
+ * fp_print_compatible:
+ * @self: A #FpPrint
+ * @device: A #FpDevice
+ *
+ * Tests whether the prints is compatible with the given device.
+ *
+ * Returns: %TRUE if the print is compatible with the device
+ */
+gboolean
+fp_print_compatible (FpPrint *self, FpDevice *device)
+{
+ g_return_val_if_fail (FP_IS_PRINT (self), FALSE);
+ g_return_val_if_fail (FP_IS_DEVICE (device), FALSE);
+
+ if (g_strcmp0 (self->driver, fp_device_get_driver (device)))
+ return FALSE;
+
+ if (g_strcmp0 (self->device_id, fp_device_get_device_id (device)))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * fp_print_equal:
+ * @self: First #FpPrint
+ * @other: Second #FpPrint
+ *
+ * Tests whether the prints can be considered equal. This only compares the
+ * actual information about the print, not the metadata.
+ *
+ * Returns: %TRUE if the prints are equal
+ */
+gboolean
+fp_print_equal (FpPrint *self, FpPrint *other)
+{
+ g_return_val_if_fail (FP_IS_PRINT (self), FALSE);
+ g_return_val_if_fail (FP_IS_PRINT (other), FALSE);
+ g_return_val_if_fail (self->type != FP_PRINT_UNDEFINED, FALSE);
+ g_return_val_if_fail (other->type != FP_PRINT_UNDEFINED, FALSE);
+
+ if (self->type != other->type)
+ return FALSE;
+
+ if (g_strcmp0 (self->driver, other->driver))
+ return FALSE;
+
+ if (g_strcmp0 (self->device_id, other->device_id))
+ return FALSE;
+
+ if (self->type == FP_PRINT_RAW)
+ {
+ return g_variant_equal (self->data, other->data);
+ }
+ else if (self->type == FP_PRINT_NBIS)
+ {
+ gint i;
+
+ if (self->prints->len != other->prints->len)
+ return FALSE;
+
+ for (i = 0; i < self->prints->len; i++)
+ {
+ struct xyt_struct *a = g_ptr_array_index (self->prints, i);
+ struct xyt_struct *b = g_ptr_array_index (other->prints, i);
+
+ if (memcmp (a, b, sizeof (struct xyt_struct)) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+}
+
+#define FP_PRINT_VARIANT_TYPE G_VARIANT_TYPE ("(issbymsmsia{sv}v)")
+
+G_STATIC_ASSERT (sizeof (((struct xyt_struct *) NULL)->xcol[0]) == 4);
+
+/**
+ * fp_print_serialize:
+ * @print: A #FpPrint
+ * @data: (array length=length) (transfer full) (out): Return location for data pointer
+ * @length: (transfer full) (out): Length of @data
+ * @error: Return location for error
+ *
+ * Serialize a print definition for permanent storage. Note that this is
+ * lossy in the sense that e.g. the image data is discarded.
+ *
+ * Returns: (type void): %TRUE on success
+ */
+gboolean
+fp_print_serialize (FpPrint *print,
+ guchar **data,
+ gsize *length,
+ GError **error)
+{
+ g_autoptr(GVariant) result = NULL;
+ GVariantBuilder builder = G_VARIANT_BUILDER_INIT (FP_PRINT_VARIANT_TYPE);
+ gsize len;
+
+ g_assert (data);
+ g_assert (length);
+
+ g_variant_builder_add (&builder, "i", print->type);
+ g_variant_builder_add (&builder, "s", print->driver);
+ g_variant_builder_add (&builder, "s", print->device_id);
+ g_variant_builder_add (&builder, "b", print->device_stored);
+
+ /* Metadata */
+ g_variant_builder_add (&builder, "y", print->finger);
+ g_variant_builder_add (&builder, "ms", print->username);
+ g_variant_builder_add (&builder, "ms", print->description);
+ if (print->enroll_date && g_date_valid (print->enroll_date))
+ g_variant_builder_add (&builder, "i", g_date_get_julian (print->enroll_date));
+ else
+ g_variant_builder_add (&builder, "i", G_MININT32);
+
+ /* Unused a{sv} for expansion */
+ g_variant_builder_open (&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_close (&builder);
+
+ /* Insert NBIS print data for type NBIS, otherwise the GVariant directly */
+ if (print->type == FP_PRINT_NBIS)
+ {
+ GVariantBuilder nested = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a(aiaiai))"));
+ gint i;
+
+ g_variant_builder_open (&nested, G_VARIANT_TYPE ("a(aiaiai)"));
+ for (i = 0; i < print->prints->len; i++)
+ {
+ struct xyt_struct *xyt = g_ptr_array_index (print->prints, i);
+ gint j;
+ gint32 *col = g_new (gint32, xyt->nrows);
+
+ g_variant_builder_open (&nested, G_VARIANT_TYPE ("(aiaiai)"));
+
+ for (j = 0; j < xyt->nrows; j++)
+ col[j] = GINT32_TO_LE (xyt->xcol[j]);
+ g_variant_builder_add_value (&nested,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
+ col,
+ xyt->nrows,
+ sizeof (col[0])));
+
+ for (j = 0; j < xyt->nrows; j++)
+ col[j] = GINT32_TO_LE (xyt->ycol[j]);
+ g_variant_builder_add_value (&nested,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
+ col,
+ xyt->nrows,
+ sizeof (col[0])));
+
+ for (j = 0; j < xyt->nrows; j++)
+ col[j] = GINT32_TO_LE (xyt->thetacol[j]);
+ g_variant_builder_add_value (&nested,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
+ col,
+ xyt->nrows,
+ sizeof (col[0])));
+ g_variant_builder_close (&nested);
+ }
+
+ g_variant_builder_close (&nested);
+ g_variant_builder_add (&builder, "v", g_variant_builder_end (&nested));
+ }
+ else
+ {
+ g_variant_builder_add (&builder, "v", g_variant_new_variant (print->data));
+ }
+
+ result = g_variant_builder_end (&builder);
+
+ if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ {
+ GVariant *tmp;
+ tmp = g_variant_byteswap (result);
+ g_variant_unref (result);
+ result = tmp;
+ }
+
+ len = g_variant_get_size (result);
+ /* Add 3 bytes of header */
+ len += 3;
+
+ *data = g_malloc (len);
+ *length = len;
+
+ (*data)[0] = (guchar) 'F';
+ (*data)[1] = (guchar) 'P';
+ (*data)[2] = (guchar) '3';
+
+ g_variant_get_data (result);
+ g_variant_store (result, (*data) + 3);
+
+ return TRUE;
+}
+
+/**
+ * fp_print_deserialize:
+ * @data: (array length=length): The binary data
+ * @length: Length of the data
+ * @error: Return location for error
+ *
+ * Deserialize a print definition from permanent storage.
+ *
+ * Returns: (transfer full): A newly created #FpPrint on success
+ */
+FpPrint *
+fp_print_deserialize (const guchar *data,
+ gsize length,
+ GError **error)
+{
+ g_autoptr(FpPrint) result = NULL;
+ g_autoptr(GVariant) raw_value = NULL;
+ g_autoptr(GVariant) value = NULL;
+ guchar *aligned_data = NULL;
+ GDate *date = NULL;
+ guint8 finger_int8;
+ FpFinger finger;
+ g_autofree gchar *username = NULL;
+ g_autofree gchar *description = NULL;
+ gint julian_date;
+ FpPrintType type;
+ const gchar *driver;
+ const gchar *device_id;
+ gboolean device_stored;
+ GVariant *print_data;
+
+ g_assert (data);
+ g_assert (length > 3);
+
+ if (memcmp (data, "FP3", 3) != 0)
+ goto invalid_format;
+
+ /* NOTE:
+ * We make sure that we have no variant left over from the parsing at the end
+ * of this function (meaning we don't need to keep the data around.
+ */
+
+ /* To support GLIB < 2.60 we need to make sure that the memory is aligned correctly.
+ * We also need to copy the backing store for the raw data that we may keep for
+ * longer. */
+ aligned_data = g_malloc (length - 3);
+ memcpy (aligned_data, data + 3, length - 3);
+ raw_value = g_variant_new_from_data (FP_PRINT_VARIANT_TYPE,
+ aligned_data, length - 3,
+ FALSE, g_free, NULL);
+
+ if (!raw_value)
+ goto invalid_format;
+
+ if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ value = g_variant_byteswap (raw_value);
+ else
+ value = g_variant_get_normal_form (raw_value);
+
+ g_variant_get (value,
+ "(issbymsmsi@a{sv}v)",
+ &type,
+ &driver,
+ &device_id,
+ &device_stored,
+ &finger_int8,
+ &username,
+ &description,
+ &julian_date,
+ NULL,
+ &print_data);
+
+ finger = finger_int8;
+
+ /* Assume data is valid at this point if the values are somewhat sane. */
+ if (type == FP_PRINT_NBIS)
+ {
+ g_autoptr(GVariant) prints = g_variant_get_child_value (print_data, 0);
+ gint i;
+
+ result = g_object_new (FP_TYPE_PRINT,
+ "driver", driver,
+ "device-id", device_id,
+ "device-stored", device_stored,
+ NULL);
+ fpi_print_set_type (result, FP_PRINT_NBIS);
+ for (i = 0; i < g_variant_n_children (prints); i++)
+ {
+ struct xyt_struct *xyt = g_new0 (struct xyt_struct, 1);
+ const gint32 *xcol, *ycol, *thetacol;
+ gsize xlen, ylen, thetalen;
+ g_autoptr(GVariant) xyt_data = NULL;
+ GVariant *child;
+
+ xyt_data = g_variant_get_child_value (prints, i);
+
+ child = g_variant_get_child_value (xyt_data, 0);
+ xcol = g_variant_get_fixed_array (child, &xlen, sizeof (gint32));
+ g_variant_unref (child);
+
+ child = g_variant_get_child_value (xyt_data, 1);
+ ycol = g_variant_get_fixed_array (child, &ylen, sizeof (gint32));
+ g_variant_unref (child);
+
+ child = g_variant_get_child_value (xyt_data, 2);
+ thetacol = g_variant_get_fixed_array (child, &thetalen, sizeof (gint32));
+ g_variant_unref (child);
+
+ if (xlen != ylen || xlen != thetalen)
+ goto invalid_format;
+
+ if (xlen > G_N_ELEMENTS (xyt->xcol))
+ goto invalid_format;
+
+ xyt->nrows = xlen;
+ memcpy (xyt->xcol, xcol, sizeof (xcol[0]) * xlen);
+ memcpy (xyt->ycol, ycol, sizeof (xcol[0]) * xlen);
+ memcpy (xyt->thetacol, thetacol, sizeof (xcol[0]) * xlen);
+
+ g_ptr_array_add (result->prints, xyt);
+ }
+ }
+ else if (type == FP_PRINT_RAW)
+ {
+ g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0);
+
+ result = g_object_new (FP_TYPE_PRINT,
+ "fp-type", type,
+ "driver", driver,
+ "device-id", device_id,
+ "device-stored", device_stored,
+ "fp-data", fp_data,
+ NULL);
+ }
+ else
+ {
+ g_warning ("Invalid print type: 0x%X", type);
+ goto invalid_format;
+ }
+
+ date = g_date_new_julian (julian_date);
+ g_object_set (result,
+ "finger", finger,
+ "username", username,
+ "description", description,
+ "enroll_date", date,
+ NULL);
+
+ g_date_free (date);
+
+ return g_steal_pointer (&result);
+
+invalid_format:
+ *error = g_error_new_literal (G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Data could not be parsed");
+ return FALSE;
+}
diff --git a/libfprint/fp-print.h b/libfprint/fp-print.h
new file mode 100644
index 0000000..fcb9532
--- /dev/null
+++ b/libfprint/fp-print.h
@@ -0,0 +1,101 @@
+/*
+ * FPrint Print handling
+ * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "fp-image.h"
+#include "fp-enums.h"
+
+G_BEGIN_DECLS
+
+#define FP_TYPE_PRINT (fp_print_get_type ())
+G_DECLARE_FINAL_TYPE (FpPrint, fp_print, FP, PRINT, GInitiallyUnowned)
+
+#include "fp-device.h"
+
+/**
+ * FpFinger:
+ * @FP_FINGER_UNKNOWN: The finger is unknown
+ * @FP_FINGER_LEFT_THUMB: Left thumb
+ * @FP_FINGER_LEFT_INDEX: Left index finger
+ * @FP_FINGER_LEFT_MIDDLE: Left middle finger
+ * @FP_FINGER_LEFT_RING: Left ring finger
+ * @FP_FINGER_LEFT_LITTLE: Left little finger
+ * @FP_FINGER_RIGHT_THUMB: Right thumb
+ * @FP_FINGER_RIGHT_INDEX: Right index finger
+ * @FP_FINGER_RIGHT_MIDDLE: Right middle finger
+ * @FP_FINGER_RIGHT_RING: Right ring finger
+ * @FP_FINGER_RIGHT_LITTLE: Right little finger
+ */
+typedef enum {
+ FP_FINGER_UNKNOWN = 0,
+ FP_FINGER_LEFT_THUMB,
+ FP_FINGER_LEFT_INDEX,
+ FP_FINGER_LEFT_MIDDLE,
+ FP_FINGER_LEFT_RING,
+ FP_FINGER_LEFT_LITTLE,
+ FP_FINGER_RIGHT_THUMB,
+ FP_FINGER_RIGHT_INDEX,
+ FP_FINGER_RIGHT_MIDDLE,
+ FP_FINGER_RIGHT_RING,
+ FP_FINGER_RIGHT_LITTLE,
+} FpFinger;
+
+FpPrint *fp_print_new (FpDevice *device);
+
+FpPrint *fp_print_new_from_data (guchar *data,
+ gsize length);
+gboolean fp_print_to_data (guchar **data,
+ gsize length);
+
+const gchar *fp_print_get_driver (FpPrint *print);
+const gchar *fp_print_get_device_id (FpPrint *print);
+FpImage *fp_print_get_image (FpPrint *print);
+
+FpFinger fp_print_get_finger (FpPrint *print);
+const gchar *fp_print_get_username (FpPrint *print);
+const gchar *fp_print_get_description (FpPrint *print);
+const GDate *fp_print_get_enroll_date (FpPrint *print);
+gboolean fp_print_get_device_stored (FpPrint *print);
+
+void fp_print_set_finger (FpPrint *print,
+ FpFinger finger);
+void fp_print_set_username (FpPrint *print,
+ const gchar *username);
+void fp_print_set_description (FpPrint *print,
+ const gchar *description);
+void fp_print_set_enroll_date (FpPrint *print,
+ const GDate *enroll_date);
+
+gboolean fp_print_compatible (FpPrint *self,
+ FpDevice *device);
+gboolean fp_print_equal (FpPrint *self,
+ FpPrint *other);
+
+gboolean fp_print_serialize (FpPrint *print,
+ guchar **data,
+ gsize *length,
+ GError **error);
+
+FpPrint *fp_print_deserialize (const guchar *data,
+ gsize length,
+ GError **error);
+
+G_END_DECLS
diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h
index 7861032..0f4df4a 100644
--- a/libfprint/fp_internal.h
+++ b/libfprint/fp_internal.h
@@ -20,147 +20,10 @@
#ifndef __FPRINT_INTERNAL_H__
#define __FPRINT_INTERNAL_H__
-#include
-
-#include
-#include
-#include
-#include
-
-#include "nbis-helpers.h"
-#include "fprint.h"
-#include "fpi-dev.h"
-#include "fpi-core.h"
#include "fpi-log.h"
-#include "fpi-dev-img.h"
-#include "fpi-data.h"
-#include "fpi-img.h"
-#include "drivers/driver_ids.h"
-
-/* Global variables */
-extern libusb_context *fpi_usb_ctx;
-extern GSList *opened_devices;
-
-/* fp_print_data structure definition */
-enum fp_print_data_type {
- PRINT_DATA_RAW = 0, /* memset-imposed default */
- PRINT_DATA_NBIS_MINUTIAE
-};
-
-struct fp_print_data {
- uint16_t driver_id;
- uint32_t devtype;
- enum fp_print_data_type type;
- GSList *prints;
-};
-
-/* fp_dev structure definition */
-enum fp_dev_state {
- DEV_STATE_INITIAL = 0,
- DEV_STATE_ERROR,
- DEV_STATE_INITIALIZING,
- DEV_STATE_INITIALIZED,
- DEV_STATE_DEINITIALIZING,
- DEV_STATE_DEINITIALIZED,
- DEV_STATE_ENROLL_STARTING,
- DEV_STATE_ENROLLING,
- DEV_STATE_ENROLL_STOPPING,
- DEV_STATE_VERIFY_STARTING,
- DEV_STATE_VERIFYING,
- DEV_STATE_VERIFY_DONE,
- DEV_STATE_VERIFY_STOPPING,
- DEV_STATE_IDENTIFY_STARTING,
- DEV_STATE_IDENTIFYING,
- DEV_STATE_IDENTIFY_DONE,
- DEV_STATE_IDENTIFY_STOPPING,
- DEV_STATE_CAPTURE_STARTING,
- DEV_STATE_CAPTURING,
- DEV_STATE_CAPTURE_DONE,
- DEV_STATE_CAPTURE_STOPPING,
-};
-
-struct fp_dev {
- struct fp_driver *drv;
- uint32_t devtype;
-
- /* only valid if drv->type == DRIVER_IMAGING */
- struct fp_img_dev *img_dev;
- /* Link to the instance specific struct */
- void *instance_data;
-
- int nr_enroll_stages;
-
- /* FIXME: This will eventually have a bus type */
- libusb_device_handle *udev;
-
- /* read-only to drivers */
- struct fp_print_data *verify_data;
-
- /* drivers should not mess with any of the below */
- enum fp_dev_state state;
- int __enroll_stage;
- int unconditional_capture;
-
- /* async I/O callbacks and data */
- /* FIXME: convert this to generic state operational data mechanism? */
- fp_dev_open_cb open_cb;
- void *open_cb_data;
- fp_operation_stop_cb close_cb;
- void *close_cb_data;
- fp_enroll_stage_cb enroll_stage_cb;
- void *enroll_stage_cb_data;
- fp_operation_stop_cb enroll_stop_cb;
- void *enroll_stop_cb_data;
- fp_img_operation_cb verify_cb;
- void *verify_cb_data;
- fp_operation_stop_cb verify_stop_cb;
- void *verify_stop_cb_data;
- fp_identify_cb identify_cb;
- void *identify_cb_data;
- fp_operation_stop_cb identify_stop_cb;
- void *identify_stop_cb_data;
- fp_img_operation_cb capture_cb;
- void *capture_cb_data;
- fp_operation_stop_cb capture_stop_cb;
- void *capture_stop_cb_data;
-
- /* FIXME: better place to put this? */
- struct fp_print_data **identify_gallery;
-};
-
-/* fp_img_dev structure definition */
-struct fp_img_dev {
- struct fp_dev *parent;
-
- enum fp_imgdev_action action;
- int action_state;
-
- struct fp_print_data *acquire_data;
- struct fp_print_data *enroll_data;
- struct fp_img *acquire_img;
- int enroll_stage;
- int action_result;
-
- /* FIXME: better place to put this? */
- size_t identify_match_offset;
-};
-
-/* fp_driver structure definition */
-
-/* fp_img_driver structure definition */
-#define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-#define fpi_driver_to_img_driver(drv) \
- container_of((drv), struct fp_img_driver, driver)
-
-/* fp_dscv_dev structure definition */
-struct fp_dscv_dev {
- struct libusb_device *udev;
- struct fp_driver *drv;
- unsigned long driver_data;
- uint32_t devtype;
-};
+#include "nbis-helpers.h"
+#include "fpi-image.h"
+#include "fpi-image-device.h"
/* fp_minutia structure definition */
struct fp_minutia {
@@ -185,48 +48,5 @@ struct fp_minutiae {
struct fp_minutia **list;
};
-/* Defined in fpi-dev-img.c */
-void fpi_img_driver_setup(struct fp_img_driver *idriver);
-int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
-int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
-
-/* Exported for use in command-line tools
- * Defined in fpi-core.c */
-struct fp_driver **fprint_get_drivers (void);
-
-/* Defined in fpi-core.c */
-enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
-
-/* Defined in fpi-data.c */
-gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
- enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
- enum fp_print_data_type type2);
-
-/* Defined in fpi-img.c */
-gboolean fpi_img_is_sane(struct fp_img *img);
-int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
- struct fp_print_data **ret);
-int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
- struct fp_print_data *new_print);
-int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
- struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
-
-/* Defined in fpi-poll.c */
-void fpi_timeout_cancel_all_for_dev(struct fp_dev *dev);
-void fpi_poll_init(void);
-void fpi_poll_exit(void);
-
-/* Defined in fpi-async.c */
-void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
-void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
- struct fp_img *img);
-void fpi_drvcb_capture_stopped(struct fp_dev *dev);
-
-void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
-void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
- size_t match_offset, struct fp_img *img);
-void fpi_drvcb_identify_stopped(struct fp_dev *dev);
-
-#include "drivers_definitions.h"
#endif
diff --git a/libfprint/fpi-assembling.c b/libfprint/fpi-assembling.c
index 65fad4d..b6403cb 100644
--- a/libfprint/fpi-assembling.c
+++ b/libfprint/fpi-assembling.c
@@ -23,12 +23,8 @@
#include "fp_internal.h"
-#include
#include
-#include
-#include
-
#include "fpi-assembling.h"
/**
@@ -42,125 +38,141 @@
* data in small stripes.
*/
-static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
- struct fpi_frame *first_frame,
- struct fpi_frame *second_frame,
- int dx,
- int dy)
+static unsigned int
+calc_error (struct fpi_frame_asmbl_ctx *ctx,
+ struct fpi_frame *first_frame,
+ struct fpi_frame *second_frame,
+ int dx,
+ int dy)
{
- unsigned int width, height;
- unsigned int x1, y1, x2, y2, err, i, j;
+ unsigned int width, height;
+ unsigned int x1, y1, x2, y2, err, i, j;
- width = ctx->frame_width - (dx > 0 ? dx : -dx);
- height = ctx->frame_height - dy;
+ width = ctx->frame_width - (dx > 0 ? dx : -dx);
+ height = ctx->frame_height - dy;
- y1 = 0;
- y2 = dy;
- i = 0;
- err = 0;
- do {
- x1 = dx < 0 ? 0 : dx;
- x2 = dx < 0 ? -dx : 0;
- j = 0;
+ y1 = 0;
+ y2 = dy;
+ i = 0;
+ err = 0;
+ do
+ {
+ x1 = dx < 0 ? 0 : dx;
+ x2 = dx < 0 ? -dx : 0;
+ j = 0;
- do {
- unsigned char v1, v2;
+ do
+ {
+ unsigned char v1, v2;
- v1 = ctx->get_pixel(ctx, first_frame, x1, y1);
- v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
- err += v1 > v2 ? v1 - v2 : v2 - v1;
- j++;
- x1++;
- x2++;
+ v1 = ctx->get_pixel (ctx, first_frame, x1, y1);
+ v2 = ctx->get_pixel (ctx, second_frame, x2, y2);
+ err += v1 > v2 ? v1 - v2 : v2 - v1;
+ j++;
+ x1++;
+ x2++;
- } while (j < width);
- i++;
- y1++;
- y2++;
- } while (i < height);
+ }
+ while (j < width);
+ i++;
+ y1++;
+ y2++;
+ }
+ while (i < height);
- /* Normalize error */
- err *= (ctx->frame_height * ctx->frame_width);
- err /= (height * width);
+ /* Normalize error */
+ err *= (ctx->frame_height * ctx->frame_width);
+ err /= (height * width);
- if (err == 0)
- return INT_MAX;
+ if (err == 0)
+ return INT_MAX;
- return err;
+ return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
-static void find_overlap(struct fpi_frame_asmbl_ctx *ctx,
- struct fpi_frame *first_frame,
- struct fpi_frame *second_frame,
- unsigned int *min_error)
+static void
+find_overlap (struct fpi_frame_asmbl_ctx *ctx,
+ struct fpi_frame *first_frame,
+ struct fpi_frame *second_frame,
+ unsigned int *min_error)
{
- int dx, dy;
- unsigned int err;
- *min_error = 255 * ctx->frame_height * ctx->frame_width;
+ int dx, dy;
+ unsigned int err;
- /* Seeking in horizontal and vertical dimensions,
- * for horizontal dimension we'll check only 8 pixels
- * in both directions. For vertical direction diff is
- * rarely less than 2, so start with it.
- */
- for (dy = 2; dy < ctx->frame_height; dy++) {
- for (dx = -8; dx < 8; dx++) {
- err = calc_error(ctx, first_frame, second_frame,
- dx, dy);
- if (err < *min_error) {
- *min_error = err;
- second_frame->delta_x = -dx;
- second_frame->delta_y = dy;
- }
- }
- }
+ *min_error = 255 * ctx->frame_height * ctx->frame_width;
+
+ /* Seeking in horizontal and vertical dimensions,
+ * for horizontal dimension we'll check only 8 pixels
+ * in both directions. For vertical direction diff is
+ * rarely less than 2, so start with it.
+ */
+ for (dy = 2; dy < ctx->frame_height; dy++)
+ {
+ for (dx = -8; dx < 8; dx++)
+ {
+ err = calc_error (ctx, first_frame, second_frame,
+ dx, dy);
+ if (err < *min_error)
+ {
+ *min_error = err;
+ second_frame->delta_x = -dx;
+ second_frame->delta_y = dy;
+ }
+ }
+ }
}
-static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
- GSList *stripes, size_t num_stripes,
- gboolean reverse)
+static unsigned int
+do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
+ GSList *stripes, size_t num_stripes,
+ gboolean reverse)
{
- GSList *list_entry = stripes;
- GTimer *timer;
- int frame = 1;
- struct fpi_frame *prev_stripe = list_entry->data;
- unsigned int min_error;
- /* Max error is width * height * 255, for AES2501 which has the largest
- * sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
- * we might get int overflow. Use 64bit value here to prevent integer overflow
- */
- unsigned long long total_error = 0;
+ GSList *list_entry = stripes;
+ GTimer *timer;
+ int frame = 1;
+ struct fpi_frame *prev_stripe = list_entry->data;
+ unsigned int min_error;
+ /* Max error is width * height * 255, for AES2501 which has the largest
+ * sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
+ * we might get int overflow. Use 64bit value here to prevent integer overflow
+ */
+ unsigned long long total_error = 0;
- list_entry = g_slist_next(list_entry);
+ list_entry = g_slist_next (list_entry);
- timer = g_timer_new();
- do {
- struct fpi_frame *cur_stripe = list_entry->data;
+ timer = g_timer_new ();
+ do
+ {
+ struct fpi_frame *cur_stripe = list_entry->data;
- if (reverse) {
- find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
- cur_stripe->delta_y = -cur_stripe->delta_y;
- cur_stripe->delta_x = -cur_stripe->delta_x;
- }
- else
- find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
- total_error += min_error;
+ if (reverse)
+ {
+ find_overlap (ctx, prev_stripe, cur_stripe, &min_error);
+ cur_stripe->delta_y = -cur_stripe->delta_y;
+ cur_stripe->delta_x = -cur_stripe->delta_x;
+ }
+ else
+ {
+ find_overlap (ctx, cur_stripe, prev_stripe, &min_error);
+ }
+ total_error += min_error;
- frame++;
- prev_stripe = cur_stripe;
- list_entry = g_slist_next(list_entry);
+ frame++;
+ prev_stripe = cur_stripe;
+ list_entry = g_slist_next (list_entry);
- } while (frame < num_stripes);
+ }
+ while (frame < num_stripes);
- g_timer_stop(timer);
- fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
- g_timer_destroy(timer);
+ g_timer_stop (timer);
+ fp_dbg ("calc delta completed in %f secs", g_timer_elapsed (timer, NULL));
+ g_timer_destroy (timer);
- return total_error / num_stripes;
+ return total_error / num_stripes;
}
/**
@@ -179,77 +191,88 @@ static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
* Note that @num_stripes might be shorter than the length of the list,
* if some stripes should be skipped.
*/
-void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
- GSList *stripes, size_t num_stripes)
+void
+fpi_do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
+ GSList *stripes, size_t num_stripes)
{
- int err, rev_err;
- err = do_movement_estimation(ctx, stripes, num_stripes, FALSE);
- rev_err = do_movement_estimation(ctx, stripes, num_stripes, TRUE);
- fp_dbg("errors: %d rev: %d", err, rev_err);
- if (err < rev_err) {
- do_movement_estimation(ctx, stripes, num_stripes, FALSE);
- }
+ int err, rev_err;
+
+ err = do_movement_estimation (ctx, stripes, num_stripes, FALSE);
+ rev_err = do_movement_estimation (ctx, stripes, num_stripes, TRUE);
+ fp_dbg ("errors: %d rev: %d", err, rev_err);
+ if (err < rev_err)
+ do_movement_estimation (ctx, stripes, num_stripes, FALSE);
}
-static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
- struct fp_img *img,
- struct fpi_frame *stripe,
- int x, int y)
+static inline void
+aes_blit_stripe (struct fpi_frame_asmbl_ctx *ctx,
+ FpImage *img,
+ struct fpi_frame *stripe,
+ int x, int y)
{
- unsigned int ix, iy;
- unsigned int fx, fy;
- unsigned int width, height;
+ unsigned int ix, iy;
+ unsigned int fx, fy;
+ unsigned int width, height;
- /* Find intersection */
- if (x < 0) {
- width = ctx->frame_width + x;
- ix = 0;
- fx = -x;
- } else {
- ix = x;
- fx = 0;
- width = ctx->frame_width;
- }
- if ((ix + width) > img->width)
- width = img->width - ix;
+ /* Find intersection */
+ if (x < 0)
+ {
+ width = ctx->frame_width + x;
+ ix = 0;
+ fx = -x;
+ }
+ else
+ {
+ ix = x;
+ fx = 0;
+ width = ctx->frame_width;
+ }
+ if ((ix + width) > img->width)
+ width = img->width - ix;
- if (y < 0) {
- iy = 0;
- fy = -y;
- height = ctx->frame_height + y;
- } else {
- iy = y;
- fy = 0;
- height = ctx->frame_height;
- }
+ if (y < 0)
+ {
+ iy = 0;
+ fy = -y;
+ height = ctx->frame_height + y;
+ }
+ else
+ {
+ iy = y;
+ fy = 0;
+ height = ctx->frame_height;
+ }
- if (fx > ctx->frame_width)
- return;
+ if (fx > ctx->frame_width)
+ return;
- if (fy > ctx->frame_height)
- return;
+ if (fy > ctx->frame_height)
+ return;
- if (ix > img->width)
- return;
+ if (ix > img->width)
+ return;
- if (iy > img->height)
- return;
+ if (iy > img->height)
+ return;
- if ((iy + height) > img->height)
- height = img->height - iy;
+ if ((iy + height) > img->height)
+ height = img->height - iy;
- for (; fy < height; fy++, iy++) {
- if (x < 0) {
- ix = 0;
- fx = -x;
- } else {
- ix = x;
- fx = 0;
- }
- for (; fx < width; fx++, ix++) {
- img->data[ix + (iy * img->width)] = ctx->get_pixel(ctx, stripe, fx, fy);
- }
- }
+ for (; fy < height; fy++, iy++)
+ {
+ if (x < 0)
+ {
+ ix = 0;
+ fx = -x;
+ }
+ else
+ {
+ ix = x;
+ fx = 0;
+ }
+ for (; fx < width; fx++, ix++)
+ img->data[ix + (iy * img->width)] = ctx->get_pixel (ctx, stripe, fx, fy);
+ }
}
/**
@@ -266,130 +289,145 @@ static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
*
* Returns: a newly allocated #fp_img.
*/
-struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
- GSList *stripes, size_t num_stripes)
+FpImage *
+fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
+ GSList *stripes, size_t num_stripes)
{
- GSList *stripe;
- struct fp_img *img;
- int height = 0;
- int i, y, x;
- gboolean reverse = FALSE;
- struct fpi_frame *fpi_frame;
+ GSList *stripe;
+ FpImage *img;
+ int height = 0;
+ int i, y, x;
+ gboolean reverse = FALSE;
+ struct fpi_frame *fpi_frame;
- //FIXME g_return_if_fail
- BUG_ON(num_stripes == 0);
- BUG_ON(ctx->image_width < ctx->frame_width);
+ //FIXME g_return_if_fail
+ BUG_ON (num_stripes == 0);
+ BUG_ON (ctx->image_width < ctx->frame_width);
- /* Calculate height */
- i = 0;
- stripe = stripes;
+ /* Calculate height */
+ i = 0;
+ stripe = stripes;
- /* No offset for 1st image */
- fpi_frame = stripe->data;
- fpi_frame->delta_x = 0;
- fpi_frame->delta_y = 0;
- do {
- fpi_frame = stripe->data;
+ /* No offset for 1st image */
+ fpi_frame = stripe->data;
+ fpi_frame->delta_x = 0;
+ fpi_frame->delta_y = 0;
+ do
+ {
+ fpi_frame = stripe->data;
- height += fpi_frame->delta_y;
- i++;
- stripe = g_slist_next(stripe);
- } while (i < num_stripes);
+ height += fpi_frame->delta_y;
+ i++;
+ stripe = g_slist_next (stripe);
+ }
+ while (i < num_stripes);
- fp_dbg("height is %d", height);
+ fp_dbg ("height is %d", height);
- if (height < 0) {
- reverse = TRUE;
- height = -height;
- }
+ if (height < 0)
+ {
+ reverse = TRUE;
+ height = -height;
+ }
- /* For last frame */
- height += ctx->frame_height;
+ /* For last frame */
+ height += ctx->frame_height;
- /* Create buffer big enough for max image */
- img = fpi_img_new(ctx->image_width * height);
- img->flags = FP_IMG_COLORS_INVERTED;
- img->flags |= reverse ? 0 : FP_IMG_H_FLIPPED | FP_IMG_V_FLIPPED;
- img->width = ctx->image_width;
- img->height = height;
+ /* Create buffer big enough for max image */
+ img = fp_image_new (ctx->image_width, height);
+ img->flags = FPI_IMAGE_COLORS_INVERTED;
+ img->flags |= reverse ? 0 : FPI_IMAGE_H_FLIPPED | FPI_IMAGE_V_FLIPPED;
+ img->width = ctx->image_width;
+ img->height = height;
- /* Assemble stripes */
- i = 0;
- stripe = stripes;
- y = reverse ? (height - ctx->frame_height) : 0;
- x = (ctx->image_width - ctx->frame_width) / 2;
+ /* Assemble stripes */
+ i = 0;
+ stripe = stripes;
+ y = reverse ? (height - ctx->frame_height) : 0;
+ x = (ctx->image_width - ctx->frame_width) / 2;
- do {
- fpi_frame = stripe->data;
+ do
+ {
+ fpi_frame = stripe->data;
- if(reverse) {
- y += fpi_frame->delta_y;
- x += fpi_frame->delta_x;
- }
+ if(reverse)
+ {
+ y += fpi_frame->delta_y;
+ x += fpi_frame->delta_x;
+ }
- aes_blit_stripe(ctx, img, fpi_frame, x, y);
+ aes_blit_stripe (ctx, img, fpi_frame, x, y);
- if(!reverse) {
- y += fpi_frame->delta_y;
- x += fpi_frame->delta_x;
- }
+ if(!reverse)
+ {
+ y += fpi_frame->delta_y;
+ x += fpi_frame->delta_x;
+ }
- stripe = g_slist_next(stripe);
- i++;
- } while (i < num_stripes);
+ stripe = g_slist_next (stripe);
+ i++;
+ }
+ while (i < num_stripes);
- return img;
+ return img;
}
-static int cmpint(const void *p1, const void *p2, gpointer data)
+static int
+cmpint (const void *p1, const void *p2, gpointer data)
{
- int a = *((int *)p1);
- int b = *((int *)p2);
- if (a < b)
- return -1;
- else if (a == b)
- return 0;
- else
- return 1;
+ int a = *((int *) p1);
+ int b = *((int *) p2);
+
+ if (a < b)
+ return -1;
+ else if (a == b)
+ return 0;
+ else
+ return 1;
}
-static void median_filter(int *data, int size, int filtersize)
+static void
+median_filter (int *data, int size, int filtersize)
{
- int i;
- int *result = (int *)g_malloc0(size*sizeof(int));
- int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
- for (i = 0; i < size; i++) {
- int i1 = i - (filtersize-1)/2;
- int i2 = i + (filtersize-1)/2;
- if (i1 < 0)
- i1 = 0;
- if (i2 >= size)
- i2 = size-1;
- memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
- g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
- result[i] = sortbuf[(i2-i1+1)/2];
- }
- memmove(data, result, size*sizeof(int));
- g_free(result);
- g_free(sortbuf);
+ int i;
+ int *result = (int *) g_malloc0 (size * sizeof (int));
+ int *sortbuf = (int *) g_malloc0 (filtersize * sizeof (int));
+
+ for (i = 0; i < size; i++)
+ {
+ int i1 = i - (filtersize - 1) / 2;
+ int i2 = i + (filtersize - 1) / 2;
+ if (i1 < 0)
+ i1 = 0;
+ if (i2 >= size)
+ i2 = size - 1;
+ memmove (sortbuf, data + i1, (i2 - i1 + 1) * sizeof (int));
+ g_qsort_with_data (sortbuf, i2 - i1 + 1, sizeof (int), cmpint, NULL);
+ result[i] = sortbuf[(i2 - i1 + 1) / 2];
+ }
+ memmove (data, result, size * sizeof (int));
+ g_free (result);
+ g_free (sortbuf);
}
-static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
- GSList *line1, float y1, GSList *line2,
- float y2, unsigned char *output, float yi, int size)
+static void
+interpolate_lines (struct fpi_line_asmbl_ctx *ctx,
+ GSList *line1, float y1, GSList *line2,
+ float y2, unsigned char *output, float yi, int size)
{
- int i;
- unsigned char p1, p2;
+ int i;
+ unsigned char p1, p2;
- if (!line1 || !line2)
- return;
+ if (!line1 || !line2)
+ return;
- for (i = 0; i < size; i++) {
- p1 = ctx->get_pixel(ctx, line1, i);
- p2 = ctx->get_pixel(ctx, line2, i);
- output[i] = (float)p1
- + (yi - y1)/(y2 - y1)*(p2 - p1);
- }
+ for (i = 0; i < size; i++)
+ {
+ p1 = ctx->get_pixel (ctx, line1, i);
+ p2 = ctx->get_pixel (ctx, line2, i);
+ output[i] = (float) p1
+ + (yi - y1) / (y2 - y1) * (p2 - p1);
+ }
}
/**
@@ -406,82 +444,89 @@ static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
*
* Returns: a newly allocated #fp_img.
*/
-struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
- GSList *lines, size_t num_lines)
+FpImage *
+fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
+ GSList *lines, size_t num_lines)
{
- /* Number of output lines per distance between two scanners */
- int i;
- GSList *row1, *row2;
- float y = 0.0;
- int line_ind = 0;
- int *offsets = (int *)g_malloc0((num_lines / 2) * sizeof(int));
- unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
- struct fp_img *img;
+ /* Number of output lines per distance between two scanners */
+ int i;
+ GSList *row1, *row2;
+ float y = 0.0;
+ int line_ind = 0;
+ int *offsets = (int *) g_malloc0 ((num_lines / 2) * sizeof (int));
+ unsigned char *output = g_malloc0 (ctx->line_width * ctx->max_height);
+ FpImage *img;
- g_return_val_if_fail (lines != NULL, NULL);
- g_return_val_if_fail (num_lines >= 2, NULL);
+ g_return_val_if_fail (lines != NULL, NULL);
+ g_return_val_if_fail (num_lines >= 2, NULL);
- fp_dbg("%"G_GINT64_FORMAT, g_get_real_time());
+ fp_dbg ("%"G_GINT64_FORMAT, g_get_real_time ());
- row1 = lines;
- for (i = 0; (i < num_lines - 1) && row1; i += 2) {
- int bestmatch = i;
- int bestdiff = 0;
- int j, firstrow, lastrow;
+ row1 = lines;
+ for (i = 0; (i < num_lines - 1) && row1; i += 2)
+ {
+ int bestmatch = i;
+ int bestdiff = 0;
+ int j, firstrow, lastrow;
- firstrow = i + 1;
- lastrow = MIN(i + ctx->max_search_offset, num_lines - 1);
+ firstrow = i + 1;
+ lastrow = MIN (i + ctx->max_search_offset, num_lines - 1);
- row2 = g_slist_next(row1);
- for (j = firstrow; j <= lastrow; j++) {
- int diff = ctx->get_deviation(ctx,
- row1,
- row2);
- if ((j == firstrow) || (diff < bestdiff)) {
- bestdiff = diff;
- bestmatch = j;
- }
- row2 = g_slist_next(row2);
- }
- offsets[i / 2] = bestmatch - i;
- fp_dbg("%d", offsets[i / 2]);
- row1 = g_slist_next(row1);
- if (row1)
- row1 = g_slist_next(row1);
- }
+ row2 = g_slist_next (row1);
+ for (j = firstrow; j <= lastrow; j++)
+ {
+ int diff = ctx->get_deviation (ctx,
+ row1,
+ row2);
+ if ((j == firstrow) || (diff < bestdiff))
+ {
+ bestdiff = diff;
+ bestmatch = j;
+ }
+ row2 = g_slist_next (row2);
+ }
+ offsets[i / 2] = bestmatch - i;
+ fp_dbg ("%d", offsets[i / 2]);
+ row1 = g_slist_next (row1);
+ if (row1)
+ row1 = g_slist_next (row1);
+ }
- median_filter(offsets, (num_lines / 2) - 1, ctx->median_filter_size);
+ median_filter (offsets, (num_lines / 2) - 1, ctx->median_filter_size);
- fp_dbg("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time());
- for (i = 0; i <= (num_lines / 2) - 1; i++)
- fp_dbg("%d", offsets[i]);
- row1 = lines;
- for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next(row1)) {
- int offset = offsets[i/2];
- if (offset > 0) {
- float ynext = y + (float)ctx->resolution / offset;
- while (line_ind < ynext) {
- if (line_ind > ctx->max_height - 1)
- goto out;
- interpolate_lines(ctx,
- row1, y,
- g_slist_next(row1),
- ynext,
- output + line_ind * ctx->line_width,
- line_ind,
- ctx->line_width);
- line_ind++;
- }
- y = ynext;
- }
- }
+ fp_dbg ("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time ());
+ for (i = 0; i <= (num_lines / 2) - 1; i++)
+ fp_dbg ("%d", offsets[i]);
+ row1 = lines;
+ for (i = 0; i < num_lines - 1; i++, row1 = g_slist_next (row1))
+ {
+ int offset = offsets[i / 2];
+ if (offset > 0)
+ {
+ float ynext = y + (float) ctx->resolution / offset;
+ while (line_ind < ynext)
+ {
+ if (line_ind > ctx->max_height - 1)
+ goto out;
+ interpolate_lines (ctx,
+ row1, y,
+ g_slist_next (row1),
+ ynext,
+ output + line_ind * ctx->line_width,
+ line_ind,
+ ctx->line_width);
+ line_ind++;
+ }
+ y = ynext;
+ }
+ }
out:
- img = fpi_img_new(ctx->line_width * line_ind);
- img->height = line_ind;
- img->width = ctx->line_width;
- img->flags = FP_IMG_V_FLIPPED;
- memmove(img->data, output, ctx->line_width * line_ind);
- g_free(offsets);
- g_free(output);
- return img;
+ img = fp_image_new (ctx->line_width, line_ind);
+ img->height = line_ind;
+ img->width = ctx->line_width;
+ img->flags = FPI_IMAGE_V_FLIPPED;
+ memmove (img->data, output, ctx->line_width * line_ind);
+ g_free (offsets);
+ g_free (output);
+ return img;
}
diff --git a/libfprint/fpi-assembling.h b/libfprint/fpi-assembling.h
index d2a6651..ae9adc2 100644
--- a/libfprint/fpi-assembling.h
+++ b/libfprint/fpi-assembling.h
@@ -32,10 +32,11 @@
* populate delta_x and delta_y if the device supports hardware movement
* estimation.
*/
-struct fpi_frame {
- int delta_x;
- int delta_y;
- unsigned char data[0];
+struct fpi_frame
+{
+ int delta_x;
+ int delta_y;
+ unsigned char data[0];
};
/**
@@ -52,21 +53,24 @@ struct fpi_frame {
* hardware parameters of scanner. @image_width is usually 25% wider than
* @frame_width to take horizontal movement into account.
*/
-struct fpi_frame_asmbl_ctx {
- unsigned int frame_width;
- unsigned int frame_height;
- unsigned int image_width;
- unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
- struct fpi_frame *frame,
- unsigned int x,
- unsigned int y);
+struct fpi_frame_asmbl_ctx
+{
+ unsigned int frame_width;
+ unsigned int frame_height;
+ unsigned int image_width;
+ unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
+ struct fpi_frame *frame,
+ unsigned int x,
+ unsigned int y);
};
-void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
- GSList *stripes, size_t num_stripes);
+void fpi_do_movement_estimation (struct fpi_frame_asmbl_ctx *ctx,
+ GSList *stripes,
+ size_t num_stripes);
-struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
- GSList *stripes, size_t num_stripes);
+FpImage *fpi_assemble_frames (struct fpi_frame_asmbl_ctx *ctx,
+ GSList *stripes,
+ size_t num_stripes);
/**
* fpi_line_asmbl_ctx:
@@ -96,20 +100,23 @@ struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
* returns two lines at a time, this function should be used to estimate the
* difference between pairs of lines.
*/
-struct fpi_line_asmbl_ctx {
- unsigned int line_width;
- unsigned int max_height;
- unsigned int resolution;
- unsigned int median_filter_size;
- unsigned int max_search_offset;
- int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
- GSList *line1, GSList *line2);
- unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
- GSList *line,
- unsigned int x);
+struct fpi_line_asmbl_ctx
+{
+ unsigned int line_width;
+ unsigned int max_height;
+ unsigned int resolution;
+ unsigned int median_filter_size;
+ unsigned int max_search_offset;
+ int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
+ GSList *line1,
+ GSList *line2);
+ unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
+ GSList *line,
+ unsigned int x);
};
-struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
- GSList *lines, size_t num_lines);
+FpImage *fpi_assemble_lines (struct fpi_line_asmbl_ctx *ctx,
+ GSList *lines,
+ size_t num_lines);
#endif
diff --git a/libfprint/fpi-async.c b/libfprint/fpi-async.c
deleted file mode 100644
index 6339c9c..0000000
--- a/libfprint/fpi-async.c
+++ /dev/null
@@ -1,706 +0,0 @@
-/*
- * Asynchronous I/O functionality
- * Copyright (C) 2008 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define FP_COMPONENT "async"
-
-#include "fp_internal.h"
-#include "fpi-async.h"
-
-#include
-#include
-#include
-
-/*
- * SECTION:fpi-async
- * @title: Asynchronous operations reporting
- * @short_description: Asynchronous operations reporting functions
- *
- * Those functions are used by primitive drivers to report back their
- * current status. Most drivers, imaging ones, do not need to use them.
- */
-
-/* Drivers call this when device initialisation has completed */
-void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
-{
- fp_dbg("status %d", status);
- BUG_ON(dev->state != DEV_STATE_INITIALIZING);
- dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_INITIALIZED;
- opened_devices = g_slist_prepend(opened_devices, dev);
- if (dev->open_cb)
- dev->open_cb(dev, status, dev->open_cb_data);
-}
-
-/**
- * fp_async_dev_open:
- * @ddev: the struct #fp_dscv_dev discovered device to open
- * @callback: the callback to call when the device has been opened
- * @user_data: user data to pass to the callback
- *
- * Opens and initialises a device. This is the function you call in order
- * to convert a #fp_dscv_dev discovered device into an actual device handle
- * that you can perform operations with.
- *
- * The error status of the opening will be provided as an argument to the
- * #fp_dev_open_cb callback.
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
- void *user_data)
-{
- struct fp_driver *drv;
- struct fp_dev *dev;
- libusb_device_handle *udevh;
- int r;
-
- g_return_val_if_fail(ddev != NULL, -ENODEV);
- g_return_val_if_fail (callback != NULL, -EINVAL);
-
- drv = ddev->drv;
-
- G_DEBUG_HERE();
- r = libusb_open(ddev->udev, &udevh);
- if (r < 0) {
- fp_err("usb_open failed, error %d", r);
- return r;
- }
-
- dev = g_malloc0(sizeof(*dev));
- dev->drv = drv;
- dev->udev = udevh;
- dev->__enroll_stage = -1;
- dev->state = DEV_STATE_INITIALIZING;
- dev->open_cb = callback;
- dev->open_cb_data = user_data;
-
- if (!drv->open) {
- fpi_drvcb_open_complete(dev, 0);
- return 0;
- }
-
- dev->state = DEV_STATE_INITIALIZING;
- r = drv->open(dev, ddev->driver_data);
- if (r) {
- fp_err("device initialisation failed, driver=%s", drv->name);
- libusb_close(udevh);
- g_free(dev);
- }
-
- return r;
-}
-
-/* Drivers call this when device deinitialisation has completed */
-void fpi_drvcb_close_complete(struct fp_dev *dev)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
- dev->state = DEV_STATE_DEINITIALIZED;
- fpi_timeout_cancel_all_for_dev(dev);
- libusb_close(dev->udev);
- if (dev->close_cb)
- dev->close_cb(dev, dev->close_cb_data);
- g_free(dev);
-}
-
-/**
- * fp_async_dev_close:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call when the device has been closed
- * @user_data: user data to pass to the callback
- *
- * Closes a device. You must call this function when you have finished using
- * a fingerprint device.
- */
-API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
- fp_operation_stop_cb callback, void *user_data)
-{
- struct fp_driver *drv;
-
- g_return_if_fail (dev != NULL);
-
- drv = dev->drv;
-
- g_return_if_fail (drv->close != NULL);
-
- if (g_slist_index(opened_devices, (gconstpointer) dev) == -1)
- fp_err("device %p not in opened list!", dev);
- opened_devices = g_slist_remove(opened_devices, (gconstpointer) dev);
-
- dev->close_cb = callback;
- dev->close_cb_data = user_data;
- dev->state = DEV_STATE_DEINITIALIZING;
- drv->close(dev);
-}
-
-/* Drivers call this when enrollment has started */
-void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
-{
- fp_dbg("status %d", status);
- BUG_ON(dev->state != DEV_STATE_ENROLL_STARTING);
- if (status) {
- if (status > 0) {
- status = -status;
- fp_dbg("adjusted to %d", status);
- }
- dev->state = DEV_STATE_ERROR;
- if (dev->enroll_stage_cb)
- dev->enroll_stage_cb(dev, status, NULL, NULL,
- dev->enroll_stage_cb_data);
- } else {
- dev->state = DEV_STATE_ENROLLING;
- }
-}
-
-/**
- * fp_async_enroll_start:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call for each stage of the enrollment
- * @user_data: user data to pass to the callback
- *
- * Starts an enrollment and calls @callback for each enrollment stage.
- * See [Enrolling](libfprint-Devices-operations.html#enrolling)
- * for an explanation of enroll stages.
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
- fp_enroll_stage_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
- g_return_val_if_fail (callback != NULL, -EINVAL);
-
- drv = dev->drv;
-
- if (!dev->nr_enroll_stages || !drv->enroll_start) {
- fp_err("driver %s has 0 enroll stages or no enroll func",
- drv->name);
- return -ENOTSUP;
- }
-
- fp_dbg("starting enrollment");
- dev->enroll_stage_cb = callback;
- dev->enroll_stage_cb_data = user_data;
-
- dev->state = DEV_STATE_ENROLL_STARTING;
- r = drv->enroll_start(dev);
- if (r < 0) {
- dev->enroll_stage_cb = NULL;
- fp_err("failed to start enrollment");
- dev->state = DEV_STATE_ERROR;
- }
-
- return r;
-}
-
-/* Drivers call this when an enroll stage has completed */
-void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
- struct fp_print_data *data, struct fp_img *img)
-{
- BUG_ON(dev->state != DEV_STATE_ENROLLING);
- fp_dbg("result %d", result);
- if (!dev->enroll_stage_cb) {
- fp_dbg("ignoring enroll result as no callback is subscribed");
- return;
- }
- if (result == FP_ENROLL_COMPLETE && !data) {
- fp_err("BUG: complete but no data?");
- result = FP_ENROLL_FAIL;
- }
- dev->enroll_stage_cb(dev, result, data, img, dev->enroll_stage_cb_data);
-}
-
-/* Drivers call this when enrollment has stopped */
-void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
- dev->state = DEV_STATE_INITIALIZED;
- if (dev->enroll_stop_cb)
- dev->enroll_stop_cb(dev, dev->enroll_stop_cb_data);
-}
-
-/**
- * fp_async_enroll_stop:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call when the enrollment has been cancelled
- * @user_data: user data to pass to the callback
- *
- * Stops an ongoing enrollment started with fp_async_enroll_start().
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
- fp_operation_stop_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
-
- drv = dev->drv;
-
- G_DEBUG_HERE();
- if (!drv->enroll_start)
- return -ENOTSUP;
-
- dev->enroll_stage_cb = NULL;
- dev->enroll_stop_cb = callback;
- dev->enroll_stop_cb_data = user_data;
- dev->state = DEV_STATE_ENROLL_STOPPING;
-
- if (!drv->enroll_stop) {
- fpi_drvcb_enroll_stopped(dev);
- return 0;
- }
-
- r = drv->enroll_stop(dev);
- if (r < 0) {
- fp_err("failed to stop enrollment");
- dev->enroll_stop_cb = NULL;
- }
-
- return r;
-}
-
-/**
- * fp_async_verify_start:
- * @dev: the struct #fp_dev device
- * @data: the print to verify against. Must have been previously
- * enrolled with a device compatible to the device selected to perform the scan
- * @callback: the callback to call when the verification has finished
- * @user_data: user data to pass to the callback
- *
- * Starts a verification and calls @callback when the verification has
- * finished. See fp_verify_finger_img() for the synchronous API. When the
- * @callback has been called, you must call fp_async_verify_stop().
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
- struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
- g_return_val_if_fail (callback != NULL, -EINVAL);
-
- drv = dev->drv;
-
- G_DEBUG_HERE();
- if (!drv->verify_start)
- return -ENOTSUP;
-
- dev->state = DEV_STATE_VERIFY_STARTING;
- dev->verify_cb = callback;
- dev->verify_cb_data = user_data;
- dev->verify_data = data;
-
- r = drv->verify_start(dev);
- if (r < 0) {
- dev->verify_cb = NULL;
- dev->state = DEV_STATE_ERROR;
- fp_err("failed to start verification, error %d", r);
- }
- return r;
-}
-
-/* Drivers call this when verification has started */
-void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
- if (status) {
- if (status > 0) {
- status = -status;
- fp_dbg("adjusted to %d", status);
- }
- dev->state = DEV_STATE_ERROR;
- if (dev->verify_cb)
- dev->verify_cb(dev, status, NULL, dev->verify_cb_data);
- } else {
- dev->state = DEV_STATE_VERIFYING;
- }
-}
-
-/* Drivers call this to report a verify result (which might mark completion) */
-void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
- struct fp_img *img)
-{
- fp_dbg("result %d", result);
- BUG_ON(dev->state != DEV_STATE_VERIFYING);
- if (result < 0 || result == FP_VERIFY_NO_MATCH
- || result == FP_VERIFY_MATCH)
- dev->state = DEV_STATE_VERIFY_DONE;
-
- if (dev->verify_cb)
- dev->verify_cb(dev, result, img, dev->verify_cb_data);
- else
- fp_dbg("ignoring verify result as no callback is subscribed");
-}
-
-/* Drivers call this when verification has stopped */
-void fpi_drvcb_verify_stopped(struct fp_dev *dev)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
- dev->state = DEV_STATE_INITIALIZED;
- if (dev->verify_stop_cb)
- dev->verify_stop_cb(dev, dev->verify_stop_cb_data);
-}
-
-/**
- * fp_async_verify_stop:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call to finish a verification
- * @user_data: user data to pass to the callback
- *
- * Finishes an ongoing verification started with fp_async_verify_start().
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
- fp_operation_stop_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
-
- G_DEBUG_HERE();
-
- if (dev->state == DEV_STATE_VERIFY_STOPPING) {
- fp_dbg ("Already stopping verification, returning -EINPROGRESS");
- return -EINPROGRESS;
- }
-
- if (dev->state == DEV_STATE_INITIALIZED) {
- if (callback)
- callback(dev, user_data);
- return 0;
- }
-
- drv = dev->drv;
-
- BUG_ON(dev->state != DEV_STATE_ERROR
- && dev->state != DEV_STATE_VERIFYING
- && dev->state != DEV_STATE_VERIFY_DONE);
-
- dev->verify_cb = NULL;
- dev->verify_stop_cb = callback;
- dev->verify_stop_cb_data = user_data;
- dev->state = DEV_STATE_VERIFY_STOPPING;
-
- if (!drv->verify_start)
- return -ENOTSUP;
- if (!drv->verify_stop) {
- dev->state = DEV_STATE_INITIALIZED;
- fpi_drvcb_verify_stopped(dev);
- return 0;
- }
-
- r = drv->verify_stop(dev, iterating);
- if (r < 0) {
- fp_err("failed to stop verification");
- dev->verify_stop_cb = NULL;
- }
- return r;
-}
-
-/**
- * fp_async_identify_start:
- * @dev: the struct #fp_dev device
- * @gallery: NULL-terminated array of pointers to the prints to
- * identify against. Each one must have been previously enrolled with a device
- * compatible to the device selected to perform the scan
- * @callback: the callback to call when the identification has finished
- * @user_data: user data to pass to the callback
- *
- * Performs a new scan and verifies it against a previously enrolled print.
- * See also: fp_verify_finger_img()
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
- struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
- g_return_val_if_fail (callback != NULL, -EINVAL);
-
- drv = dev->drv;
-
- G_DEBUG_HERE();
- if (!drv->identify_start)
- return -ENOTSUP;
- dev->state = DEV_STATE_IDENTIFY_STARTING;
- dev->identify_cb = callback;
- dev->identify_cb_data = user_data;
- dev->identify_gallery = gallery;
-
- r = drv->identify_start(dev);
- if (r < 0) {
- fp_err("identify_start failed with error %d", r);
- dev->identify_cb = NULL;
- dev->state = DEV_STATE_ERROR;
- }
- return r;
-}
-
-/* Driver-lib: identification has started, expect results soon */
-void fpi_drvcb_identify_started(struct fp_dev *dev, int status)
-{
- fp_dbg("status %d", status);
- BUG_ON(dev->state != DEV_STATE_IDENTIFY_STARTING);
- if (status) {
- if (status > 0) {
- status = -status;
- fp_dbg("adjusted to %d", status);
- }
- dev->state = DEV_STATE_ERROR;
- if (dev->identify_cb)
- dev->identify_cb(dev, status, 0, NULL, dev->identify_cb_data);
- } else {
- dev->state = DEV_STATE_IDENTIFYING;
- }
-}
-
-/* Drivers report an identify result (which might mark completion) */
-void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
- size_t match_offset, struct fp_img *img)
-{
- fp_dbg("result %d", result);
- BUG_ON(dev->state != DEV_STATE_IDENTIFYING
- && dev->state != DEV_STATE_ERROR);
- if (result < 0 || result == FP_VERIFY_NO_MATCH
- || result == FP_VERIFY_MATCH)
- dev->state = DEV_STATE_IDENTIFY_DONE;
-
- if (dev->identify_cb)
- dev->identify_cb(dev, result, match_offset, img, dev->identify_cb_data);
- else
- fp_dbg("ignoring verify result as no callback is subscribed");
-}
-
-/**
- * fp_async_identify_stop:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call when the identification has stopped
- * @user_data: user data to pass to the callback
- *
- * Stops an ongoing identification started with fp_async_identify_start().
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
- fp_operation_stop_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
-
- G_DEBUG_HERE();
-
- if (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
- fp_dbg ("Already stopping identification, returning -EINPROGRESS");
- return -EINPROGRESS;
- }
-
- if (dev->state == DEV_STATE_INITIALIZED) {
- if (callback)
- callback(dev, user_data);
- return 0;
- }
-
- drv = dev->drv;
-
- BUG_ON(dev->state != DEV_STATE_IDENTIFYING
- && dev->state != DEV_STATE_IDENTIFY_DONE);
-
- dev->state = DEV_STATE_IDENTIFY_STOPPING;
- dev->identify_cb = NULL;
- dev->identify_stop_cb = callback;
- dev->identify_stop_cb_data = user_data;
-
- if (!drv->identify_start)
- return -ENOTSUP;
- if (!drv->identify_stop) {
- dev->state = DEV_STATE_INITIALIZED;
- fpi_drvcb_identify_stopped(dev);
- return 0;
- }
-
- r = drv->identify_stop(dev, iterating);
- if (r < 0) {
- fp_err("failed to stop identification");
- dev->identify_stop_cb = NULL;
- }
-
- return r;
-}
-
-/* Drivers call this when identification has stopped */
-void fpi_drvcb_identify_stopped(struct fp_dev *dev)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
- dev->state = DEV_STATE_INITIALIZED;
- if (dev->identify_stop_cb)
- dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
-}
-
-/**
- * fp_async_capture_start:
- * @dev: the struct #fp_dev device
- * @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
- * @callback: the callback to call when the capture has finished
- * @user_data: user data to pass to the callback
- *
- * Start the capture of an #fp_img from a device. When the @callback has been called,
- * you must call fp_async_capture_stop().
- *
- * Returns: 0 on success, non-zero on error. -ENOTSUP indicates that either the
- * @unconditional flag was set but the device does not support this, or that the•
- * device does not support imaging
- */
-API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
- fp_img_operation_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
- g_return_val_if_fail (callback != NULL, -EINVAL);
-
- drv = dev->drv;
-
- G_DEBUG_HERE();
- if (!drv->capture_start)
- return -ENOTSUP;
-
- dev->state = DEV_STATE_CAPTURE_STARTING;
- dev->capture_cb = callback;
- dev->capture_cb_data = user_data;
- dev->unconditional_capture = unconditional;
-
- r = drv->capture_start(dev);
- if (r < 0) {
- dev->capture_cb = NULL;
- dev->state = DEV_STATE_ERROR;
- fp_err("failed to start capture, error %d", r);
- }
- return r;
-}
-
-/* Drivers call this when capture has started */
-void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
- if (status) {
- if (status > 0) {
- status = -status;
- fp_dbg("adjusted to %d", status);
- }
- dev->state = DEV_STATE_ERROR;
- if (dev->capture_cb)
- dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
- } else {
- dev->state = DEV_STATE_CAPTURING;
- }
-}
-
-/* Drivers call this to report a capture result (which might mark completion) */
-void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
- struct fp_img *img)
-{
- fp_dbg("result %d", result);
- BUG_ON(dev->state != DEV_STATE_CAPTURING);
- if (result < 0 || result == FP_CAPTURE_COMPLETE)
- dev->state = DEV_STATE_CAPTURE_DONE;
-
- if (dev->capture_cb)
- dev->capture_cb(dev, result, img, dev->capture_cb_data);
- else
- fp_dbg("ignoring capture result as no callback is subscribed");
-}
-
-/* Drivers call this when capture has stopped */
-void fpi_drvcb_capture_stopped(struct fp_dev *dev)
-{
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
- dev->state = DEV_STATE_INITIALIZED;
- if (dev->capture_stop_cb)
- dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
-}
-
-/**
- * fp_async_capture_stop:
- * @dev: the struct #fp_dev device
- * @callback: the callback to call when the capture has been stopped
- * @user_data: user data to pass to the callback
- *
- * Stops an ongoing verification started with fp_async_capture_start().
- *
- * Returns: 0 on success, non-zero on error
- */
-API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
- fp_operation_stop_cb callback, void *user_data)
-{
- struct fp_driver *drv;
- int r;
-
- g_return_val_if_fail(dev != NULL, -ENODEV);
-
- drv = dev->drv;
-
- G_DEBUG_HERE();
- BUG_ON(dev->state != DEV_STATE_ERROR
- && dev->state != DEV_STATE_CAPTURING
- && dev->state != DEV_STATE_CAPTURE_DONE);
-
- dev->capture_cb = NULL;
- dev->capture_stop_cb = callback;
- dev->capture_stop_cb_data = user_data;
- dev->state = DEV_STATE_CAPTURE_STOPPING;
-
- if (!drv->capture_start)
- return -ENOTSUP;
- if (!drv->capture_stop) {
- dev->state = DEV_STATE_INITIALIZED;
- fpi_drvcb_capture_stopped(dev);
- return 0;
- }
-
- r = drv->capture_stop(dev);
- if (r < 0) {
- fp_err("failed to stop capture");
- dev->capture_stop_cb = NULL;
- }
- return r;
-}
diff --git a/libfprint/fpi-async.h b/libfprint/fpi-async.h
deleted file mode 100644
index cf6fa32..0000000
--- a/libfprint/fpi-async.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_ASYNC_H__
-#define __FPI_ASYNC_H__
-
-#include "fpi-dev.h"
-#include "fpi-data.h"
-
-void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
-void fpi_drvcb_close_complete(struct fp_dev *dev);
-
-void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
-void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
- struct fp_print_data *data, struct fp_img *img);
-void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
-
-void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
-void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
- struct fp_img *img);
-void fpi_drvcb_verify_stopped(struct fp_dev *dev);
-
-#endif
diff --git a/libfprint/fpi-context.h b/libfprint/fpi-context.h
new file mode 100644
index 0000000..c5a1075
--- /dev/null
+++ b/libfprint/fpi-context.h
@@ -0,0 +1,33 @@
+/*
+ * FpContext - A FPrint context
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#pragma once
+
+#include
+#include "fp-context.h"
+
+/**
+ * fpi_get_driver_types:
+ * @drivers: #GArray to be filled with all driver types
+ *
+ * This function is purely for private used. It is solely part of the public
+ * API as it is useful during build time.
+ *
+ * Stability: private
+ */
+void fpi_get_driver_types (GArray *drivers);
diff --git a/libfprint/fpi-core.c b/libfprint/fpi-core.c
deleted file mode 100644
index ee158bd..0000000
--- a/libfprint/fpi-core.c
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Core functions for libfprint
- * Copyright (C) 2007-2008 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include "fp_internal.h"
-
-libusb_context *fpi_usb_ctx = NULL;
-GSList *opened_devices = NULL;
-
-/**
- * SECTION:discovery
- * @title: Device discovery
- * @short_description: Device discovery functions
- *
- * These functions allow you to scan the system for supported fingerprint
- * scanning hardware. This is your starting point when integrating libfprint
- * into your software.
- *
- * When you've identified a discovered device that you would like to control,
- * you can open it with fp_dev_open(). Note that discovered devices may no
- * longer be available at the time when you want to open them, for example
- * the user may have unplugged the device.
- */
-
-/**
- * SECTION:drv
- * @title: Driver operations
- * @short_description: Driver operation functions
- *
- * Internally, libfprint is abstracted into various drivers to communicate
- * with the different types of supported fingerprint readers. libfprint works
- * hard so that you don't have to care about these internal abstractions,
- * however there are some situations where you may be interested in a little
- * behind-the-scenes driver info.
- *
- * You can obtain the driver for a device using fp_dev_get_driver(), which
- * you can pass to the functions documented on this page.
- */
-
-/**
- * SECTION:dev
- * @title: Devices operations
- * @short_description: Device operation functions
- *
- * In order to interact with fingerprint scanners, your software will
- * interface primarily with libfprint's representation of devices, detailed
- * on this page.
- *
- * # Enrolling # {#enrolling}
- *
- * Enrolling is represented within libfprint as a multi-stage process. This
- * slightly complicates things for application developers, but is required
- * for a smooth process.
- *
- * Some devices require the user to scan their finger multiple times in
- * order to complete the enrollment process. libfprint must return control
- * to your application in-between each scan in order for your application to
- * instruct the user to swipe their finger again. Each scan is referred to
- * as a stage, so a device that requires 3 scans for enrollment corresponds
- * to you running 3 enrollment stages using libfprint.
- *
- * The fp_dev_get_nr_enroll_stages() function can be used to find out how
- * many enroll stages are needed.
- *
- * In order to complete an enroll stage, you call an enroll function such
- * as fp_enroll_finger(). The return of this function does not necessarily
- * indicate that a stage has completed though, as the user may not have
- * produced a good enough scan. Each stage may have to be retried several
- * times.
- *
- * The exact semantics of the enroll functions are described in the
- * fp_enroll_finger() documentation. You should pay careful attention to the
- * details.
- *
- * # Imaging # {#imaging}
- *
- * libfprint provides you with some ways to retrieve images of scanned
- * fingers, such as the fp_dev_img_capture() function, or some enroll/verify
- * function variants which provide images. You may wish to do something with
- * such images in your application.
- *
- * However, you must be aware that not all hardware supported by libfprint
- * operates like this. Most hardware does operate simply by sending
- * fingerprint images to the host computer for further processing, but some
- * devices do all fingerprint processing in hardware and do not present images
- * to the host computer.
- *
- * You can use fp_dev_supports_imaging() to see if image capture is possible
- * on a particular device. Your application must be able to cope with the
- * fact that libfprint does support regular operations (e.g. enrolling and
- * verification) on some devices which do not provide images.
- */
-
-/**
- * SECTION:fpi-core
- * @title: Driver structures
- * @short_description: Driver structures
- *
- * Driver structures need to be defined inside each driver in
- * order for the core library to know what function to call, and the capabilities
- * of the driver and the devices it supports.
- */
-
-/**
- * SECTION:fpi-core-img
- * @title: Image driver structures
- * @short_description: Image driver structures
- *
- * Image driver structures need to be defined inside each image driver in
- * order for the core library to know what function to call, and the capabilities
- * of the driver and the devices it supports. Its structure is based off the
- * #fp_driver struct.
- */
-
-static GSList *registered_drivers = NULL;
-
-static void register_driver(struct fp_driver *drv)
-{
- if (drv->id == 0) {
- fp_err("not registering driver %s: driver ID is 0", drv->name);
- return;
- }
- registered_drivers = g_slist_prepend(registered_drivers, (gpointer) drv);
- fp_dbg("registered driver %s", drv->name);
-}
-
-#include "drivers_arrays.h"
-
-static void register_drivers(void)
-{
- unsigned int i;
-
- for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
- register_driver(primitive_drivers[i]);
-
- for (i = 0; i < G_N_ELEMENTS(img_drivers); i++) {
- struct fp_img_driver *imgdriver = img_drivers[i];
- fpi_img_driver_setup(imgdriver);
- register_driver(&imgdriver->driver);
- }
-}
-
-API_EXPORTED struct fp_driver **fprint_get_drivers (void)
-{
- GPtrArray *array;
- unsigned int i;
-
- array = g_ptr_array_new ();
- for (i = 0; i < G_N_ELEMENTS(primitive_drivers); i++)
- g_ptr_array_add (array, primitive_drivers[i]);
-
- for (i = 0; i < G_N_ELEMENTS(img_drivers); i++)
- g_ptr_array_add (array, &(img_drivers[i]->driver));
-
- /* Add a null item terminating the array */
- g_ptr_array_add (array, NULL);
-
- return (struct fp_driver **) g_ptr_array_free (array, FALSE);
-}
-
-static struct fp_driver *find_supporting_driver(libusb_device *udev,
- const struct usb_id **usb_id, uint32_t *devtype)
-{
- int ret;
- GSList *elem = registered_drivers;
- struct libusb_device_descriptor dsc;
-
- const struct usb_id *best_usb_id;
- struct fp_driver *best_drv;
- uint32_t best_devtype;
- int drv_score = 0;
-
- ret = libusb_get_device_descriptor(udev, &dsc);
- if (ret < 0) {
- fp_err("Failed to get device descriptor");
- return NULL;
- }
-
- best_drv = NULL;
- best_devtype = 0;
-
- do {
- struct fp_driver *drv = elem->data;
- uint32_t type = 0;
- const struct usb_id *id;
-
- for (id = drv->id_table; id->vendor; id++) {
- if (dsc.idVendor == id->vendor && dsc.idProduct == id->product) {
- if (drv->discover) {
- int r = drv->discover(&dsc, &type);
- if (r < 0)
- fp_err("%s discover failed, code %d", drv->name, r);
- if (r <= 0)
- continue;
- /* Has a discover function, and matched our device */
- drv_score = 100;
- } else {
- /* Already got a driver as good */
- if (drv_score >= 50)
- continue;
- drv_score = 50;
- }
- fp_dbg("driver %s supports USB device %04x:%04x",
- drv->name, id->vendor, id->product);
- best_usb_id = id;
- best_drv = drv;
- best_devtype = type;
-
- /* We found the best possible driver */
- if (drv_score == 100)
- break;
- }
- }
- } while ((elem = g_slist_next(elem)));
-
- if (best_drv != NULL) {
- fp_dbg("selected driver %s supports USB device %04x:%04x",
- best_drv->name, dsc.idVendor, dsc.idProduct);
- *devtype = best_devtype;
- *usb_id = best_usb_id;
- }
-
- return best_drv;
-}
-
-static struct fp_dscv_dev *discover_dev(libusb_device *udev)
-{
- const struct usb_id *usb_id;
- struct fp_driver *drv;
- struct fp_dscv_dev *ddev;
- uint32_t devtype;
-
- drv = find_supporting_driver(udev, &usb_id, &devtype);
-
- if (!drv)
- return NULL;
-
- ddev = g_malloc0(sizeof(*ddev));
- ddev->drv = drv;
- ddev->udev = udev;
- ddev->driver_data = usb_id->driver_data;
- ddev->devtype = devtype;
- return ddev;
-}
-
-/**
- * fp_discover_devs:
- *
- * Scans the system and returns a list of discovered devices. This is your
- * entry point into finding a fingerprint reader to operate. Note that %NULL
- * is only returned on error. When there are no supported readers available,
- * an empty list is returned instead.
- *
- * Returns: a nul-terminated list of discovered devices or %NULL on error.
- * Must be freed with fp_dscv_devs_free() after use.
- */
-API_EXPORTED struct fp_dscv_dev **fp_discover_devs(void)
-{
- GPtrArray *tmparray;
- libusb_device *udev;
- libusb_device **devs;
- int r;
- int i = 0;
-
- g_return_val_if_fail (registered_drivers != NULL, NULL);
-
- r = libusb_get_device_list(fpi_usb_ctx, &devs);
- if (r < 0) {
- fp_err("couldn't enumerate USB devices, error %d", r);
- return NULL;
- }
-
- tmparray = g_ptr_array_new ();
-
- /* Check each device against each driver, temporarily storing successfully
- * discovered devices in a GPtrArray. */
- while ((udev = devs[i++]) != NULL) {
- struct fp_dscv_dev *ddev = discover_dev(udev);
- if (!ddev)
- continue;
- /* discover_dev() doesn't hold a reference to the udev,
- * so increase the reference for ddev to hold this ref */
- libusb_ref_device(udev);
- g_ptr_array_add (tmparray, (gpointer) ddev);
- }
- libusb_free_device_list(devs, 1);
-
- /* Convert our temporary array into a standard NULL-terminated pointer
- * array. */
- g_ptr_array_add (tmparray, NULL);
- return (struct fp_dscv_dev **) g_ptr_array_free (tmparray, FALSE);
-}
-
-/**
- * fp_dscv_devs_free:
- * @devs: the list of discovered devices. If %NULL, function simply
- * returns.
- *
- * Free a list of discovered devices. This function destroys the list and all
- * discovered devices that it included, so make sure you have opened your
- * discovered device before freeing the list.
- */
-API_EXPORTED void fp_dscv_devs_free(struct fp_dscv_dev **devs)
-{
- int i;
- if (!devs)
- return;
-
- for (i = 0; devs[i]; i++) {
- libusb_unref_device(devs[i]->udev);
- g_free(devs[i]);
- }
- g_free(devs);
-}
-
-/**
- * fp_dscv_dev_get_driver:
- * @dev: the discovered device
- *
- * Gets the #fp_driver for a discovered device.
- *
- * Returns: the driver backing the device
- */
-API_EXPORTED struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev)
-{
- g_return_val_if_fail(dev, NULL);
-
- return dev->drv;
-}
-
-/**
- * fp_dscv_dev_get_driver_id:
- * @dev: a discovered fingerprint device
- *
- * Returns a unique driver identifier for the underlying driver
- * for that device.
- *
- * Returns: the ID for #dev
- */
-API_EXPORTED uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev));
-}
-
-/**
- * fp_dscv_dev_get_devtype:
- * @dev: the discovered device
- *
- * Gets the [devtype](advanced-topics.html#device-types) for a discovered device.
- *
- * Returns: the devtype of the device
- */
-API_EXPORTED uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return dev->devtype;
-}
-
-enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv)
-{
- switch (drv->type) {
- case DRIVER_PRIMITIVE:
- return PRINT_DATA_RAW;
- case DRIVER_IMAGING:
- return PRINT_DATA_NBIS_MINUTIAE;
- default:
- fp_err("unrecognised drv type %d", drv->type);
- return PRINT_DATA_RAW;
- }
-}
-
-/**
- * fp_dscv_dev_supports_print_data:
- * @dev: the discovered device
- * @print: the print for compatibility checking
- *
- * Determines if a specific #fp_print_data stored print appears to be
- * compatible with a discovered device.
- *
- * Returns: 1 if the print is compatible with the device, 0 otherwise
- */
-API_EXPORTED int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
- struct fp_print_data *print)
-{
- g_return_val_if_fail(dev, 0);
- g_return_val_if_fail(print, 0);
-
- return fpi_print_data_compatible(dev->drv->id, dev->devtype,
- fpi_driver_get_data_type(dev->drv), print->driver_id, print->devtype,
- print->type);
-}
-
-/**
- * fp_dev_get_driver:
- * @dev: the struct #fp_dev device
- *
- * Get the #fp_driver for a fingerprint device.
- *
- * Returns: the driver controlling the device
- */
-API_EXPORTED struct fp_driver *fp_dev_get_driver(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, NULL);
-
- return dev->drv;
-}
-
-/**
- * fp_dev_get_nr_enroll_stages:
- * @dev: the struct #fp_dev device
- *
- * Gets the number of [enroll stages](intro.html#enrollment) required to enroll a
- * fingerprint with the device.
- *
- * Returns: the number of enroll stages
- */
-API_EXPORTED int fp_dev_get_nr_enroll_stages(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return dev->nr_enroll_stages;
-}
-
-/**
- * fp_dev_get_devtype:
- * @dev: the struct #fp_dev device
- *
- * Gets the [devtype](advanced-topics.html#device-types) for a device.
- *
- * Returns: the devtype
- */
-API_EXPORTED uint32_t fp_dev_get_devtype(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return dev->devtype;
-}
-
-/**
- * fp_dev_supports_print_data:
- * @dev: the struct #fp_dev device
- * @data: the stored print
- *
- * Determines if a stored print is compatible with a certain device.
- *
- * Returns: 1 if the print is compatible with the device, 0 if not
- */
-API_EXPORTED int fp_dev_supports_print_data(struct fp_dev *dev,
- struct fp_print_data *data)
-{
- g_return_val_if_fail(dev, 0);
- g_return_val_if_fail(data, 0);
-
- return fpi_print_data_compatible(dev->drv->id, dev->devtype,
- fpi_driver_get_data_type(dev->drv), data->driver_id, data->devtype,
- data->type);
-}
-
-/**
- * fp_driver_get_name:
- * @drv: the driver
- *
- * Retrieves the name of the driver. For example: "upekts"
- *
- * Returns: the driver name. Must not be modified or freed.
- */
-API_EXPORTED const char *fp_driver_get_name(struct fp_driver *drv)
-{
- g_return_val_if_fail(drv, NULL);
-
- return drv->name;
-}
-
-/**
- * fp_driver_get_full_name:
- * @drv: the driver
- *
- * Retrieves a descriptive name of the driver. For example: "UPEK TouchStrip"
- *
- * Returns: the descriptive name. Must not be modified or freed.
- */
-API_EXPORTED const char *fp_driver_get_full_name(struct fp_driver *drv)
-{
- g_return_val_if_fail(drv, NULL);
-
- return drv->full_name;
-}
-
-/**
- * fp_driver_get_driver_id:
- * @drv: the driver
- *
- * Retrieves the driver ID code for a driver.
- *
- * Returns: the driver ID
- */
-API_EXPORTED uint16_t fp_driver_get_driver_id(struct fp_driver *drv)
-{
- g_return_val_if_fail(drv, 0);
-
- return drv->id;
-}
-
-/**
- * fp_driver_get_scan_type:
- * @drv: the driver
- *
- * Retrieves the scan type for the devices associated with the driver.
- *
- * Returns: the scan type
- */
-API_EXPORTED enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv)
-{
- g_return_val_if_fail(drv, FP_SCAN_TYPE_PRESS);
-
- return drv->scan_type;
-}
-
-/**
- * fp_driver_supports_imaging:
- * @drv: the driver
- *
- * Determines if a driver has imaging capabilities. If a driver has imaging
- * capabilities you are able to perform imaging operations such as retrieving
- * scan images using fp_dev_img_capture(). However, not all drivers support
- * imaging devices – some do all processing in hardware. This function will
- * indicate which class a device in question falls into.
- *
- * Returns: 1 if the device is an imaging device, 0 if the device does not
- * provide images to the host computer
- */
-API_EXPORTED int fp_driver_supports_imaging(struct fp_driver *drv)
-{
- g_return_val_if_fail(drv, 0);
-
- return drv->capture_start != NULL;
-}
-
-/**
- * fp_dev_supports_imaging:
- * @dev: the struct #fp_dev device
- *
- * Determines if a device has imaging capabilities. If a device has imaging
- * capabilities you are able to perform imaging operations such as retrieving
- * scan images using fp_dev_img_capture(). However, not all devices are
- * imaging devices – some do all processing in hardware. This function will
- * indicate which class a device in question falls into.
- *
- * Returns: 1 if the device is an imaging device, 0 if the device does not
- * provide images to the host computer
- */
-API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return dev->drv->capture_start != NULL;
-}
-
-/**
- * fp_dev_supports_identification:
- * @dev: the struct #fp_dev device
- *
- * Determines if a device is capable of [identification](intro.html#identification)
- * through fp_identify_finger() and similar. Not all devices support this
- * functionality.
- *
- * Returns: 1 if the device is capable of identification, 0 otherwise.
- */
-API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, 0);
-
- return dev->drv->identify_start != NULL;
-}
-
-/**
- * fp_dev_get_img_width:
- * @dev: the struct #fp_dev device
- *
- * Gets the expected width of images that will be captured from the device.
- * This function will return -1 for devices that are not
- * [imaging devices](libfprint-Devices-operations.html#imaging). If the width of images from this device
- * can vary, 0 will be returned.
- *
- * Returns: the expected image width, or 0 for variable, or -1 for non-imaging
- * devices.
- */
-API_EXPORTED int fp_dev_get_img_width(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, -1);
-
- if (!dev->img_dev) {
- fp_dbg("get image width for non-imaging device");
- return -1;
- }
-
- return fpi_imgdev_get_img_width(dev->img_dev);
-}
-
-/**
- * fp_dev_get_img_height:
- * @dev: the struct #fp_dev device
- *
- * Gets the expected height of images that will be captured from the device.
- * This function will return -1 for devices that are not
- * [imaging devices](libfprint-Devices-operations.html#imaging). If the height of images from this device
- * can vary, 0 will be returned.
- *
- * Returns: the expected image height, or 0 for variable, or -1 for non-imaging
- * devices.
- */
-API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
-{
- g_return_val_if_fail(dev, -1);
-
- if (!dev->img_dev) {
- fp_dbg("get image height for non-imaging device");
- return -1;
- }
-
- return fpi_imgdev_get_img_height(dev->img_dev);
-}
-
-/**
- * fp_set_debug:
- * @level: the verbosity level
- *
- * This call does nothing, see fp_init() for details.
- */
-API_EXPORTED void fp_set_debug(int level)
-{
- /* Nothing */
-}
-
-/**
- * fp_init:
- *
- * Initialise libfprint. This function must be called before you attempt to
- * use the library in any way.
- *
- * To enable debug output of libfprint specifically, use GLib's `G_MESSAGES_DEBUG`
- * environment variable as explained in [Running and debugging GLib Applications](https://developer.gnome.org/glib/stable/glib-running.html#G_MESSAGES_DEBUG).
- *
- * The log domains used in libfprint are either `libfprint` or `libfprint-FP_COMPONENT`
- * where `FP_COMPONENT` is defined in the source code for each driver, or component
- * of the library. Starting with `all` and trimming down is advised.
- *
- * To enable debugging of libusb, for USB-based fingerprint reader drivers, use
- * libusb's `LIBUSB_DEBUG` environment variable as explained in the
- * [libusb-1.0 API Reference](http://libusb.sourceforge.net/api-1.0/#msglog).
- *
- * Example:
- *
- * ```
- * LIBUSB_DEBUG=4 G_MESSAGES_DEBUG=all my-libfprint-application
- * ```
- *
- * Returns: 0 on success, non-zero on error.
- */
-API_EXPORTED int fp_init(void)
-{
- int r;
- G_DEBUG_HERE();
-
- r = libusb_init(&fpi_usb_ctx);
- if (r < 0)
- return r;
-
- register_drivers();
- fpi_poll_init();
- return 0;
-}
-
-/**
- * fp_exit:
- *
- * Deinitialise libfprint. This function should be called during your program
- * exit sequence. You must not use any libfprint functions after calling this
- * function, unless you call fp_init() again.
- */
-API_EXPORTED void fp_exit(void)
-{
- G_DEBUG_HERE();
-
- if (opened_devices) {
- GSList *copy = g_slist_copy(opened_devices);
- GSList *elem = copy;
- fp_dbg("naughty app left devices open on exit!");
-
- do
- fp_dev_close((struct fp_dev *) elem->data);
- while ((elem = g_slist_next(elem)));
-
- g_slist_free(copy);
- g_slist_free(opened_devices);
- opened_devices = NULL;
- }
-
- fpi_poll_exit();
- g_slist_free(registered_drivers);
- registered_drivers = NULL;
- libusb_exit(fpi_usb_ctx);
-}
-
diff --git a/libfprint/fpi-core.h b/libfprint/fpi-core.h
deleted file mode 100644
index d183f88..0000000
--- a/libfprint/fpi-core.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_CORE_H__
-#define __FPI_CORE_H__
-
-#include
-#include "fpi-dev-img.h"
-
-/**
- * usb_id:
- * @vendor: the USB vendor ID
- * @product: the USB product ID
- * @driver_data: data to differentiate devices of different
- * vendor and product IDs.
- *
- * The struct #usb_id is used to declare devices supported by a
- * particular driver. The @driver_data information is used to
- * differentiate different models of devices which only need
- * small changes compared to the default driver behaviour to function.
- *
- * For example, a device might have a different initialisation from
- * the stock device, so the driver could do:
- *
- * |[
- * if (driver_data == MY_DIFFERENT_DEVICE_QUIRK) {
- * ...
- * } else {
- * ...
- * }
- * ]|
- *
- * The default value is zero, so the @driver_data needs to be a
- * non-zero to be useful.
- */
-struct usb_id {
- uint16_t vendor;
- uint16_t product;
- unsigned long driver_data;
-};
-
-/**
- * fp_driver_type:
- * @DRIVER_PRIMITIVE: primitive, non-imaging, driver
- * @DRIVER_IMAGING: imaging driver
- *
- * The type of device the driver supports.
- */
-enum fp_driver_type {
- DRIVER_PRIMITIVE = 0,
- DRIVER_IMAGING = 1,
-};
-
-struct fp_driver {
- const uint16_t id;
- const char *name;
- const char *full_name;
- const struct usb_id * const id_table;
- enum fp_driver_type type;
- enum fp_scan_type scan_type;
-
- /* Device operations */
- int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
- int (*open)(struct fp_dev *dev, unsigned long driver_data);
- void (*close)(struct fp_dev *dev);
- int (*enroll_start)(struct fp_dev *dev);
- int (*enroll_stop)(struct fp_dev *dev);
- int (*verify_start)(struct fp_dev *dev);
- int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
- int (*identify_start)(struct fp_dev *dev);
- int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
- int (*capture_start)(struct fp_dev *dev);
- int (*capture_stop)(struct fp_dev *dev);
-};
-
-/**
- * FpiImgDriverFlags:
- * @FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE: Whether the driver supports
- * unconditional image capture. No driver currently does.
- *
- * Flags used in the #fp_img_driver to advertise the capabilities of drivers.
- */
-typedef enum {
- FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE = 1 << 0
-} FpiImgDriverFlags;
-
-struct fp_img_driver {
- struct fp_driver driver;
- FpiImgDriverFlags flags;
- int img_width;
- int img_height;
- int bz3_threshold;
-
- /* Device operations */
- int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
- void (*close)(struct fp_img_dev *dev);
- int (*activate)(struct fp_img_dev *dev);
- int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
- void (*deactivate)(struct fp_img_dev *dev);
-};
-
-#endif
diff --git a/libfprint/fpi-data.c b/libfprint/fpi-data.c
deleted file mode 100644
index 2a33064..0000000
--- a/libfprint/fpi-data.c
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Fingerprint data handling and storage
- * Copyright (C) 2007 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include "fp_internal.h"
-
-#define DIR_PERMS 0700
-
-struct fpi_print_data_fp2 {
- char prefix[3];
- uint16_t driver_id;
- uint32_t devtype;
- unsigned char data_type;
- unsigned char data[0];
-} __attribute__((__packed__));
-
-struct fpi_print_data_item_fp2 {
- uint32_t length;
- unsigned char data[0];
-} __attribute__((__packed__));
-
-/**
- * SECTION: print_data
- * @title: Stored prints
- * @short_description: Stored prints functions
- *
- * Stored prints are represented by a structure named #fp_print_data.
- * Stored prints are originally obtained from an enrollment function such as
- * fp_enroll_finger().
- *
- * This page documents the various operations you can do with a stored print.
- * Note that by default, "stored prints" are not actually stored anywhere
- * except in RAM. Storage needs to be handled by the API user by using the
- * fp_print_data_get_data() and fp_print_data_from_data(). This API allows
- * to convert print data into byte strings, and to reconstruct stored prints
- * from such data at a later point. You are welcome to store these byte strings
- * in any fashion that suits you.
- */
-
-/*
- * SECTION: fpi-data
- * @title: Stored prints creation
- * @short_description: Stored prints creation functions
- *
- * Stored print can be loaded and created by certain drivers which do their own
- * print matching in hardware. Most drivers will not be using those functions.
- * See #fp_print_data for the public API counterpart.
- */
-
-#define FP_FINGER_IS_VALID(finger) \
- ((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
-
-static struct fp_print_data *print_data_new(uint16_t driver_id,
- uint32_t devtype, enum fp_print_data_type type)
-{
- struct fp_print_data *data = g_malloc0(sizeof(*data));
- fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
- data->driver_id = driver_id;
- data->devtype = devtype;
- data->type = type;
- return data;
-}
-
-static void fpi_print_data_item_free(struct fp_print_data_item *item)
-{
- g_free(item);
-}
-
-struct fp_print_data_item *fpi_print_data_item_new(size_t length)
-{
- struct fp_print_data_item *item = g_malloc0(sizeof(*item) + length);
- item->length = length;
-
- return item;
-}
-
-struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
-{
- return print_data_new(dev->drv->id, dev->devtype,
- fpi_driver_get_data_type(dev->drv));
-}
-
-struct fp_print_data_item *
-fpi_print_data_get_item(struct fp_print_data *data)
-{
- return data->prints->data;
-}
-
-void
-fpi_print_data_add_item(struct fp_print_data *data,
- struct fp_print_data_item *item)
-{
- data->prints = g_slist_prepend(data->prints, item);
-}
-
-/**
- * fp_print_data_get_data:
- * @data: the stored print
- * @ret: output location for the data buffer. Must be freed with free()
- * after use.
-
- * Convert a stored print into a unified representation inside a data buffer.
- * You can then store this data buffer in any way that suits you, and load
- * it back at some later time using fp_print_data_from_data().
- *
- * Returns: the size of the freshly allocated buffer, or 0 on error.
- */
-API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
- unsigned char **ret)
-{
- struct fpi_print_data_fp2 *out_data;
- struct fpi_print_data_item_fp2 *out_item;
- struct fp_print_data_item *item;
- size_t buflen = 0;
- GSList *list_item;
- unsigned char *buf;
-
- G_DEBUG_HERE();
-
- list_item = data->prints;
- while (list_item) {
- item = list_item->data;
- buflen += sizeof(*out_item);
- buflen += item->length;
- list_item = g_slist_next(list_item);
- }
-
- buflen += sizeof(*out_data);
- out_data = g_malloc(buflen);
-
- *ret = (unsigned char *) out_data;
- buf = out_data->data;
- out_data->prefix[0] = 'F';
- out_data->prefix[1] = 'P';
- out_data->prefix[2] = '2';
- out_data->driver_id = GUINT16_TO_LE(data->driver_id);
- out_data->devtype = GUINT32_TO_LE(data->devtype);
- out_data->data_type = data->type;
-
- list_item = data->prints;
- while (list_item) {
- item = list_item->data;
- out_item = (struct fpi_print_data_item_fp2 *)buf;
- out_item->length = GUINT32_TO_LE(item->length);
- /* FIXME: fp_print_data_item->data content is not endianness agnostic */
- memcpy(out_item->data, item->data, item->length);
- buf += sizeof(*out_item);
- buf += item->length;
- list_item = g_slist_next(list_item);
- }
-
- return buflen;
-}
-
-static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
- size_t buflen)
-{
- size_t print_data_len;
- struct fp_print_data *data;
- struct fp_print_data_item *item;
- struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
-
- print_data_len = buflen - sizeof(*raw);
- data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
- GUINT32_FROM_LE(raw->devtype), raw->data_type);
- item = fpi_print_data_item_new(print_data_len);
- /* FIXME: fp_print_data->data content is not endianness agnostic */
- memcpy(item->data, raw->data, print_data_len);
- data->prints = g_slist_prepend(data->prints, item);
-
- return data;
-}
-
-static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
- size_t buflen)
-{
- size_t total_data_len, item_len;
- struct fp_print_data *data;
- struct fp_print_data_item *item;
- struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
- unsigned char *raw_buf;
- struct fpi_print_data_item_fp2 *raw_item;
-
- total_data_len = buflen - sizeof(*raw);
- data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
- GUINT32_FROM_LE(raw->devtype), raw->data_type);
- raw_buf = raw->data;
- while (total_data_len) {
- if (total_data_len < sizeof(*raw_item))
- break;
- total_data_len -= sizeof(*raw_item);
-
- raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
- item_len = GUINT32_FROM_LE(raw_item->length);
- fp_dbg("item len %d, total_data_len %d", (int) item_len, (int) total_data_len);
- if (total_data_len < item_len) {
- fp_err("corrupted fingerprint data");
- break;
- }
- total_data_len -= item_len;
-
- item = fpi_print_data_item_new(item_len);
- /* FIXME: fp_print_data->data content is not endianness agnostic */
- memcpy(item->data, raw_item->data, item_len);
- data->prints = g_slist_prepend(data->prints, item);
-
- raw_buf += sizeof(*raw_item);
- raw_buf += item_len;
- }
-
- if (g_slist_length(data->prints) == 0) {
- fp_print_data_free(data);
- data = NULL;
- }
-
- return data;
-
-}
-
-/**
- * fp_print_data_from_data:
- * @buf: the data buffer
- * @buflen: the length of the buffer
-
- * Load a stored print from a data buffer. The contents of said buffer must
- * be the untouched contents of a buffer previously supplied to you by the
- * fp_print_data_get_data() function.
- *
- * Returns: the stored print represented by the data, or %NULL on error. Must
- * be freed with fp_print_data_free() after use.
- */
-API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
- size_t buflen)
-{
- struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
-
- fp_dbg("buffer size %zd", buflen);
- if (buflen < sizeof(*raw))
- return NULL;
-
- if (strncmp(raw->prefix, "FP1", 3) == 0) {
- return fpi_print_data_from_fp1_data(buf, buflen);
- } else if (strncmp(raw->prefix, "FP2", 3) == 0) {
- return fpi_print_data_from_fp2_data(buf, buflen);
- } else {
- fp_dbg("bad header prefix");
- }
-
- return NULL;
-}
-
-gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
- enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
- enum fp_print_data_type type2)
-{
- if (driver_id1 != driver_id2) {
- fp_dbg("driver ID mismatch: %02x vs %02x", driver_id1, driver_id2);
- return FALSE;
- }
-
- if (devtype1 != devtype2) {
- fp_dbg("devtype mismatch: %04x vs %04x", devtype1, devtype2);
- return FALSE;
- }
-
- if (type1 != type2) {
- fp_dbg("type mismatch: %d vs %d", type1, type2);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * fp_print_data_free:
- * @data: the stored print to destroy. If NULL, function simply returns.
- *
- * Frees a stored print. Must be called when you are finished using the print.
- */
-API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
-{
- if (data)
- g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
- g_free(data);
-}
-
-/**
- * fp_print_data_get_driver_id:
- * @data: the stored print
-
- * Gets the [driver ID](advanced-topics.html#driver_id) for a stored print. The driver ID
- * indicates which driver the print originally came from. The print is
- * only usable with a device controlled by that driver.
- *
- * Returns: the driver ID of the driver compatible with the print
- */
-API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
-{
- return data->driver_id;
-}
-
-/**
- * fp_print_data_get_devtype:
- * @data: the stored print
-
- * Gets the [devtype](advanced-topics.html#device-types) for a stored print. The devtype represents
- * which type of device under the parent driver is compatible with the print.
- *
- * Returns: the devtype of the device range compatible with the print
- */
-API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
-{
- return data->devtype;
-}
diff --git a/libfprint/fpi-dev-img.c b/libfprint/fpi-dev-img.c
deleted file mode 100644
index b9d2783..0000000
--- a/libfprint/fpi-dev-img.c
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * Core imaging device functions for libfprint
- * Copyright (C) 2007-2008 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include
-
-#include
-
-#include "fpi-dev-img.h"
-#include "fpi-async.h"
-#include "fp_internal.h"
-
-/**
- * SECTION:fpi-dev-img
- * @title: Image device operations
- * @short_description: Image device operation functions
- *
- * As drivers work through different operations, they need to report back
- * to the core as to their internal state, so errors and successes can be
- * reported back to front-ends.
- */
-
-#define MIN_ACCEPTABLE_MINUTIAE 10
-#define BOZORTH3_DEFAULT_THRESHOLD 40
-#define IMG_ENROLL_STAGES 5
-
-/**
- * fpi_imgdev_get_action_state:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Returns the state of an imaging device while enrolling a fingerprint.
- *
- * Returns: a enum #fp_imgdev_enroll_state
- */
-enum fp_imgdev_enroll_state
-fpi_imgdev_get_action_state(struct fp_img_dev *imgdev)
-{
- return imgdev->action_state;
-}
-
-/**
- * fpi_imgdev_get_action:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Returns the current action being performed by an imaging device.
- *
- * Returns: a enum #fp_imgdev_action
- */
-enum fp_imgdev_action
-fpi_imgdev_get_action(struct fp_img_dev *imgdev)
-{
- return imgdev->action;
-}
-
-/**
- * fpi_imgdev_get_action_result:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Returns an integer representing the result of an action. Which enum
- * the result code is taken from depends on the current action being performed.
- * See #fp_capture_result, #fp_enroll_result and #fp_verify_result.
- */
-int
-fpi_imgdev_get_action_result(struct fp_img_dev *imgdev)
-{
- return imgdev->action_result;
-}
-
-/**
- * fpi_imgdev_set_action_result:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @action_result: an action result
- *
- * Drivers should use fpi_imgdev_image_captured() instead. This function
- * should not be used, and will be removed soon.
- */
-void
-fpi_imgdev_set_action_result(struct fp_img_dev *imgdev,
- int action_result)
-{
- imgdev->action_result = action_result;
-}
-
-static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
-{
- struct fp_img_dev *imgdev = g_malloc0(sizeof(*imgdev));
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
- int r = 0;
-
- /* Set up back pointers */
- dev->img_dev = imgdev;
- imgdev->parent = dev;
-
- imgdev->enroll_stage = 0;
- dev->nr_enroll_stages = IMG_ENROLL_STAGES;
-
- if (imgdrv->open) {
- r = imgdrv->open(imgdev, driver_data);
- if (r)
- goto err;
- } else {
- fpi_drvcb_open_complete(dev, 0);
- }
-
- return 0;
-err:
- g_free(imgdev);
- return r;
-}
-
-/**
- * fpi_imgdev_open_complete:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @status: an error code
- *
- * Function to call when the device has been opened, whether
- * successfully of not.
- */
-void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status)
-{
- fpi_drvcb_open_complete(FP_DEV(imgdev), status);
-}
-
-static void img_dev_close(struct fp_dev *dev)
-{
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
-
- if (imgdrv->close)
- imgdrv->close(dev->img_dev);
- else
- fpi_drvcb_close_complete(dev);
-}
-
-/**
- * fpi_imgdev_close_complete:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Function to call when the device has been closed.
- */
-void fpi_imgdev_close_complete(struct fp_img_dev *imgdev)
-{
- fpi_drvcb_close_complete(FP_DEV(imgdev));
- g_free(imgdev);
-}
-
-static int dev_change_state(struct fp_img_dev *imgdev,
- enum fp_imgdev_state state)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
-
- if (!imgdrv->change_state)
- return 0;
- return imgdrv->change_state(imgdev, state);
-}
-
-/* check image properties and resize it if necessary. potentially returns a new
- * image after freeing the old one. */
-static int sanitize_image(struct fp_img_dev *imgdev, struct fp_img **_img)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
- struct fp_img *img = *_img;
-
- if (imgdrv->img_width > 0) {
- img->width = imgdrv->img_width;
- } else if (img->width <= 0) {
- fp_err("no image width assigned");
- return -EINVAL;
- }
-
- if (imgdrv->img_height > 0) {
- img->height = imgdrv->img_height;
- } else if (img->height <= 0) {
- fp_err("no image height assigned");
- return -EINVAL;
- }
-
- if (!fpi_img_is_sane(img)) {
- fp_err("image is not sane!");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * fpi_imgdev_report_finger_status:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @present: whether the finger is present on the sensor
- *
- * Reports from the driver whether the user's finger is on
- * the sensor.
- */
-void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
- gboolean present)
-{
- int r = imgdev->action_result;
- struct fp_print_data *data = imgdev->acquire_data;
- struct fp_img *img = imgdev->acquire_img;
-
- fp_dbg(present ? "finger on sensor" : "finger removed");
-
- if (present && imgdev->action_state == IMG_ACQUIRE_STATE_AWAIT_FINGER_ON) {
- dev_change_state(imgdev, IMGDEV_STATE_CAPTURE);
- imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_IMAGE;
- return;
- } else if (present
- || imgdev->action_state != IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF) {
- fp_dbg("ignoring status report");
- return;
- }
-
- /* clear these before reporting results to avoid complications with
- * call cascading in and out of the library */
- imgdev->acquire_img = NULL;
- imgdev->acquire_data = NULL;
-
- /* finger removed, report results */
- switch (imgdev->action) {
- case IMG_ACTION_ENROLL:
- fp_dbg("reporting enroll result");
- data = imgdev->enroll_data;
- if (r == FP_ENROLL_COMPLETE) {
- imgdev->enroll_data = NULL;
- }
- fpi_drvcb_enroll_stage_completed(FP_DEV(imgdev), r,
- r == FP_ENROLL_COMPLETE ? data : NULL,
- img);
- /* the callback can cancel enrollment, so recheck current
- * action and the status to see if retry is needed */
- if (imgdev->action == IMG_ACTION_ENROLL &&
- r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
- imgdev->action_result = 0;
- imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
- dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
- }
- break;
- case IMG_ACTION_VERIFY:
- fpi_drvcb_report_verify_result(FP_DEV(imgdev), r, img);
- imgdev->action_result = 0;
- fp_print_data_free(data);
- break;
- case IMG_ACTION_IDENTIFY:
- fpi_drvcb_report_identify_result(FP_DEV(imgdev), r,
- imgdev->identify_match_offset, img);
- imgdev->action_result = 0;
- fp_print_data_free(data);
- break;
- case IMG_ACTION_CAPTURE:
- fpi_drvcb_report_capture_result(FP_DEV(imgdev), r, img);
- imgdev->action_result = 0;
- break;
- default:
- fp_err("unhandled action %d", imgdev->action);
- break;
- }
-}
-
-static void verify_process_img(struct fp_img_dev *imgdev)
-{
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
- int match_score = imgdrv->bz3_threshold;
- int r;
-
- if (match_score == 0)
- match_score = BOZORTH3_DEFAULT_THRESHOLD;
-
- r = fpi_img_compare_print_data(FP_DEV(imgdev)->verify_data,
- imgdev->acquire_data);
-
- if (r >= match_score)
- r = FP_VERIFY_MATCH;
- else if (r >= 0)
- r = FP_VERIFY_NO_MATCH;
-
- imgdev->action_result = r;
-}
-
-static void identify_process_img(struct fp_img_dev *imgdev)
-{
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
- int match_score = imgdrv->bz3_threshold;
- size_t match_offset;
- int r;
-
- if (match_score == 0)
- match_score = BOZORTH3_DEFAULT_THRESHOLD;
-
- r = fpi_img_compare_print_data_to_gallery(imgdev->acquire_data,
- FP_DEV(imgdev)->identify_gallery, match_score, &match_offset);
-
- imgdev->action_result = r;
- imgdev->identify_match_offset = match_offset;
-}
-
-/**
- * fpi_imgdev_abort_scan:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @result: the scan result
- *
- * Aborts a scan after an error, and set the action result. See
- * fpi_imgdev_get_action_result() for possible values.
- */
-void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result)
-{
- imgdev->action_result = result;
- imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
- dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
-}
-
-/**
- * fpi_imgdev_image_captured:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @img: an #fp_img image
- *
- * Report to the core that the driver captured this image from the sensor.
- */
-void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
-{
- struct fp_print_data *print = NULL;
- int r;
- G_DEBUG_HERE();
-
- if (imgdev->action_state != IMG_ACQUIRE_STATE_AWAIT_IMAGE) {
- fp_dbg("ignoring due to current state %d", imgdev->action_state);
- return;
- }
-
- if (imgdev->action_result) {
- fp_dbg("not overwriting existing action result");
- return;
- }
-
- r = sanitize_image(imgdev, &img);
- if (r < 0) {
- imgdev->action_result = r;
- fp_img_free(img);
- goto next_state;
- }
-
- fp_img_standardize(img);
- imgdev->acquire_img = img;
- if (imgdev->action != IMG_ACTION_CAPTURE) {
- r = fpi_img_to_print_data(imgdev, img, &print);
- if (r < 0) {
- fp_dbg("image to print data conversion error: %d", r);
- imgdev->action_result = FP_ENROLL_RETRY;
- goto next_state;
- } else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
- fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
- MIN_ACCEPTABLE_MINUTIAE);
- fp_print_data_free(print);
- /* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
- imgdev->action_result = FP_ENROLL_RETRY;
- goto next_state;
- }
- }
-
- imgdev->acquire_data = print;
- switch (imgdev->action) {
- case IMG_ACTION_ENROLL:
- if (!imgdev->enroll_data) {
- imgdev->enroll_data = fpi_print_data_new(FP_DEV(imgdev));
- }
- BUG_ON(g_slist_length(print->prints) != 1);
- /* Move print data from acquire data into enroll_data */
- imgdev->enroll_data->prints =
- g_slist_prepend(imgdev->enroll_data->prints, print->prints->data);
- print->prints = g_slist_remove(print->prints, print->prints->data);
-
- fp_print_data_free(imgdev->acquire_data);
- imgdev->acquire_data = NULL;
- imgdev->enroll_stage++;
- if (imgdev->enroll_stage == FP_DEV(imgdev)->nr_enroll_stages)
- imgdev->action_result = FP_ENROLL_COMPLETE;
- else
- imgdev->action_result = FP_ENROLL_PASS;
- break;
- case IMG_ACTION_VERIFY:
- verify_process_img(imgdev);
- break;
- case IMG_ACTION_IDENTIFY:
- identify_process_img(imgdev);
- break;
- case IMG_ACTION_CAPTURE:
- imgdev->action_result = FP_CAPTURE_COMPLETE;
- break;
- default:
- BUG();
- break;
- }
-
-next_state:
- imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
- dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
-}
-
-/**
- * fpi_imgdev_session_error:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @error: an error code
- *
- * Report an error that occurred in the driver.
- */
-void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error)
-{
- fp_dbg("error %d", error);
- BUG_ON(error == 0);
- switch (imgdev->action) {
- case IMG_ACTION_ENROLL:
- fpi_drvcb_enroll_stage_completed(FP_DEV(imgdev), error, NULL, NULL);
- break;
- case IMG_ACTION_VERIFY:
- fpi_drvcb_report_verify_result(FP_DEV(imgdev), error, NULL);
- break;
- case IMG_ACTION_IDENTIFY:
- fpi_drvcb_report_identify_result(FP_DEV(imgdev), error, 0, NULL);
- break;
- case IMG_ACTION_CAPTURE:
- fpi_drvcb_report_capture_result(FP_DEV(imgdev), error, NULL);
- break;
- default:
- fp_err("unhandled action %d", imgdev->action);
- break;
- }
-}
-
-/**
- * fpi_imgdev_activate_complete:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- * @status: the activation result
- *
- * Marks an activation as complete, whether successful or not.
- * See fpi_imgdev_get_action_result() for possible values.
- */
-void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
-{
- fp_dbg("status %d", status);
-
- switch (imgdev->action) {
- case IMG_ACTION_ENROLL:
- fpi_drvcb_enroll_started(FP_DEV(imgdev), status);
- break;
- case IMG_ACTION_VERIFY:
- fpi_drvcb_verify_started(FP_DEV(imgdev), status);
- break;
- case IMG_ACTION_IDENTIFY:
- fpi_drvcb_identify_started(FP_DEV(imgdev), status);
- break;
- case IMG_ACTION_CAPTURE:
- fpi_drvcb_capture_started(FP_DEV(imgdev), status);
- break;
- default:
- fp_err("unhandled action %d", imgdev->action);
- return;
- }
-
- if (status == 0) {
- imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
- dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
- }
-}
-
-/**
- * fpi_imgdev_deactivate_complete:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Marks a deactivation as complete.
- */
-void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
-{
- enum fp_imgdev_action action;
-
- G_DEBUG_HERE();
-
- action = imgdev->action;
- imgdev->action = IMG_ACTION_NONE;
- imgdev->action_state = 0;
-
- switch (action) {
- case IMG_ACTION_ENROLL:
- fpi_drvcb_enroll_stopped(FP_DEV(imgdev));
- break;
- case IMG_ACTION_VERIFY:
- fpi_drvcb_verify_stopped(FP_DEV(imgdev));
- break;
- case IMG_ACTION_IDENTIFY:
- fpi_drvcb_identify_stopped(FP_DEV(imgdev));
- break;
- case IMG_ACTION_CAPTURE:
- fpi_drvcb_capture_stopped(FP_DEV(imgdev));
- break;
- default:
- fp_err("unhandled action %d", imgdev->action);
- break;
- }
-}
-
-int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
- int width = imgdrv->img_width;
-
- if (width == -1)
- width = 0;
-
- return width;
-}
-
-int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
- int height = imgdrv->img_height;
-
- if (height == -1)
- height = 0;
-
- return height;
-}
-
-static int dev_activate(struct fp_img_dev *imgdev)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
-
- if (!imgdrv->activate)
- return 0;
- return imgdrv->activate(imgdev);
-}
-
-static void dev_deactivate(struct fp_img_dev *imgdev)
-{
- struct fp_driver *drv = FP_DEV(imgdev)->drv;
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
-
- if (!imgdrv->deactivate)
- return;
- return imgdrv->deactivate(imgdev);
-}
-
-static int generic_acquire_start(struct fp_dev *dev, int action)
-{
- struct fp_img_dev *imgdev = dev->img_dev;
- int r;
- fp_dbg("action %d", action);
- imgdev->action = action;
- imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
- imgdev->enroll_stage = 0;
-
- r = dev_activate(imgdev);
- if (r < 0)
- fp_err("activation failed with error %d", r);
-
- return r;
-
-}
-
-static void generic_acquire_stop(struct fp_img_dev *imgdev)
-{
- imgdev->action_state = IMG_ACQUIRE_STATE_DEACTIVATING;
- dev_deactivate(imgdev);
-
- fp_print_data_free(imgdev->acquire_data);
- fp_print_data_free(imgdev->enroll_data);
- fp_img_free(imgdev->acquire_img);
- imgdev->acquire_data = NULL;
- imgdev->enroll_data = NULL;
- imgdev->acquire_img = NULL;
- imgdev->action_result = 0;
-}
-
-static int img_dev_enroll_start(struct fp_dev *dev)
-{
- return generic_acquire_start(dev, IMG_ACTION_ENROLL);
-}
-
-static int img_dev_verify_start(struct fp_dev *dev)
-{
- return generic_acquire_start(dev, IMG_ACTION_VERIFY);
-}
-
-static int img_dev_identify_start(struct fp_dev *dev)
-{
- return generic_acquire_start(dev, IMG_ACTION_IDENTIFY);
-}
-
-static int img_dev_capture_start(struct fp_dev *dev)
-{
- /* Unconditional capture is not supported yet */
- if (dev->unconditional_capture)
- return -ENOTSUP;
- return generic_acquire_start(dev, IMG_ACTION_CAPTURE);
-}
-
-static int img_dev_enroll_stop(struct fp_dev *dev)
-{
- struct fp_img_dev *imgdev = dev->img_dev;
- BUG_ON(imgdev->action != IMG_ACTION_ENROLL);
- generic_acquire_stop(imgdev);
- return 0;
-}
-
-static int img_dev_verify_stop(struct fp_dev *dev, gboolean iterating)
-{
- struct fp_img_dev *imgdev = dev->img_dev;
- BUG_ON(imgdev->action != IMG_ACTION_VERIFY);
- generic_acquire_stop(imgdev);
- return 0;
-}
-
-static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
-{
- struct fp_img_dev *imgdev = dev->img_dev;
- BUG_ON(imgdev->action != IMG_ACTION_IDENTIFY);
- generic_acquire_stop(imgdev);
- imgdev->identify_match_offset = 0;
- return 0;
-}
-
-static int img_dev_capture_stop(struct fp_dev *dev)
-{
- struct fp_img_dev *imgdev = dev->img_dev;
- BUG_ON(imgdev->action != IMG_ACTION_CAPTURE);
- generic_acquire_stop(imgdev);
- return 0;
-}
-
-void fpi_img_driver_setup(struct fp_img_driver *idriver)
-{
- idriver->driver.type = DRIVER_IMAGING;
- idriver->driver.open = img_dev_open;
- idriver->driver.close = img_dev_close;
- idriver->driver.enroll_start = img_dev_enroll_start;
- idriver->driver.enroll_stop = img_dev_enroll_stop;
- idriver->driver.verify_start = img_dev_verify_start;
- idriver->driver.verify_stop = img_dev_verify_stop;
- idriver->driver.identify_start = img_dev_identify_start;
- idriver->driver.identify_stop = img_dev_identify_stop;
- idriver->driver.capture_start = img_dev_capture_start;
- idriver->driver.capture_stop = img_dev_capture_stop;
-}
-
diff --git a/libfprint/fpi-dev-img.h b/libfprint/fpi-dev-img.h
deleted file mode 100644
index 0b0d48b..0000000
--- a/libfprint/fpi-dev-img.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_DEV_IMG_H__
-#define __FPI_DEV_IMG_H__
-
-#include "fpi-dev.h"
-#include "fpi-img.h"
-
-/**
- * fp_imgdev_action:
- * @IMG_ACTION_NONE: no action
- * @IMG_ACTION_ENROLL: device action is enrolling
- * @IMG_ACTION_VERIFY: device action is verifying
- * @IMG_ACTION_IDENTIFY: device action is identifying
- * @IMG_ACTION_CAPTURE: device action is capturing
- *
- * The current action being performed by an imaging device. The current
- * action can be gathered inside the driver using fpi_imgdev_get_action().
- */
-enum fp_imgdev_action {
- IMG_ACTION_NONE = 0,
- IMG_ACTION_ENROLL,
- IMG_ACTION_VERIFY,
- IMG_ACTION_IDENTIFY,
- IMG_ACTION_CAPTURE,
-};
-
-/**
- * fp_imgdev_state:
- * @IMGDEV_STATE_INACTIVE: inactive
- * @IMGDEV_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
- * @IMGDEV_STATE_CAPTURE: capturing an image
- * @IMGDEV_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
- *
- * The state of an imaging device while doing a capture. The state is
- * passed through to the driver using the ::activate() or ::change_state() vfuncs.
- */
-enum fp_imgdev_state {
- IMGDEV_STATE_INACTIVE,
- IMGDEV_STATE_AWAIT_FINGER_ON,
- IMGDEV_STATE_CAPTURE,
- IMGDEV_STATE_AWAIT_FINGER_OFF,
-};
-
-/**
- * fp_imgdev_enroll_state:
- * @IMG_ACQUIRE_STATE_NONE: doing nothing
- * @IMG_ACQUIRE_STATE_ACTIVATING: activating the device
- * @IMG_ACQUIRE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
- * @IMG_ACQUIRE_STATE_AWAIT_IMAGE: waiting for the image to be captured
- * @IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
- * @IMG_ACQUIRE_STATE_DONE: enrollment has all the images it needs
- * @IMG_ACQUIRE_STATE_DEACTIVATING: deactivating the device
- *
- * The state of an imaging device while enrolling a fingerprint. Given that enrollment
- * requires multiple captures, a number of those states will be repeated before
- * the state is @IMG_ACQUIRE_STATE_DONE.
- */
-enum fp_imgdev_enroll_state {
- IMG_ACQUIRE_STATE_NONE = 0,
- IMG_ACQUIRE_STATE_ACTIVATING,
- IMG_ACQUIRE_STATE_AWAIT_FINGER_ON,
- IMG_ACQUIRE_STATE_AWAIT_IMAGE,
- IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF,
- IMG_ACQUIRE_STATE_DONE,
- IMG_ACQUIRE_STATE_DEACTIVATING,
-};
-
-void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
-void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);
-void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status);
-void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
-void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
- gboolean present);
-void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
-void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result);
-void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
-
-enum fp_imgdev_enroll_state fpi_imgdev_get_action_state(struct fp_img_dev *imgdev);
-enum fp_imgdev_action fpi_imgdev_get_action(struct fp_img_dev *imgdev);
-int fpi_imgdev_get_action_result(struct fp_img_dev *imgdev);
-void fpi_imgdev_set_action_result(struct fp_img_dev *imgdev, int action_result);
-
-#endif
diff --git a/libfprint/fpi-dev.c b/libfprint/fpi-dev.c
deleted file mode 100644
index 219a2a1..0000000
--- a/libfprint/fpi-dev.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * fp_dev types manipulation
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "fp_internal.h"
-#include
-
-/**
- * SECTION:fpi-dev
- * @title: Device operations
- * @short_description: Device operation functions
- *
- * Those macros and functions will help get access to and from struct #fp_dev,
- * and struct #fp_img_dev types, as well as get and set the instance struct
- * data, eg. the structure containing the data specific to each driver.
- */
-
-/**
- * FP_DEV:
- * @dev: a struct #fp_img_dev
- *
- * Returns the struct #fp_dev associated with @dev, or %NULL on failure.
- *
- * Returns: a struct #fp_dev or %NULL
- */
-struct fp_dev *
-FP_DEV(struct fp_img_dev *dev)
-{
- struct fp_img_dev *imgdev;
-
- g_return_val_if_fail (dev, NULL);
- imgdev = (struct fp_img_dev *) dev;
- return imgdev->parent;
-}
-
-/**
- * FP_IMG_DEV:
- * @dev: a struct #fp_dev representing an imaging device.
- *
- * Returns a struct #fp_img_dev associated with @dev, or %NULL on failure.
- *
- * Returns: a struct #fp_img_dev or %NULL
- */
-struct fp_img_dev *
-FP_IMG_DEV(struct fp_dev *dev)
-{
- g_return_val_if_fail (dev, NULL);
- g_return_val_if_fail (dev->drv, NULL);
- g_return_val_if_fail (dev->drv->type == DRIVER_IMAGING, NULL);
- return dev->img_dev;
-}
-
-/**
- * fp_dev_set_instance_data:
- * @dev: a struct #fp_dev
- * @instance_data: a pointer to the instance data
- *
- * Set the instance data for a struct #fp_dev. This is usually a structure
- * private to the driver used to keep state and pass it as user_data to
- * asynchronous functions.
- *
- * The core does not do any memory management for this data, so the driver
- * itself will have to create and free its own structure when appropriate.
- */
-void
-fp_dev_set_instance_data (struct fp_dev *dev,
- void *instance_data)
-{
- g_return_if_fail (dev);
- g_return_if_fail (instance_data != NULL);
- g_return_if_fail (dev->instance_data == NULL);
-
- dev->instance_data = instance_data;
-}
-
-/**
- * FP_INSTANCE_DATA:
- * @dev: a struct #fp_dev
- *
- * Returns the instance data set using fp_dev_set_instance_data().
- */
-void *
-FP_INSTANCE_DATA (struct fp_dev *dev)
-{
- g_return_val_if_fail (dev, NULL);
-
- return dev->instance_data;
-}
-
-/**
- * fpi_dev_get_usb_dev:
- * @dev: a struct #fp_dev
- *
- * Returns the #libusb_device_handle associated with @dev or %NULL
- * if none are associated.
- *
- * Returns: a #libusb_device_handle pointer or %NULL
- */
-libusb_device_handle *
-fpi_dev_get_usb_dev(struct fp_dev *dev)
-{
- return dev->udev;
-}
-
-/**
- * fpi_dev_set_nr_enroll_stages:
- * @dev: a struct #fp_dev
- * @nr_enroll_stages: the number of enroll stages
- *
- * Sets the number of enroll stages that this device uses. This is
- * usually only necessary for primitive devices which have a hard-coded
- * number of enroll stages baked into their protocol.
- */
-void
-fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
- int nr_enroll_stages)
-{
- dev->nr_enroll_stages = nr_enroll_stages;
-}
-
-/**
- * fpi_dev_get_verify_data:
- * @dev: a struct #fp_dev
- *
- * Returns the verify data associated with @dev.
- * This is usually only necessary for primitive devices which need to
- * have access to the raw verify data as it might have been stored on disk.
- *
- * Returns: a struct #fp_print_data pointer or %NULL
- */
-struct fp_print_data *
-fpi_dev_get_verify_data(struct fp_dev *dev)
-{
- return dev->verify_data;
-}
diff --git a/libfprint/fpi-dev.h b/libfprint/fpi-dev.h
deleted file mode 100644
index 9843391..0000000
--- a/libfprint/fpi-dev.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_DEV_H__
-#define __FPI_DEV_H__
-
-#include
-#include
-
-struct fp_dev;
-
-/**
- * fp_img_dev:
- *
- * #fp_img_dev is an opaque structure type. You must access it using the
- * appropriate functions.
- */
-struct fp_img_dev;
-
-struct fp_dev *FP_DEV (struct fp_img_dev *dev);
-struct fp_img_dev *FP_IMG_DEV (struct fp_dev *dev);
-
-void fp_dev_set_instance_data (struct fp_dev *dev,
- void *instance_data);
-void *FP_INSTANCE_DATA (struct fp_dev *dev);
-
-libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev);
-void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev,
- int nr_enroll_stages);
-struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
-
-#endif
diff --git a/libfprint/fpi-device.h b/libfprint/fpi-device.h
new file mode 100644
index 0000000..a206798
--- /dev/null
+++ b/libfprint/fpi-device.h
@@ -0,0 +1,252 @@
+/*
+ * FpDevice - A fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include
+#include "fp-device.h"
+#include "fp-image.h"
+#include "fpi-print.h"
+
+/**
+ * FpIdEntry:
+ *
+ * An entry in the table of supported hardware. For USB devices, the product ID
+ * and vendor ID should be provided. The optional @driver_data field defaults
+ * to 0 and can be used as a simple flag for device quirks.
+ */
+typedef struct _FpIdEntry FpIdEntry;
+
+struct _FpIdEntry
+{
+ union
+ {
+ struct
+ {
+ guint pid;
+ guint vid;
+ };
+ const gchar *virtual_envvar;
+ };
+ guint64 driver_data;
+};
+
+/**
+ * FpDeviceClass:
+ * @id: ID string for the driver. Should be a valid C identifier and should
+ * match the drivers file name.
+ * @full_name: Human readable description of the driver
+ * @type: The type of driver
+ * @id_table: The table of IDs to bind the driver to
+ * @nr_enroll_stages: The number of enroll stages supported devices need; use
+ * fpi_device_set_nr_enroll_stages() from @probe if this is dynamic.
+ * @scan_type: The scan type of supported devices; use
+ * fpi_device_set_scan_type() from @probe if this is dynamic.
+ * @usb_discover: Class method to check whether a USB device is supported by
+ * the driver. Should return 0 if the device is unsupported and a positive
+ * score otherwise. The default score is 50 and the driver with the highest
+ * score will be loaded.
+ * @probe: Called immediately for all devices. Most drivers will not need to
+ * implement this. Drivers should setup the device identifier from the probe
+ * callback which will be used to verify the compatibility of stored
+ * #FpPrint's. It is permissable to temporarily open the USB device if this
+ * is required for the operation. If an error is returned, then the device
+ * will be destroyed again immediately and never reported to the API user.
+ * @open: Open the device for futher operations. Any of the normal actions are
+ * guaranteed to only happen when the device is open (this includes delete).
+ * @close: Close the device again
+ * @enroll: Start an enroll operation
+ * @verify: Start a verify operation
+ * @identify: Start an identify operation
+ * @capture: Start a capture operation
+ * @list: List prints stored on the device
+ * @delete: Delete a print from the device
+ * @cancel: Called on cancellation, this is a convenience to not need to handle
+ * the #GCancellable directly by using fpi_device_get_cancellable().
+ *
+ * NOTE: If your driver is image based, then you should subclass #FpImageDevice
+ * instead. #FpImageDevice based drivers use a different way of interacting
+ * with libfprint.
+ *
+ * These are the main entry points for drivers to implement. Drivers may not
+ * implement all of these entry points if they do not support the operation
+ * (or a default implementation is sufficient).
+ *
+ * Drivers must eventually call the corresponding function to finish the
+ * operation. It is also acceptable to call the generic
+ * fpi_device_action_error() function but doing so is not recommended in most
+ * usecases.
+ *
+ * Drivers must also handle cancellation properly for any long running
+ * operation (i.e. any operation that requires capturing). It is entirely fine
+ * to ignore cancellation requests for short operations (e.g. open/close).
+ *
+ * This API is solely intended for drivers. It is purely internal and neither
+ * API nor ABI stable.
+ */
+struct _FpDeviceClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+ /* Static information about the driver. */
+ const gchar *id;
+ const gchar *full_name;
+ FpDeviceType type;
+ const FpIdEntry *id_table;
+
+ /* Defaults for device properties */
+ gint nr_enroll_stages;
+ FpScanType scan_type;
+
+ /* Callbacks */
+ gint (*usb_discover) (GUsbDevice *usb_device);
+ void (*probe) (FpDevice *device);
+ void (*open) (FpDevice *device);
+ void (*close) (FpDevice *device);
+ void (*enroll) (FpDevice *device);
+ void (*verify) (FpDevice *device);
+ void (*identify) (FpDevice *device);
+ void (*capture) (FpDevice *device);
+ void (*list) (FpDevice *device);
+ void (*delete) (FpDevice * device);
+
+ void (*cancel) (FpDevice *device);
+};
+
+/**
+ * FpTimeoutFunc:
+ * @device: The #FpDevice passed to fpi_device_add_timeout()
+ * @user_data: the data passed to fpi_device_add_timeout()
+ *
+ * The prototype of the callback function for fpi_device_add_timeout().
+ */
+typedef void (*FpTimeoutFunc) (FpDevice *device,
+ gpointer user_data);
+
+/**
+ * FpDeviceAction:
+ * @FP_DEVICE_ACTION_NONE: No action is active.
+ * @FP_DEVICE_ACTION_PROBE: Probe device for support and information.
+ * @FP_DEVICE_ACTION_OPEN: Device is currently being opened.
+ * @FP_DEVICE_ACTION_CLOSE: Device is currently being closed.
+ * @FP_DEVICE_ACTION_ENROLL: Device is currently enrolling.
+ * @FP_DEVICE_ACTION_VERIFY: Device is currently verifying.
+ * @FP_DEVICE_ACTION_IDENTIFY: Device is currently identifying.
+ * @FP_DEVICE_ACTION_CAPTURE: Device is currently capturing an image.
+ * @FP_DEVICE_ACTION_LIST: Device stored prints are being queried.
+ * @FP_DEVICE_ACTION_DELETE: Device stored print is being deleted.
+ *
+ * Current active action of the device. A driver can retrieve the action.
+ */
+typedef enum {
+ FP_DEVICE_ACTION_NONE = 0,
+ FP_DEVICE_ACTION_PROBE,
+ FP_DEVICE_ACTION_OPEN,
+ FP_DEVICE_ACTION_CLOSE,
+ FP_DEVICE_ACTION_ENROLL,
+ FP_DEVICE_ACTION_VERIFY,
+ FP_DEVICE_ACTION_IDENTIFY,
+ FP_DEVICE_ACTION_CAPTURE,
+ FP_DEVICE_ACTION_LIST,
+ FP_DEVICE_ACTION_DELETE,
+} FpDeviceAction;
+
+GUsbDevice *fpi_device_get_usb_device (FpDevice *device);
+const gchar *fpi_device_get_virtual_env (FpDevice *device);
+//const gchar *fpi_device_get_spi_dev (FpDevice *device);
+
+
+FpDeviceAction fpi_device_get_current_action (FpDevice *device);
+gboolean fpi_device_action_is_cancelled (FpDevice *device);
+
+GError * fpi_device_retry_new (FpDeviceRetry error);
+GError * fpi_device_error_new (FpDeviceError error);
+
+GError * fpi_device_retry_new_msg (FpDeviceRetry error,
+ const gchar *msg);
+GError * fpi_device_error_new_msg (FpDeviceError error,
+ const gchar *msg);
+
+guint64 fpi_device_get_driver_data (FpDevice *device);
+
+void fpi_device_get_enroll_data (FpDevice *device,
+ FpPrint **print);
+
+void fpi_device_get_capture_data (FpDevice *device,
+ gboolean *wait_for_finger);
+void fpi_device_get_verify_data (FpDevice *device,
+ FpPrint **print);
+void fpi_device_get_identify_data (FpDevice *device,
+ GPtrArray **prints);
+void fpi_device_get_delete_data (FpDevice *device,
+ FpPrint **print);
+GCancellable *fpi_device_get_cancellable (FpDevice *device);
+
+
+
+GSource * fpi_device_add_timeout (FpDevice *device,
+ gint interval,
+ FpTimeoutFunc func,
+ gpointer user_data);
+
+void fpi_device_set_nr_enroll_stages (FpDevice *device,
+ gint enroll_stages);
+
+void fpi_device_set_scan_type (FpDevice *device,
+ FpScanType scan_type);
+
+void fpi_device_action_error (FpDevice *device,
+ GError *error);
+
+void fpi_device_probe_complete (FpDevice *device,
+ const gchar *device_id,
+ const gchar *device_name,
+ GError *error);
+void fpi_device_open_complete (FpDevice *device,
+ GError *error);
+void fpi_device_close_complete (FpDevice *device,
+ GError *error);
+void fpi_device_enroll_complete (FpDevice *device,
+ FpPrint *print,
+ GError *error);
+void fpi_device_verify_complete (FpDevice *device,
+ FpiMatchResult result,
+ FpPrint *print,
+ GError *error);
+void fpi_device_identify_complete (FpDevice *device,
+ FpPrint *match,
+ FpPrint *print,
+ GError *error);
+void fpi_device_capture_complete (FpDevice *device,
+ FpImage *image,
+ GError *error);
+void fpi_device_delete_complete (FpDevice *device,
+ GError *error);
+void fpi_device_list_complete (FpDevice *device,
+ GPtrArray *prints,
+ GError *error);
+
+void fpi_device_enroll_progress (FpDevice *device,
+ gint completed_stages,
+ FpPrint *print,
+ GError *error);
+
+G_END_DECLS
diff --git a/libfprint/fpi-image-device.h b/libfprint/fpi-image-device.h
new file mode 100644
index 0000000..06d1a64
--- /dev/null
+++ b/libfprint/fpi-image-device.h
@@ -0,0 +1,118 @@
+/*
+ * FpImageDevice - An image based fingerprint reader device
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include "fpi-device.h"
+#include "fp-image-device.h"
+
+/**
+ * FpImageDeviceState:
+ * @FP_IMAGE_DEVICE_STATE_INACTIVE: inactive
+ * @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON: waiting for the finger to be pressed or swiped
+ * @FP_IMAGE_DEVICE_STATE_CAPTURE: capturing an image
+ * @FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF: waiting for the finger to be removed
+ *
+ * The state of an imaging device while doing a capture. The state is
+ * passed through to the driver using the ::activate() or ::change_state() vfuncs.
+ *
+ * The driver needs to call fpi_image_device_report_finger_status() to move
+ * between the different states. Note that the capture state might be entered
+ * unconditionally if the device supports raw capturing.
+ */
+typedef enum {
+ FP_IMAGE_DEVICE_STATE_INACTIVE,
+ FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON,
+ FP_IMAGE_DEVICE_STATE_CAPTURE,
+ FP_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF,
+} FpImageDeviceState;
+
+/**
+ * FpImageDeviceClass:
+ * @bz3_threshold: Threshold to consider bozorth3 score a match, default: 40
+ * @img_width: Width of the image, only provide if constant
+ * @img_height: Height of the image, only provide if constant
+ * @img_open: Open the device and do basic initialization
+ * (use this instead of the #FpDeviceClass open vfunc)
+ * @img_close: Close the device
+ * (use this instead of the #FpDeviceClass close vfunc)
+ * @activate: Start image capture and finger detection
+ * @deactivate: Stop image capture and finger detection
+ * @change_state: Notification about the current device state (i.e. waiting for
+ * finger or image capture). Implementing this is optional, it can e.g. be
+ * used to flash an LED when waiting for a finger.
+ *
+ * These are the main entry points for image based drivers. For all but the
+ * change_state vfunc, implementations *must* eventually call the corresponding
+ * function to finish the operation. It is also acceptable to call the generic
+ *
+ *
+ * These are the main entry points for drivers to implement. Drivers may not
+ * implement all of these entry points if they do not support the operation
+ * (or a default implementation is sufficient).
+ *
+ * Drivers *must* eventually call the corresponding function to finish the
+ * operation. It is also acceptable to call the generic
+ * fpi_device_action_error() function but doing so is not recommended in most
+ * usecases.
+ *
+ * Drivers *must* also handle cancellation properly for any long running
+ * operation (i.e. any operation that requires capturing). It is entirely fine
+ * to ignore cancellation requests for short operations (e.g. open/close).
+ *
+ * This API is solely intended for drivers. It is purely internal and neither
+ * API nor ABI stable.
+ */
+struct _FpImageDeviceClass
+{
+ FpDeviceClass parent_class;
+
+ gint bz3_threshold;
+ gint img_width;
+ gint img_height;
+
+ void (*img_open) (FpImageDevice *dev);
+ void (*img_close) (FpImageDevice *dev);
+ void (*activate) (FpImageDevice *dev);
+ void (*change_state) (FpImageDevice *dev,
+ FpImageDeviceState state);
+ void (*deactivate) (FpImageDevice *dev);
+};
+
+void fpi_image_device_set_bz3_threshold (FpImageDevice *self,
+ gint bz3_threshold);
+
+void fpi_image_device_session_error (FpImageDevice *self,
+ GError *error);
+
+void fpi_image_device_open_complete (FpImageDevice *self,
+ GError *error);
+void fpi_image_device_close_complete (FpImageDevice *self,
+ GError *error);
+void fpi_image_device_activate_complete (FpImageDevice *self,
+ GError *error);
+void fpi_image_device_deactivate_complete (FpImageDevice *self,
+ GError *error);
+
+void fpi_image_device_report_finger_status (FpImageDevice *self,
+ gboolean present);
+void fpi_image_device_image_captured (FpImageDevice *self,
+ FpImage *image);
+void fpi_image_device_retry_scan (FpImageDevice *self,
+ FpDeviceRetry retry);
diff --git a/libfprint/fpi-image.h b/libfprint/fpi-image.h
new file mode 100644
index 0000000..dd6dbf8
--- /dev/null
+++ b/libfprint/fpi-image.h
@@ -0,0 +1,83 @@
+/*
+ * FPrint Image
+ * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include
+#include "fp-image.h"
+
+/**
+ * FpiImageFlags:
+ * @FPI_IMAGE_V_FLIPPED: the image is vertically flipped
+ * @FPI_IMAGE_H_FLIPPED: the image is horizontally flipped
+ * @FPI_IMAGE_COLORS_INVERTED: the colours are inverted
+ *
+ * Flags used in an #FpImage structure to describe the contained image.
+ * This is useful for image drivers as they can simply set these flags and
+ * rely on the image to be normalized by libfprint before further processing.
+ */
+typedef enum {
+ FPI_IMAGE_V_FLIPPED = 1 << 0,
+ FPI_IMAGE_H_FLIPPED = 1 << 1,
+ FPI_IMAGE_COLORS_INVERTED = 1 << 2,
+} FpiImageFlags;
+
+/**
+ * FpImage:
+ * @width: Width of the image
+ * @height: Height of the image
+ * @ppmm: Pixels per millimeter
+ * @flags: #FpiImageFlags for required normalization
+ *
+ * Structure holding an image. The public fields are only public for internal
+ * use by the drivers.
+ */
+struct _FpImage
+{
+ /*< private >*/
+ GObject parent;
+
+ /*< public >*/
+ guint width;
+ guint height;
+
+ gdouble ppmm;
+
+ FpiImageFlags flags;
+
+ /*< private >*/
+ guint8 *data;
+ guint8 *binarized;
+
+ GPtrArray *minutiae;
+ guint ref_count;
+};
+
+gint fpi_std_sq_dev (const guint8 *buf,
+ gint size);
+gint fpi_mean_sq_diff_norm (const guint8 *buf1,
+ const guint8 *buf2,
+ gint size);
+
+#if HAVE_PIXMAN
+FpImage *fpi_image_resize (FpImage *orig,
+ guint w_factor,
+ guint h_factor);
+#endif
diff --git a/libfprint/fpi-img-pixman.c b/libfprint/fpi-img-pixman.c
deleted file mode 100644
index 1c1bedb..0000000
--- a/libfprint/fpi-img-pixman.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Imaging utility functions for libfprint
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2013 Vasily Khoruzhick
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include
-#include
-
-#include "fp_internal.h"
-
-/**
- * fpi_img_resize:
- * @img: an #fp_img image
- * @w_factor: horizontal factor to resize the image by
- * @h_factor: vertical factor to resize the image by
- *
- * Resizes the #fp_img image by scaling it by @w_factor times horizontally
- * and @h_factor times vertically.
- *
- * Returns: a newly allocated #fp_img, the original @img will not be modified
- * and will also need to be freed
- */
-struct fp_img *fpi_img_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
-{
- int new_width = img->width * w_factor;
- int new_height = img->height * h_factor;
- pixman_image_t *orig, *resized;
- pixman_transform_t transform;
- struct fp_img *newimg;
-
- orig = pixman_image_create_bits(PIXMAN_a8, img->width, img->height, (uint32_t *)img->data, img->width);
- resized = pixman_image_create_bits(PIXMAN_a8, new_width, new_height, NULL, new_width);
-
- pixman_transform_init_identity(&transform);
- pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(w_factor), pixman_int_to_fixed(h_factor));
- pixman_image_set_transform(orig, &transform);
- pixman_image_set_filter(orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
- pixman_image_composite32(PIXMAN_OP_SRC,
- orig, /* src */
- NULL, /* mask */
- resized, /* dst */
- 0, 0, /* src x y */
- 0, 0, /* mask x y */
- 0, 0, /* dst x y */
- new_width, new_height /* width height */
- );
-
- newimg = fpi_img_new(new_width * new_height);
- newimg->width = new_width;
- newimg->height = new_height;
- newimg->flags = img->flags;
-
- memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
-
- pixman_image_unref(orig);
- pixman_image_unref(resized);
-
- return newimg;
-}
-
diff --git a/libfprint/fpi-img.c b/libfprint/fpi-img.c
deleted file mode 100644
index c7f44c2..0000000
--- a/libfprint/fpi-img.c
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Image management functions for libfprint
- * Copyright (C) 2007 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include
-#include
-#include
-#include
-
-#include
-
-#include "fp_internal.h"
-#include "nbis/include/bozorth.h"
-#include "nbis/include/lfs.h"
-
-/**
- * SECTION:img
- * @title: Image operations
- * @short_description: Image operation functions
- *
- * libfprint offers several ways of retrieving images from imaging devices,
- * one example being the fp_dev_img_capture() function. The functions
- * documented below allow you to work with such images.
- *
- * # Image format # {#img_fmt}
- * All images are represented as 8-bit greyscale data.
- *
- * # Image standardization # {#img_std}
- * In some contexts, images you are provided through libfprint are raw images
- * from the hardware. The orientation of these varies from device-to-device,
- * as does the color scheme (black-on-white or white-on-black?). libfprint
- * provides the fp_img_standardize() function to convert images into standard
- * form, which is defined to be: finger flesh as black on white surroundings,
- * natural upright orientation.
- */
-
-/**
- * SECTION:fpi-img
- * @title: Driver Image operations
- * @short_description: Driver image operation functions
- *
- * Those are the driver-specific helpers for #fp_img manipulation. See #fp_img's
- * documentation for more information about data formats, and their uses in
- * front-end applications.
- */
-
-/**
- * fpi_img_new:
- * @length: the length of data to allocate
- *
- * Creates a new #fp_img structure with @length bytes of data allocated
- * to hold the image.
- *
- * Returns: a new #fp_img to free with fp_img_free()
- */
-struct fp_img *fpi_img_new(size_t length)
-{
- struct fp_img *img = g_malloc0(sizeof(*img) + length);
- fp_dbg("length=%zd", length);
- img->length = length;
- return img;
-}
-
-/**
- * fpi_img_new_for_imgdev:
- * @imgdev: a #fp_img_dev imaging fingerprint device
- *
- * Creates a new #fp_img structure, like fpi_img_new(), but uses the
- * driver's advertised height and width to calculate the size of the
- * length of data to allocate.
- *
- * Returns: a new #fp_img to free with fp_img_free()
- */
-struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *imgdev)
-{
- struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(FP_DEV(imgdev)->drv);
- int width = imgdrv->img_width;
- int height = imgdrv->img_height;
- struct fp_img *img = fpi_img_new(width * height);
- img->width = width;
- img->height = height;
- return img;
-}
-
-/**
- * fpi_img_is_sane:
- * @img: a #fp_img image
- *
- * Checks whether an #fp_img structure passes some basic checks, such
- * as length, width and height being non-zero, and the buffer being
- * big enough to hold the image of said size.
- *
- * Returns: %TRUE if the image is sane, %FALSE otherwise
- */
-gboolean fpi_img_is_sane(struct fp_img *img)
-{
- guint len;
-
- /* basic checks */
- if (!img->length || img->width <= 0 || img->height <= 0)
- return FALSE;
-
- /* Are width and height just too big? */
- if (!g_uint_checked_mul(&len, img->width, img->height) ||
- len > G_MAXINT)
- return FALSE;
-
- /* buffer big enough? */
- if (len > img->length)
- return FALSE;
-
- return TRUE;
-}
-
-/**
- * fpi_img_realloc:
- * @img: an #fp_img image
- * @newsize: the new length of the image
- *
- * Changes the size of the data part of the #fp_img.
- *
- * Returns: the modified #fp_img, the same as the first argument to this function
- */
-struct fp_img *fpi_img_realloc(struct fp_img *img, size_t newsize)
-{
- return g_realloc(img, sizeof(*img) + newsize);
-}
-
-/**
- * fp_img_free:
- * @img: the image to destroy. If %NULL, function simply returns.
- *
- * Frees an image. Must be called when you are finished working with an image.
- */
-API_EXPORTED void fp_img_free(struct fp_img *img)
-{
- if (!img)
- return;
-
- if (img->minutiae)
- free_minutiae(img->minutiae);
- if (img->binarized)
- free(img->binarized);
- g_free(img);
-}
-
-/**
- * fp_img_get_height:
- * @img: an image
- *
- * Gets the pixel height of an image.
- *
- * Returns: the height of the image
- */
-API_EXPORTED int fp_img_get_height(struct fp_img *img)
-{
- return img->height;
-}
-
-/**
- * fp_img_get_width:
- * @img: an image
- *
- * Gets the pixel width of an image.
- *
- * Returns: the width of the image
- */
-API_EXPORTED int fp_img_get_width(struct fp_img *img)
-{
- return img->width;
-}
-
-/**
- * fp_img_get_data:
- * @img: an image
- *
- * Gets the greyscale data for an image. This data must not be modified or
- * freed, and must not be used after fp_img_free() has been called.
- *
- * Returns: a pointer to libfprint's internal data for the image
- */
-API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img)
-{
- return img->data;
-}
-
-/**
- * fp_img_save_to_file:
- * @img: the image to save
- * @path: the path to save the image. Existing files will be overwritten.
- *
- * A quick convenience function to save an image to a file in
- * [PGM format](http://netpbm.sourceforge.net/doc/pgm.html).
- *
- * Returns: 0 on success, non-zero on error.
- */
-API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path)
-{
- FILE *fd = fopen(path, "w");
- size_t write_size = img->width * img->height;
- int r;
-
- if (!fd) {
- fp_dbg("could not open '%s' for writing: %d", path, errno);
- return -errno;
- }
-
- r = fprintf(fd, "P5 %d %d 255\n", img->width, img->height);
- if (r < 0) {
- fclose(fd);
- fp_err("pgm header write failed, error %d", r);
- return r;
- }
-
- r = fwrite(img->data, 1, write_size, fd);
- if (r < write_size) {
- fclose(fd);
- fp_err("short write (%d)", r);
- return -EIO;
- }
-
- fclose(fd);
- fp_dbg("written to '%s'", path);
- return 0;
-}
-
-static void vflip(struct fp_img *img)
-{
- int width = img->width;
- int data_len = img->width * img->height;
- unsigned char rowbuf[width];
- int i;
-
- for (i = 0; i < img->height / 2; i++) {
- int offset = i * width;
- int swap_offset = data_len - (width * (i + 1));
-
- /* copy top row into buffer */
- memcpy(rowbuf, img->data + offset, width);
-
- /* copy lower row over upper row */
- memcpy(img->data + offset, img->data + swap_offset, width);
-
- /* copy buffer over lower row */
- memcpy(img->data + swap_offset, rowbuf, width);
- }
-}
-
-static void hflip(struct fp_img *img)
-{
- int width = img->width;
- unsigned char rowbuf[width];
- int i, j;
-
- for (i = 0; i < img->height; i++) {
- int offset = i * width;
-
- memcpy(rowbuf, img->data + offset, width);
- for (j = 0; j < width; j++)
- img->data[offset + j] = rowbuf[width - j - 1];
- }
-}
-
-static void invert_colors(struct fp_img *img)
-{
- int data_len = img->width * img->height;
- int i;
- for (i = 0; i < data_len; i++)
- img->data[i] = 0xff - img->data[i];
-}
-
-/**
- * fp_img_standardize:
- * @img: the image to standardize
- *
- * [Standardizes](libfprint-Image-operations.html#img_std) an image by normalizing its orientation, colors,
- * etc. It is safe to call this multiple times on an image, libfprint keeps
- * track of the work it needs to do to make an image standard and will not
- * perform these operations more than once for a given image.
- */
-API_EXPORTED void fp_img_standardize(struct fp_img *img)
-{
- if (img->flags & FP_IMG_V_FLIPPED) {
- vflip(img);
- img->flags &= ~FP_IMG_V_FLIPPED;
- }
- if (img->flags & FP_IMG_H_FLIPPED) {
- hflip(img);
- img->flags &= ~FP_IMG_H_FLIPPED;
- }
- if (img->flags & FP_IMG_COLORS_INVERTED) {
- invert_colors(img);
- img->flags &= ~FP_IMG_COLORS_INVERTED;
- }
-}
-
-/* Based on write_minutiae_XYTQ and bz_load */
-static void minutiae_to_xyt(struct fp_minutiae *minutiae, int bwidth,
- int bheight, unsigned char *buf)
-{
- int i;
- struct fp_minutia *minutia;
- struct minutiae_struct c[MAX_FILE_MINUTIAE];
- struct xyt_struct *xyt = (struct xyt_struct *) buf;
-
- /* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
- int nmin = min(minutiae->num, MAX_BOZORTH_MINUTIAE);
-
- for (i = 0; i < nmin; i++){
- minutia = minutiae->list[i];
-
- lfs2nist_minutia_XYT(&c[i].col[0], &c[i].col[1], &c[i].col[2],
- minutia, bwidth, bheight);
- c[i].col[3] = sround(minutia->reliability * 100.0);
-
- if (c[i].col[2] > 180)
- c[i].col[2] -= 360;
- }
-
- qsort((void *) &c, (size_t) nmin, sizeof(struct minutiae_struct),
- sort_x_y);
-
- for (i = 0; i < nmin; i++) {
- xyt->xcol[i] = c[i].col[0];
- xyt->ycol[i] = c[i].col[1];
- xyt->thetacol[i] = c[i].col[2];
- }
- xyt->nrows = nmin;
-}
-
-#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
- | FP_IMG_COLORS_INVERTED)
-
-static int fpi_img_detect_minutiae(struct fp_img *img)
-{
- struct fp_minutiae *minutiae;
- int r;
- int *direction_map, *low_contrast_map, *low_flow_map;
- int *high_curve_map, *quality_map;
- int map_w, map_h;
- unsigned char *bdata;
- int bw, bh, bd;
- GTimer *timer;
-
- if (img->flags & FP_IMG_STANDARDIZATION_FLAGS) {
- fp_err("Cannot detect minutiae for non-standardized images");
- return -EINVAL;
- }
-
- /* 25.4 mm per inch */
- timer = g_timer_new();
- r = get_minutiae(&minutiae, &quality_map, &direction_map,
- &low_contrast_map, &low_flow_map, &high_curve_map,
- &map_w, &map_h, &bdata, &bw, &bh, &bd,
- img->data, img->width, img->height, 8,
- DEFAULT_PPI / (double)25.4, &g_lfsparms_V2);
- g_timer_stop(timer);
- fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
- g_timer_destroy(timer);
- if (r) {
- fp_err("get minutiae failed, code %d", r);
- return r;
- }
- fp_dbg("detected %d minutiae", minutiae->num);
- img->minutiae = minutiae;
- img->binarized = bdata;
-
- free(quality_map);
- free(direction_map);
- free(low_contrast_map);
- free(low_flow_map);
- free(high_curve_map);
- return minutiae->num;
-}
-
-int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
- struct fp_print_data **ret)
-{
- struct fp_print_data *print;
- struct fp_print_data_item *item;
- int r;
-
- if (!img->minutiae) {
- r = fpi_img_detect_minutiae(img);
- if (r < 0)
- return r;
- if (!img->minutiae) {
- fp_err("no minutiae after successful detection?");
- return -ENOENT;
- }
- }
-
- /* FIXME: space is wasted if we don't hit the max minutiae count. would
- * be good to make this dynamic. */
- print = fpi_print_data_new(FP_DEV(imgdev));
- item = fpi_print_data_item_new(sizeof(struct xyt_struct));
- print->type = PRINT_DATA_NBIS_MINUTIAE;
- minutiae_to_xyt(img->minutiae, img->width, img->height, item->data);
- print->prints = g_slist_prepend(print->prints, item);
-
- /* FIXME: the print buffer at this point is endian-specific, and will
- * only work when loaded onto machines with identical endianness. not good!
- * data format should be platform-independent. */
- *ret = print;
-
- return 0;
-}
-
-int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
- struct fp_print_data *new_print)
-{
- int score, max_score = 0, probe_len;
- struct xyt_struct *pstruct = NULL;
- struct xyt_struct *gstruct = NULL;
- struct fp_print_data_item *data_item;
- GSList *list_item;
-
- if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
- new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
- fp_err("invalid print format");
- return -EINVAL;
- }
-
- if (g_slist_length(new_print->prints) != 1) {
- fp_err("new_print contains more than one sample, is it enrolled print?");
- return -EINVAL;
- }
-
- data_item = new_print->prints->data;
- pstruct = (struct xyt_struct *)data_item->data;
-
- probe_len = bozorth_probe_init(pstruct);
- list_item = enrolled_print->prints;
- do {
- data_item = list_item->data;
- gstruct = (struct xyt_struct *)data_item->data;
- score = bozorth_to_gallery(probe_len, pstruct, gstruct);
- fp_dbg("score %d", score);
- max_score = max(score, max_score);
- list_item = g_slist_next(list_item);
- } while (list_item);
-
- return max_score;
-}
-
-int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
- struct fp_print_data **gallery, int match_threshold, size_t *match_offset)
-{
- struct xyt_struct *pstruct;
- struct xyt_struct *gstruct;
- struct fp_print_data *gallery_print;
- struct fp_print_data_item *data_item;
- int probe_len;
- size_t i = 0;
- int r;
- GSList *list_item;
-
- if (g_slist_length(print->prints) != 1) {
- fp_err("new_print contains more than one sample, is it enrolled print?");
- return -EINVAL;
- }
-
- data_item = print->prints->data;
- pstruct = (struct xyt_struct *)data_item->data;
-
- probe_len = bozorth_probe_init(pstruct);
- while ((gallery_print = gallery[i++])) {
- list_item = gallery_print->prints;
- do {
- data_item = list_item->data;
- gstruct = (struct xyt_struct *)data_item->data;
- r = bozorth_to_gallery(probe_len, pstruct, gstruct);
- if (r >= match_threshold) {
- *match_offset = i - 1;
- return FP_VERIFY_MATCH;
- }
- list_item = g_slist_next(list_item);
- } while (list_item);
- }
- return FP_VERIFY_NO_MATCH;
-}
-
-/**
- * fp_img_binarize:
- * @img: a standardized image
- *
- * Get a binarized form of a standardized scanned image. This is where the
- * fingerprint image has been "enhanced" and is a set of pure black ridges
- * on a pure white background. Internally, image processing happens on top
- * of the binarized image.
- *
- * The image must have been [standardized](libfprint-Image-operations.html#img_std)
- * otherwise this function will fail.
- *
- * It is safe to binarize an image and free the original while continuing
- * to use the binarized version.
- *
- * You cannot binarize an image twice.
- *
- * Returns: a new image representing the binarized form of the original, or
- * %NULL on error. Must be freed with fp_img_free() after use.
- */
-API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
-{
- struct fp_img *ret;
- int height = img->height;
- int width = img->width;
- int imgsize = height * width;
-
- if (img->flags & FP_IMG_BINARIZED_FORM) {
- fp_err("image already binarized");
- return NULL;
- }
-
- if (!img->binarized) {
- int r = fpi_img_detect_minutiae(img);
- if (r < 0)
- return NULL;
- if (!img->binarized) {
- fp_err("no minutiae after successful detection?");
- return NULL;
- }
- }
-
- ret = fpi_img_new(imgsize);
- ret->flags |= FP_IMG_BINARIZED_FORM;
- ret->width = width;
- ret->height = height;
- memcpy(ret->data, img->binarized, imgsize);
- return ret;
-}
-
-/**
- * fp_img_get_minutiae:
- * @img: a standardized image
- * @nr_minutiae: an output location to store minutiae list length
- *
- * Get a list of minutiae detected in an image. A minutia point is a feature
- * detected on a fingerprint, typically where ridges end or split.
- * libfprint's image processing code relies upon comparing sets of minutiae,
- * so accurate placement of minutia points is critical for good imaging
- * performance.
- *
- * The image must have been [standardized](libfprint-Image-operations.html#img_std)
- * otherwise this function will fail.
- *
- * You cannot pass a binarized image to this function. Instead, pass the
- * original image.
- *
- * Returns a list of pointers to minutiae, where the list is of length
- * indicated in the nr_minutiae output parameter. The returned list is only
- * valid while the parent image has not been freed, and the minutiae data
- * must not be modified or freed.
- *
- * Returns: a list of minutiae points. Must not be modified or freed.
- */
-API_EXPORTED struct fp_minutia **fp_img_get_minutiae(struct fp_img *img,
- int *nr_minutiae)
-{
- if (img->flags & FP_IMG_BINARIZED_FORM) {
- fp_err("image is binarized");
- return NULL;
- }
-
- if (!img->minutiae) {
- int r = fpi_img_detect_minutiae(img);
- if (r < 0)
- return NULL;
- if (!img->minutiae) {
- fp_err("no minutiae after successful detection?");
- return NULL;
- }
- }
-
- *nr_minutiae = img->minutiae->num;
- return img->minutiae->list;
-}
-
-/**
- * fp_minutia_get_coords:
- * @minutia: a struct #fp_minutia
- * @coord_x: the return variable for the X coordinate of the minutia
- * @coord_y: the return variable for the Y coordinate of the minutia
- *
- * Sets @coord_x and @coord_y to be the coordinates of the detected minutia, so it
- * can be presented in a more verbose user interface. This is usually only
- * used for debugging purposes.
- *
- * Returns: 0 on success, -1 on error.
- */
-API_EXPORTED int fp_minutia_get_coords(struct fp_minutia *minutia, int *coord_x, int *coord_y)
-{
- g_return_val_if_fail (minutia != NULL, -1);
- g_return_val_if_fail (coord_x != NULL, -1);
- g_return_val_if_fail (coord_y != NULL, -1);
-
- *coord_x = minutia->x;
- *coord_y = minutia->y;
-
- return 0;
-}
-
-/**
- * fpi_std_sq_dev:
- * @buf: buffer (usually bitmap, one byte per pixel)
- * @size: size of @buffer
- *
- * Calculates the squared standard deviation of the individual
- * pixels in the buffer, as per the following formula:
- * |[
- * mean = sum (buf[0..size]) / size
- * sq_dev = sum ((buf[0.size] - mean) ^ 2)
- * ]|
- * This function is usually used to determine whether image
- * is empty.
- *
- * Returns: the squared standard deviation for @buffer
- */
-int fpi_std_sq_dev(const unsigned char *buf, int size)
-{
- int res = 0, mean = 0, i;
-
- if (size > (INT_MAX / 65536)) {
- fp_err("%s: we might get an overflow!", __func__);
- return -EOVERFLOW;
- }
-
- for (i = 0; i < size; i++)
- mean += buf[i];
-
- mean /= size;
-
- for (i = 0; i < size; i++) {
- int dev = (int)buf[i] - mean;
- res += dev*dev;
- }
-
- return res / size;
-}
-
-/**
- * fpi_mean_sq_diff_norm:
- * @buf1: buffer (usually bitmap, one byte per pixel)
- * @buf2: buffer (usually bitmap, one byte per pixel)
- * @size: buffer size of smallest buffer
- *
- * This function calculates the normalized mean square difference of
- * two buffers, usually two lines, as per the following formula:
- * |[
- * sq_diff = sum ((buf1[0..size] - buf2[0..size]) ^ 2) / size
- * ]|
- *
- * This functions is usually used to get numerical difference
- * between two images.
- *
- * Returns: the normalized mean squared difference between @buf1 and @buf2
- */
-int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size)
-{
- int res = 0, i;
- for (i = 0; i < size; i++) {
- int dev = (int)buf1[i] - (int)buf2[i];
- res += dev * dev;
- }
-
- return res / size;
-}
diff --git a/libfprint/fpi-img.h b/libfprint/fpi-img.h
deleted file mode 100644
index 4ebc3f3..0000000
--- a/libfprint/fpi-img.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_IMG_H__
-#define __FPI_IMG_H__
-
-#include
-
-struct fp_minutiae;
-
-/**
- * FpiImgFlags:
- * @FP_IMG_V_FLIPPED: the image is vertically flipped
- * @FP_IMG_H_FLIPPED: the image is horizontally flipped
- * @FP_IMG_COLORS_INVERTED: the colours are inverted
- * @FP_IMG_BINARIZED_FORM: binarised image, see fp_img_binarize()
- * @FP_IMG_PARTIAL: the image is partial, useful for driver to keep track
- * of incomplete captures
- *
- * Flags used in the #fp_img structure to describe the image contained
- * into the structure. Note that a number of functions will refuse to
- * handle images which haven't been standardised through fp_img_standardize()
- * (meaning the @FP_IMG_V_FLIPPED, @FP_IMG_H_FLIPPED and @FP_IMG_COLORS_INVERTED
- * should all be unset when the image needs to be analysed).
- */
-typedef enum {
- FP_IMG_V_FLIPPED = 1 << 0,
- FP_IMG_H_FLIPPED = 1 << 1,
- FP_IMG_COLORS_INVERTED = 1 << 2,
- FP_IMG_BINARIZED_FORM = 1 << 3,
- FP_IMG_PARTIAL = 1 << 4
-} FpiImgFlags;
-
-/**
- * fp_img:
- * @width: the width of the image
- * @height: the height of the image
- * @length: the length of the data associated with the image
- * @flags: @FpiImgFlags flags describing the image contained in the structure
- * @minutiae: an opaque structure representing the detected minutiae
- * @binarized: the binarized image data
- * @data: the start of the image data, which will be of @length size.
- *
- * A structure representing a captured, or processed image. The @flags member
- * will show its current state, including whether whether the binarized form
- * if present, whether it is complete, and whether it needs particular changes
- * before being processed.
- */
-struct fp_img {
- int width;
- int height;
- size_t length;
- FpiImgFlags flags;
- /*< private >*/
- struct fp_minutiae *minutiae;
- /*< public >*/
- unsigned char *binarized;
- unsigned char data[0];
-};
-
-struct fp_img *fpi_img_new(size_t length);
-struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *imgdev);
-struct fp_img *fpi_img_realloc(struct fp_img *img, size_t newsize);
-struct fp_img *fpi_img_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor);
-
-int fpi_std_sq_dev(const unsigned char *buf, int size);
-int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size);
-
-#endif
diff --git a/libfprint/fpi-poll.c b/libfprint/fpi-poll.c
deleted file mode 100644
index 3f2c898..0000000
--- a/libfprint/fpi-poll.c
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Polling/timing management
- * Copyright (C) 2008 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define FP_COMPONENT "poll"
-
-#include "fp_internal.h"
-#include "fpi-poll.h"
-
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-/**
- * SECTION:events
- * @title: Initialisation and events handling
- * @short_description: Initialisation and events handling functions
- *
- * These functions are only applicable to users of libfprint's asynchronous
- * API.
- *
- * libfprint does not create internal library threads and hence can only
- * execute when your application is calling a libfprint function. However,
- * libfprint often has work to be do, such as handling of completed USB
- * transfers, and processing of timeouts required in order for the library
- * to function. Therefore it is essential that your own application must
- * regularly "phone into" libfprint so that libfprint can handle any pending
- * events.
- *
- * The function you must call is fp_handle_events() or a variant of it. This
- * function will handle any pending events, and it is from this context that
- * all asynchronous event callbacks from the library will occur. You can view
- * this function as a kind of iteration function.
- *
- * If there are no events pending, fp_handle_events() will block for a few
- * seconds (and will handle any new events should anything occur in that time).
- * If you wish to customise this timeout, you can use
- * fp_handle_events_timeout() instead. If you wish to do a non-blocking
- * iteration, call fp_handle_events_timeout() with a zero timeout.
- *
- * How to integrate events handling depends on your main loop implementation.
- * The sister fprintd project includes an implementation of main loop handling
- * that integrates into GLib's main loop. The
- * [libusb documentation](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
- * also includes more details about how to integrate libfprint events into
- * your main loop.
- */
-
-/**
- * SECTION:fpi-poll
- * @title: Timeouts
- * @short_description: Timeout handling helpers
- *
- * Helper functions to schedule a function call to be made after a timeout. This
- * is useful to avoid making blocking calls while waiting for hardware to answer
- * for example.
- */
-
-/* this is a singly-linked list of pending timers, sorted with the timer that
- * is expiring soonest at the head. */
-static GSList *active_timers = NULL;
-
-/* notifiers for added or removed poll fds */
-static fp_pollfd_added_cb fd_added_cb = NULL;
-static fp_pollfd_removed_cb fd_removed_cb = NULL;
-
-struct fpi_timeout {
- struct timeval expiry;
- fpi_timeout_fn callback;
- struct fp_dev *dev;
- void *data;
- char *name;
-};
-
-static int timeout_sort_fn(gconstpointer _a, gconstpointer _b)
-{
- fpi_timeout *a = (fpi_timeout *) _a;
- fpi_timeout *b = (fpi_timeout *) _b;
- struct timeval *tv_a = &a->expiry;
- struct timeval *tv_b = &b->expiry;
-
- if (timercmp(tv_a, tv_b, <))
- return -1;
- else if (timercmp(tv_a, tv_b, >))
- return 1;
- else
- return 0;
-}
-
-static void
-fpi_timeout_free(fpi_timeout *timeout)
-{
- if (timeout == NULL)
- return;
-
- g_free(timeout->name);
- g_free(timeout);
-}
-
-/**
- * fpi_timeout_set_name:
- * @timeout: a #fpi_timeout
- * @name: the name to give the timeout
- *
- * Sets a name for a timeout, allowing that name to be printed
- * along with any timeout related debug.
- */
-void
-fpi_timeout_set_name(fpi_timeout *timeout,
- const char *name)
-{
- g_return_if_fail (timeout != NULL);
- g_return_if_fail (name != NULL);
- g_return_if_fail (timeout->name == NULL);
-
- timeout->name = g_strdup(name);
-}
-
-/**
- * fpi_timeout_add:
- * @msec: the time before calling the function, in milliseconds (1/1000ths of a second)
- * @callback: function to callback
- * @dev: a struct #fp_dev
- * @data: data to pass to @callback, or %NULL
- *
- * A timeout is the asynchronous equivalent of sleeping. You create a timeout
- * saying that you'd like to have a function invoked at a certain time in
- * the future.
- *
- * Note that you should hold onto the return value of this function to cancel it
- * use fpi_timeout_cancel(), otherwise the callback could be called while the driver
- * is being torn down.
- *
- * This function can be considered to never fail.
- *
- * Returns: an #fpi_timeout structure
- */
-fpi_timeout *fpi_timeout_add(unsigned int msec,
- fpi_timeout_fn callback,
- struct fp_dev *dev,
- void *data)
-{
- struct timespec ts;
- struct timeval add_msec;
- fpi_timeout *timeout;
- int r;
-
- g_return_val_if_fail (dev != NULL, NULL);
-
- fp_dbg("in %dms", msec);
-
- r = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (r < 0) {
- fp_err("failed to read monotonic clock, errno=%d", errno);
- BUG();
- return NULL;
- }
-
- timeout = g_new0(fpi_timeout, 1);
- timeout->callback = callback;
- timeout->dev = dev;
- timeout->data = data;
- TIMESPEC_TO_TIMEVAL(&timeout->expiry, &ts);
-
- /* calculate timeout expiry by adding delay to current monotonic clock */
- timerclear(&add_msec);
- add_msec.tv_sec = msec / 1000;
- add_msec.tv_usec = (msec % 1000) * 1000;
- timeradd(&timeout->expiry, &add_msec, &timeout->expiry);
-
- active_timers = g_slist_insert_sorted(active_timers, timeout,
- timeout_sort_fn);
-
- return timeout;
-}
-
-/**
- * fpi_timeout_cancel:
- * @timeout: an #fpi_timeout structure
- *
- * Cancels a timeout scheduled with fpi_timeout_add(), and frees the
- * @timeout structure.
- */
-void fpi_timeout_cancel(fpi_timeout *timeout)
-{
- G_DEBUG_HERE();
- active_timers = g_slist_remove(active_timers, timeout);
- fpi_timeout_free(timeout);
-}
-
-/* get the expiry time and optionally the timeout structure for the next
- * timeout. returns 0 if there are no expired timers, or 1 if the
- * timeval/timeout output parameters were populated. if the returned timeval
- * is zero then it means the timeout has already expired and should be handled
- * ASAP. */
-static int get_next_timeout_expiry(struct timeval *out,
- struct fpi_timeout **out_timeout)
-{
- struct timespec ts;
- struct timeval tv;
- struct fpi_timeout *next_timeout;
- int r;
-
- if (active_timers == NULL)
- return 0;
-
- r = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (r < 0) {
- fp_err("failed to read monotonic clock, errno=%d", errno);
- return r;
- }
- TIMESPEC_TO_TIMEVAL(&tv, &ts);
-
- next_timeout = active_timers->data;
- if (out_timeout)
- *out_timeout = next_timeout;
-
- if (timercmp(&tv, &next_timeout->expiry, >=)) {
- if (next_timeout->name)
- fp_dbg("first timeout '%s' already expired", next_timeout->name);
- else
- fp_dbg("first timeout already expired");
- timerclear(out);
- } else {
- timersub(&next_timeout->expiry, &tv, out);
- if (next_timeout->name)
- fp_dbg("next timeout '%s' in %ld.%06lds", next_timeout->name,
- out->tv_sec, out->tv_usec);
- else
- fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
- }
-
- return 1;
-}
-
-/* handle a timeout that has expired */
-static void handle_timeout(struct fpi_timeout *timeout)
-{
- G_DEBUG_HERE();
- timeout->callback(timeout->dev, timeout->data);
- active_timers = g_slist_remove(active_timers, timeout);
- fpi_timeout_free(timeout);
-}
-
-static int handle_timeouts(void)
-{
- struct timeval next_timeout_expiry;
- struct fpi_timeout *next_timeout;
- int r;
-
- r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
- if (r <= 0)
- return r;
-
- if (!timerisset(&next_timeout_expiry))
- handle_timeout(next_timeout);
-
- return 0;
-}
-
-/**
- * fp_handle_events_timeout:
- * @timeout: Maximum timeout for this blocking function
- *
- * Handle any pending events. If a non-zero timeout is specified, the function
- * will potentially block for the specified amount of time, although it may
- * return sooner if events have been handled. The function acts as non-blocking
- * for a zero timeout.
- *
- * Returns: 0 on success, non-zero on error.
- */
-API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
-{
- struct timeval next_timeout_expiry;
- struct timeval select_timeout;
- struct fpi_timeout *next_timeout;
- int r;
-
- r = get_next_timeout_expiry(&next_timeout_expiry, &next_timeout);
- if (r < 0)
- return r;
-
- if (r) {
- /* timer already expired? */
- if (!timerisset(&next_timeout_expiry)) {
- handle_timeout(next_timeout);
- return 0;
- }
-
- /* choose the smallest of next URB timeout or user specified timeout */
- if (timercmp(&next_timeout_expiry, timeout, <))
- select_timeout = next_timeout_expiry;
- else
- select_timeout = *timeout;
- } else {
- select_timeout = *timeout;
- }
-
- r = libusb_handle_events_timeout(fpi_usb_ctx, &select_timeout);
- *timeout = select_timeout;
- if (r < 0)
- return r;
-
- return handle_timeouts();
-}
-
-/**
- * fp_handle_events:
- *
- * Convenience function for calling fp_handle_events_timeout() with a sensible
- * default timeout value of two seconds (subject to change if we decide another
- * value is more sensible).
- *
- * Returns: 0 on success, non-zero on error.
- */
-API_EXPORTED int fp_handle_events(void)
-{
- struct timeval tv;
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- return fp_handle_events_timeout(&tv);
-}
-
-/**
- * fp_get_next_timeout:
- * @tv: a #timeval structure containing the duration to the next timeout.
- *
- * A zero filled @tv timeout means events are to be handled immediately
- *
- * Returns: returns 0 if no timeouts active, or 1 if timeout returned.
- */
-API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
-{
- struct timeval fprint_timeout = { 0, 0 };
- struct timeval libusb_timeout = { 0, 0 };
- int r_fprint;
- int r_libusb;
-
- r_fprint = get_next_timeout_expiry(&fprint_timeout, NULL);
- r_libusb = libusb_get_next_timeout(fpi_usb_ctx, &libusb_timeout);
-
- /* if we have no pending timeouts and the same is true for libusb,
- * indicate that we have no pending timouts */
- if (r_fprint <= 0 && r_libusb <= 0)
- return 0;
-
- /* if fprint have no pending timeouts return libusb timeout */
- else if (r_fprint == 0)
- *tv = libusb_timeout;
-
- /* if libusb have no pending timeouts return fprint timeout */
- else if (r_libusb == 0)
- *tv = fprint_timeout;
-
- /* otherwise return the smaller of the 2 timeouts */
- else if (timercmp(&fprint_timeout, &libusb_timeout, <))
- *tv = fprint_timeout;
- else
- *tv = libusb_timeout;
- return 1;
-}
-
-/**
- * fp_get_pollfds:
- * @pollfds: output location for a list of pollfds. If non-%NULL, must be
- * released with free() when done.
- *
- * Retrieve a list of file descriptors that should be polled for events
- * interesting to libfprint. This function is only for users who wish to
- * combine libfprint's file descriptor set with other event sources – more
- * simplistic users will be able to call fp_handle_events() or a variant
- * directly.
- *
- * Returns: the number of pollfds in the resultant list, or negative on error.
- */
-API_EXPORTED ssize_t fp_get_pollfds(struct fp_pollfd **pollfds)
-{
- const struct libusb_pollfd **usbfds;
- const struct libusb_pollfd *usbfd;
- struct fp_pollfd *ret;
- ssize_t cnt = 0;
- size_t i = 0;
-
- g_return_val_if_fail (fpi_usb_ctx != NULL, -EIO);
-
- usbfds = libusb_get_pollfds(fpi_usb_ctx);
- if (!usbfds) {
- *pollfds = NULL;
- return -EIO;
- }
-
- while ((usbfd = usbfds[i++]) != NULL)
- cnt++;
-
- ret = g_malloc(sizeof(struct fp_pollfd) * cnt);
- i = 0;
- while ((usbfd = usbfds[i]) != NULL) {
- ret[i].fd = usbfd->fd;
- ret[i].events = usbfd->events;
- i++;
- }
-
- *pollfds = ret;
- return cnt;
-}
-
-/**
- * fp_set_pollfd_notifiers:
- * @added_cb: a #fp_pollfd_added_cb callback or %NULL
- * @removed_cb: a #fp_pollfd_removed_cb callback or %NULL
- *
- * This sets the callback functions to call for every new or removed
- * file descriptor used as an event source.
- */
-API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
- fp_pollfd_removed_cb removed_cb)
-{
- fd_added_cb = added_cb;
- fd_removed_cb = removed_cb;
-}
-
-static void add_pollfd(int fd, short events, void *user_data)
-{
- if (fd_added_cb)
- fd_added_cb(fd, events);
-}
-
-static void remove_pollfd(int fd, void *user_data)
-{
- if (fd_removed_cb)
- fd_removed_cb(fd);
-}
-
-void fpi_poll_init(void)
-{
- libusb_set_pollfd_notifiers(fpi_usb_ctx, add_pollfd, remove_pollfd, NULL);
-}
-
-void fpi_poll_exit(void)
-{
- g_slist_free_full(active_timers, (GDestroyNotify) fpi_timeout_free);
- active_timers = NULL;
- fd_added_cb = NULL;
- fd_removed_cb = NULL;
- libusb_set_pollfd_notifiers(fpi_usb_ctx, NULL, NULL, NULL);
-}
-
-void
-fpi_timeout_cancel_all_for_dev(struct fp_dev *dev)
-{
- GSList *l;
-
- g_return_if_fail (dev != NULL);
-
- l = active_timers;
- while (l) {
- struct fpi_timeout *timeout = l->data;
- GSList *current = l;
-
- l = l->next;
- if (timeout->dev == dev) {
- g_free (timeout);
- active_timers = g_slist_delete_link (active_timers, current);
- }
- }
-}
diff --git a/libfprint/fpi-poll.h b/libfprint/fpi-poll.h
deleted file mode 100644
index 2682f27..0000000
--- a/libfprint/fpi-poll.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 Daniel Drake
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_POLL_H__
-#define __FPI_POLL_H__
-
-#include "fprint.h"
-
-/**
- * fpi_timeout_fn:
- * @dev: the struct #fp_dev passed to fpi_timeout_add()
- * @data: the data passed to fpi_timeout_add()
- *
- * The prototype of the callback function for fpi_timeout_add().
- * Note that after the callback is called, the #fpi_timeout structure will
- * be freed.
- */
-typedef void (*fpi_timeout_fn)(struct fp_dev *dev, void *data);
-
-/**
- * fpi_timeout:
- *
- * An opaque structure representing a scheduled function call, created with
- * fpi_timeout_add().
- */
-typedef struct fpi_timeout fpi_timeout;
-fpi_timeout *fpi_timeout_add(unsigned int msec,
- fpi_timeout_fn callback,
- struct fp_dev *dev,
- void *data);
-void fpi_timeout_set_name(fpi_timeout *timeout,
- const char *name);
-void fpi_timeout_cancel(fpi_timeout *timeout);
-
-#endif
diff --git a/libfprint/fpi-print.h b/libfprint/fpi-print.h
new file mode 100644
index 0000000..fe07c26
--- /dev/null
+++ b/libfprint/fpi-print.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "fpi-enums.h"
+#include "fp-device.h"
+#include "fp-print.h"
+
+G_BEGIN_DECLS
+
+/**
+ * FpPrintType:
+ * @FP_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment
+ * @FP_PRINT_RAW: A raw print where the data is directly compared
+ * @FP_PRINT_NBIS: NBIS minutiae comparison
+ */
+typedef enum {
+ FP_PRINT_UNDEFINED = 0,
+ FP_PRINT_RAW,
+ FP_PRINT_NBIS,
+} FpPrintType;
+
+/**
+ * FpiMatchResult:
+ * @FPI_MATCH_ERROR: An error occured during matching
+ * @FPI_MATCH_SUCCESS: The prints matched
+ * @FPI_MATCH_FAIL: The prints did not match
+ */
+typedef enum {
+ FPI_MATCH_ERROR = 0,
+ FPI_MATCH_SUCCESS,
+ FPI_MATCH_FAIL,
+} FpiMatchResult;
+
+void fpi_print_add_print (FpPrint *print,
+ FpPrint *add);
+
+void fpi_print_set_type (FpPrint *print,
+ FpPrintType type);
+void fpi_print_set_device_stored (FpPrint *print,
+ gboolean device_stored);
+
+gboolean fpi_print_add_from_image (FpPrint *print,
+ FpImage *image,
+ GError **error);
+
+FpiMatchResult fpi_print_bz3_match (FpPrint * template,
+ FpPrint *print,
+ gint bz3_threshold,
+ GError **error);
+
+G_END_DECLS
diff --git a/libfprint/fpi-ssm.c b/libfprint/fpi-ssm.c
index 64430c1..2fb2565 100644
--- a/libfprint/fpi-ssm.c
+++ b/libfprint/fpi-ssm.c
@@ -1,6 +1,7 @@
/*
* Functions to assist with asynchronous driver <---> library communications
* Copyright (C) 2007-2008 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,13 +18,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define FP_COMPONENT "drv"
+#define FP_COMPONENT "SSM"
-#include "fp_internal.h"
+#include "drivers_api.h"
#include "fpi-ssm.h"
-#include
-#include
/**
* SECTION:fpi-ssm
@@ -32,7 +31,7 @@
*
* Asynchronous driver design encourages some kind of state machine behind it.
* In most cases, the state machine is entirely linear - you only go to the
- * next state, you never jump or go backwards. The #fpi_ssm functions help you
+ * next state, you never jump or go backwards. The #FpiSsm functions help you
* implement such a machine.
*
* e.g. `S1` ↦ `S2` ↦ `S3` ↦ `S4`
@@ -72,20 +71,21 @@
* upon success (or fails).
*
* Your completion callback should examine the return value of
- * fpi_ssm_get_error() in order to determine whether the #fpi_ssm completed or
+ * fpi_ssm_get_error() in ordater to determine whether the #FpiSsm completed or
* failed. An error code of zero indicates successful completion.
*/
-struct fpi_ssm {
- struct fp_dev *dev;
- fpi_ssm *parentsm;
- void *user_data;
- int nr_states;
- int cur_state;
- gboolean completed;
- int error;
- ssm_completed_fn callback;
- ssm_handler_fn handler;
+struct _FpiSsm
+{
+ FpDevice *dev;
+ FpiSsm *parentsm;
+ void *user_data;
+ int nr_states;
+ int cur_state;
+ gboolean completed;
+ GError *error;
+ FpiSsmCompletedCallback callback;
+ FpiSsmHandlerCallback handler;
};
/**
@@ -98,28 +98,30 @@ struct fpi_ssm {
* Allocate a new ssm, with @nr_states states. The @handler callback
* will be called after each state transition.
*
- * Returns: a new #fpi_ssm state machine
+ * Returns: a new #FpiSsm state machine
*/
-fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
- ssm_handler_fn handler,
- int nr_states,
- void *user_data)
+FpiSsm *
+fpi_ssm_new (FpDevice *dev,
+ FpiSsmHandlerCallback handler,
+ int nr_states,
+ void *user_data)
{
- fpi_ssm *machine;
- BUG_ON(nr_states < 1);
+ FpiSsm *machine;
- machine = g_malloc0(sizeof(*machine));
- machine->handler = handler;
- machine->nr_states = nr_states;
- machine->dev = dev;
- machine->completed = TRUE;
- machine->user_data = user_data;
- return machine;
+ BUG_ON (nr_states < 1);
+
+ machine = g_malloc0 (sizeof (*machine));
+ machine->handler = handler;
+ machine->nr_states = nr_states;
+ machine->dev = dev;
+ machine->completed = TRUE;
+ machine->user_data = user_data;
+ return machine;
}
/**
* fpi_ssm_get_user_data:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Retrieve the pointer to user data set when fpi_ssm_new()
* is called.
@@ -127,66 +129,73 @@ fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
* Returns: a pointer
*/
void *
-fpi_ssm_get_user_data(fpi_ssm *machine)
+fpi_ssm_get_user_data (FpiSsm *machine)
{
- return machine->user_data;
+ return machine->user_data;
}
/**
* fpi_ssm_free:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Frees a state machine. This does not call any error or success
* callbacks, so you need to do this yourself.
*/
-void fpi_ssm_free(fpi_ssm *machine)
+void
+fpi_ssm_free (FpiSsm *machine)
{
- if (!machine)
- return;
- g_free(machine);
+ if (!machine)
+ return;
+
+ g_clear_pointer (&machine->error, g_error_free);
+ g_free (machine);
}
/* Invoke the state handler */
-static void __ssm_call_handler(fpi_ssm *machine)
+static void
+__ssm_call_handler (FpiSsm *machine)
{
- fp_dbg("%p entering state %d", machine, machine->cur_state);
- machine->handler(machine, machine->dev, machine->user_data);
+ fp_dbg ("%p entering state %d", machine, machine->cur_state);
+ machine->handler (machine, machine->dev, machine->user_data);
}
/**
* fpi_ssm_start:
- * @ssm: an #fpi_ssm state machine
- * @callback: the #ssm_completed_fn callback to call on completion
+ * @ssm: an #FpiSsm state machine
+ * @callback: the #FpiSsmCompletedCallback callback to call on completion
*
* Starts a state machine. You can also use this function to restart
* a completed or failed state machine. The @callback will be called
* on completion.
*/
-void fpi_ssm_start(fpi_ssm *ssm, ssm_completed_fn callback)
+void
+fpi_ssm_start (FpiSsm *ssm, FpiSsmCompletedCallback callback)
{
- BUG_ON(!ssm->completed);
- ssm->callback = callback;
- ssm->cur_state = 0;
- ssm->completed = FALSE;
- ssm->error = 0;
- __ssm_call_handler(ssm);
+ BUG_ON (!ssm->completed);
+ ssm->callback = callback;
+ ssm->cur_state = 0;
+ ssm->completed = FALSE;
+ ssm->error = NULL;
+ __ssm_call_handler (ssm);
}
-static void __subsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
+static void
+__subsm_complete (FpiSsm *ssm, FpDevice *_dev, void *user_data, GError *error)
{
- fpi_ssm *parent = ssm->parentsm;
- BUG_ON(!parent);
- if (ssm->error)
- fpi_ssm_mark_failed(parent, ssm->error);
- else
- fpi_ssm_next_state(parent);
- fpi_ssm_free(ssm);
+ FpiSsm *parent = ssm->parentsm;
+
+ BUG_ON (!parent);
+ if (error)
+ fpi_ssm_mark_failed (parent, error);
+ else
+ fpi_ssm_next_state (parent);
+ fpi_ssm_free (ssm);
}
/**
* fpi_ssm_start_subsm:
- * @parent: an #fpi_ssm state machine
- * @child: an #fpi_ssm state machine
+ * @parent: an #FpiSsm state machine
+ * @child: an #FpiSsm state machine
*
* Starts a state machine as a child of another. if the child completes
* successfully, the parent will be advanced to the next state. if the
@@ -194,124 +203,185 @@ static void __subsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
*
* The child will be automatically freed upon completion or failure.
*/
-void fpi_ssm_start_subsm(fpi_ssm *parent, fpi_ssm *child)
+void
+fpi_ssm_start_subsm (FpiSsm *parent, FpiSsm *child)
{
- child->parentsm = parent;
- fpi_ssm_start(child, __subsm_complete);
+ child->parentsm = parent;
+ fpi_ssm_start (child, __subsm_complete);
}
/**
* fpi_ssm_mark_completed:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Mark a ssm as completed successfully. The callback set when creating
* the state machine with fpi_ssm_new() will be called synchronously.
*/
-void fpi_ssm_mark_completed(fpi_ssm *machine)
+void
+fpi_ssm_mark_completed (FpiSsm *machine)
{
- BUG_ON(machine->completed);
- machine->completed = TRUE;
- fp_dbg("%p completed with status %d", machine, machine->error);
- if (machine->callback)
- machine->callback(machine, machine->dev, machine->user_data);
+ BUG_ON (machine->completed);
+ machine->completed = TRUE;
+ if (machine->error)
+ fp_dbg ("%p completed with error: %s", machine, machine->error->message);
+ else
+ fp_dbg ("%p completed successfully", machine);
+ if (machine->callback)
+ {
+ GError *error = machine->error ? g_error_copy (machine->error) : NULL;
+
+ machine->callback (machine, machine->dev, machine->user_data, error);
+ }
}
/**
* fpi_ssm_mark_failed:
- * @machine: an #fpi_ssm state machine
- * @error: the error code
+ * @machine: an #FpiSsm state machine
+ * @error: a #GError
*
* Mark a state machine as failed with @error as the error code.
*/
-void fpi_ssm_mark_failed(fpi_ssm *machine, int error)
+void
+fpi_ssm_mark_failed (FpiSsm *machine, GError *error)
{
- fp_dbg("error %d from state %d", error, machine->cur_state);
- BUG_ON(error == 0);
- machine->error = error;
- fpi_ssm_mark_completed(machine);
+ g_assert (error);
+ if (machine->error)
+ {
+ fp_warn ("SSM already has an error set, ignoring new error %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ fp_dbg ("SSM failed in state %d with error: %s", machine->cur_state, error->message);
+ machine->error = error;
+ fpi_ssm_mark_completed (machine);
}
/**
* fpi_ssm_next_state:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Iterate to next state of a state machine. If the current state is the
* last state, then the state machine will be marked as completed, as
* if calling fpi_ssm_mark_completed().
*/
-void fpi_ssm_next_state(fpi_ssm *machine)
-{
- g_return_if_fail (machine != NULL);
-
- BUG_ON(machine->completed);
- machine->cur_state++;
- if (machine->cur_state == machine->nr_states) {
- fpi_ssm_mark_completed(machine);
- } else {
- __ssm_call_handler(machine);
- }
-}
-
-/**
- * fpi_ssm_next_state_timeout_cb:
- * @dev: a struct #fp_dev
- * @data: a pointer to an #fpi_ssm state machine
- *
- * Same as fpi_ssm_next_state(), but to be used as a callback
- * for an fpi_timeout_add() callback, when the state change needs
- * to happen after a timeout.
- *
- * Make sure to pass the #fpi_ssm as the `user_data` argument
- * for that fpi_timeout_add() call.
- */
void
-fpi_ssm_next_state_timeout_cb(struct fp_dev *dev,
- void *data)
+fpi_ssm_next_state (FpiSsm *machine)
{
- g_return_if_fail (dev != NULL);
- g_return_if_fail (data != NULL);
+ g_return_if_fail (machine != NULL);
- fpi_ssm_next_state(data);
+ BUG_ON (machine->completed);
+ machine->cur_state++;
+ if (machine->cur_state == machine->nr_states)
+ fpi_ssm_mark_completed (machine);
+ else
+ __ssm_call_handler (machine);
}
/**
* fpi_ssm_jump_to_state:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
* @state: the state to jump to
*
* Jump to the @state state, bypassing intermediary states.
*/
-void fpi_ssm_jump_to_state(fpi_ssm *machine, int state)
+void
+fpi_ssm_jump_to_state (FpiSsm *machine, int state)
{
- BUG_ON(machine->completed);
- BUG_ON(state >= machine->nr_states);
- machine->cur_state = state;
- __ssm_call_handler(machine);
+ BUG_ON (machine->completed);
+ BUG_ON (state >= machine->nr_states);
+ machine->cur_state = state;
+ __ssm_call_handler (machine);
}
/**
* fpi_ssm_get_cur_state:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Returns the value of the current state. Note that states are
* 0-indexed, so a value of 0 means “the first state”.
*
* Returns: the current state.
*/
-int fpi_ssm_get_cur_state(fpi_ssm *machine)
+int
+fpi_ssm_get_cur_state (FpiSsm *machine)
{
- return machine->cur_state;
+ return machine->cur_state;
}
/**
* fpi_ssm_get_error:
- * @machine: an #fpi_ssm state machine
+ * @machine: an #FpiSsm state machine
*
* Returns the error code set by fpi_ssm_mark_failed().
*
- * Returns: a error code
+ * Returns: (transfer none): a error code
*/
-int fpi_ssm_get_error(fpi_ssm *machine)
+GError *
+fpi_ssm_get_error (FpiSsm *machine)
{
- return machine->error;
+ return machine->error;
+}
+
+/**
+ * fpi_ssm_dup_error:
+ * @machine: an #FpiSsm state machine
+ *
+ * Returns the error code set by fpi_ssm_mark_failed().
+ *
+ * Returns: (transfer full): a error code
+ */
+GError *
+fpi_ssm_dup_error (FpiSsm *machine)
+{
+ if (machine->error)
+ return g_error_copy (machine->error);
+
+ return NULL;
+}
+
+/**
+ * fpi_ssm_next_state_timeout_cb:
+ * @dev: a struct #fp_dev
+ * @data: a pointer to an #FpiSsm state machine
+ *
+ * Same as fpi_ssm_next_state(), but to be used as a callback
+ * for an fpi_timeout_add() callback, when the state change needs
+ * to happen after a timeout.
+ *
+ * Make sure to pass the #FpiSsm as the `user_data` argument
+ * for that fpi_timeout_add() call.
+ */
+void
+fpi_ssm_next_state_timeout_cb (FpDevice *dev,
+ void *data)
+{
+ g_return_if_fail (dev != NULL);
+ g_return_if_fail (data != NULL);
+
+ fpi_ssm_next_state (data);
+}
+
+/**
+ * fpi_ssm_usb_transfer_cb:
+ * @transfer: a #FpiUsbTransfer
+ * @device: a #FpDevice
+ * @user_data: User data (unused)
+ * @error: The #GError or %NULL
+ *
+ * Can be used in as a #FpiUsbTransfer callback handler to automatically
+ * advance or fail a statemachine on transfer completion.
+ *
+ * Make sure to set the #FpiSsm on the transfer.
+ */
+void
+fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer, FpDevice *device,
+ gpointer user_data, GError *error)
+{
+ g_return_if_fail (transfer->ssm);
+
+ if (error)
+ fpi_ssm_mark_failed (transfer->ssm, error);
+ else
+ fpi_ssm_next_state (transfer->ssm);
}
diff --git a/libfprint/fpi-ssm.h b/libfprint/fpi-ssm.h
index a619d42..560452c 100644
--- a/libfprint/fpi-ssm.h
+++ b/libfprint/fpi-ssm.h
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2007-2008 Daniel Drake
* Copyright (C) 2018 Bastien Nocera
+ * Copyright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,70 +18,79 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __FPI_SSM_H__
-#define __FPI_SSM_H__
+#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
+#include "fp-device.h"
+#include "fpi-usb-transfer.h"
/* async drv <--> lib comms */
/**
- * fpi_ssm:
+ * FpiSsm:
*
* Sequential state machine that iterates sequentially over
* a predefined series of states. Can be terminated by either completion or
* failure error conditions.
*/
-typedef struct fpi_ssm fpi_ssm;
+typedef struct _FpiSsm FpiSsm;
/**
- * ssm_completed_fn:
- * @ssm: a #fpi_ssm state machine
+ * FpiSsmCompletedCallback:
+ * @ssm: a #FpiSsm state machine
* @dev: the #fp_dev fingerprint device
* @user_data: the user data passed to fpi_ssm_new()
+ * @error: The #GError or %NULL on successful completion
*
* The callback called when a state machine completes successfully,
* as set when calling fpi_ssm_start().
*/
-typedef void (*ssm_completed_fn)(fpi_ssm *ssm,
- struct fp_dev *dev,
- void *user_data);
+typedef void (*FpiSsmCompletedCallback)(FpiSsm *ssm,
+ FpDevice *dev,
+ void *user_data,
+ GError *error);
/**
- * ssm_handler_fn:
- * @ssm: a #fpi_ssm state machine
+ * FpiSsmHandlerCallback:
+ * @ssm: a #FpiSsm state machine
* @dev: the #fp_dev fingerprint device
* @user_data: the user data passed to fpi_ssm_new()
*
* The callback called when a state machine transitions from one
* state to the next, as set when calling fpi_ssm_new().
*/
-typedef void (*ssm_handler_fn)(fpi_ssm *ssm,
- struct fp_dev *dev,
- void *user_data);
+typedef void (*FpiSsmHandlerCallback)(FpiSsm *ssm,
+ FpDevice *dev,
+ void *user_data);
/* for library and drivers */
-fpi_ssm *fpi_ssm_new(struct fp_dev *dev,
- ssm_handler_fn handler,
- int nr_states,
- void *user_data);
-void fpi_ssm_free(fpi_ssm *machine);
-void fpi_ssm_start(fpi_ssm *ssm, ssm_completed_fn callback);
-void fpi_ssm_start_subsm(fpi_ssm *parent, fpi_ssm *child);
+FpiSsm *fpi_ssm_new (FpDevice *dev,
+ FpiSsmHandlerCallback handler,
+ int nr_states,
+ void *user_data);
+void fpi_ssm_free (FpiSsm *machine);
+void fpi_ssm_start (FpiSsm *ssm,
+ FpiSsmCompletedCallback callback);
+void fpi_ssm_start_subsm (FpiSsm *parent,
+ FpiSsm *child);
/* for drivers */
-void fpi_ssm_next_state(fpi_ssm *machine);
-void fpi_ssm_next_state_timeout_cb(struct fp_dev *dev, void *data);
-void fpi_ssm_jump_to_state(fpi_ssm *machine, int state);
-void fpi_ssm_mark_completed(fpi_ssm *machine);
-void fpi_ssm_mark_failed(fpi_ssm *machine, int error);
-void *fpi_ssm_get_user_data(fpi_ssm *machine);
-int fpi_ssm_get_error(fpi_ssm *machine);
-int fpi_ssm_get_cur_state(fpi_ssm *machine);
+void fpi_ssm_next_state (FpiSsm *machine);
+void fpi_ssm_jump_to_state (FpiSsm *machine,
+ int state);
+void fpi_ssm_mark_completed (FpiSsm *machine);
+void fpi_ssm_mark_failed (FpiSsm *machine,
+ GError *error);
+void *fpi_ssm_get_user_data (FpiSsm *machine);
+GError * fpi_ssm_get_error (FpiSsm *machine);
+GError * fpi_ssm_dup_error (FpiSsm *machine);
+int fpi_ssm_get_cur_state (FpiSsm *machine);
-#endif
+/* Callbacks to be used by the driver instead of implementing their own
+ * logic.
+ */
+void fpi_ssm_next_state_timeout_cb (FpDevice *dev,
+ void *data);
+void fpi_ssm_usb_transfer_cb (FpiUsbTransfer *transfer,
+ FpDevice *device,
+ gpointer user_data,
+ GError *error);
diff --git a/libfprint/fpi-sync.c b/libfprint/fpi-sync.c
deleted file mode 100644
index c2dd792..0000000
--- a/libfprint/fpi-sync.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * Synchronous I/O functionality
- * Copyright (C) 2007-2008 Daniel Drake
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define FP_COMPONENT "sync"
-
-#include "fp_internal.h"
-#include "fpi-dev.h"
-
-#include
-#include
-
-struct sync_open_data {
- struct fp_dev *dev;
- int status;
-};
-
-static void sync_open_cb(struct fp_dev *dev, int status, void *user_data)
-{
- struct sync_open_data *odata = user_data;
- fp_dbg("status %d", status);
- odata->dev = dev;
- odata->status = status;
-}
-
-/**
- * fp_dev_open:
- * @ddev: the struct #fp_dscv_dev discovered device to open
- *
- * Opens and initialises a device. This is the function you call in order
- * to convert a #fp_dscv_dev discovered device into an actual device handle
- * that you can perform operations with.
- *
- * Returns: the opened device handle, or %NULL on error
- */
-API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
-{
- struct fp_dev *dev = NULL;
- struct sync_open_data *odata = g_malloc0(sizeof(*odata));
- int r;
-
- G_DEBUG_HERE();
- r = fp_async_dev_open(ddev, sync_open_cb, odata);
- if (r)
- goto out;
-
- while (!odata->dev)
- if (fp_handle_events() < 0)
- goto out;
-
- if (odata->status == 0)
- dev = odata->dev;
- else
- fp_dev_close(odata->dev);
-
-out:
- g_free(odata);
- return dev;
-}
-
-static void sync_close_cb(struct fp_dev *dev, void *user_data)
-{
- G_DEBUG_HERE();
- gboolean *closed = user_data;
- *closed = TRUE;
-}
-
-/**
- * fp_dev_close:
- * @dev: the struct #fp_dev device to close. If %NULL, function simply returns
- *
- * Closes a device. You must call this function when you have finished using
- * a fingerprint device.
- */
-API_EXPORTED void fp_dev_close(struct fp_dev *dev)
-{
- gboolean closed = FALSE;
-
- if (!dev)
- return;
-
- G_DEBUG_HERE();
- fp_async_dev_close(dev, sync_close_cb, &closed);
- while (!closed)
- if (fp_handle_events() < 0)
- break;
-}
-
-struct sync_enroll_data {
- gboolean populated;
- int result;
- struct fp_print_data *data;
- struct fp_img *img;
-};
-
-static void sync_enroll_cb(struct fp_dev *dev, int result,
- struct fp_print_data *data, struct fp_img *img, void *user_data)
-{
- struct sync_enroll_data *edata = user_data;
- fp_dbg("result %d", result);
- edata->result = result;
- edata->data = data;
- edata->img = img;
- edata->populated = TRUE;
-}
-
-static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
-{
- gboolean *stopped = user_data;
- G_DEBUG_HERE();
- *stopped = TRUE;
-}
-
-/**
- * fp_enroll_finger_img:
- * @dev: the struct #fp_dev device
- * @print_data: a location to return the resultant enrollment data from
- * the final stage. Must be freed with fp_print_data_free() after use
- * @img: location to store the scan image. accepts %NULL for no image
- * storage. If an image is returned, it must be freed with fp_img_free() after
- * use
- *
- * Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
- * for an explanation of enroll stages.
- *
- * If no enrollment is in process, this kicks of the process and runs the
- * first stage. If an enrollment is already in progress, calling this
- * function runs the next stage, which may well be the last.
- *
- * A negative error code may be returned from any stage. When this occurs,
- * further calls to the enroll function will start a new enrollment process,
- * i.e. a negative error code indicates that the enrollment process has been
- * aborted. These error codes only ever indicate unexpected internal errors
- * or I/O problems.
- *
- * The RETRY codes from #fp_enroll_result may be returned from any enroll
- * stage. These codes indicate that the scan was not successful in that the
- * user did not position their finger correctly or similar. When a RETRY code
- * is returned, the enrollment stage is not advanced, so the next call
- * into this function will retry the current stage again. The current stage may
- * need to be retried several times.
- *
- * The %FP_ENROLL_FAIL code may be returned from any enroll
- * stage. This code indicates that even though the scans themselves have been
- * acceptable, data processing applied to these scans produces incomprehensible
- * results. In other words, the user may have been scanning a different finger
- * for each stage or something like that. Like negative error codes, this
- * return code indicates that the enrollment process has been aborted.
- *
- * The %FP_ENROLL_PASS code will only ever be returned for
- * non-final stages. This return code indicates that the scan was acceptable
- * and the next call into this function will advance onto the next enroll
- * stage.
- *
- * The %FP_ENROLL_COMPLETE code will only ever be returned
- * from the final enroll stage. It indicates that enrollment completed
- * successfully, and that print_data has been assigned to point to the
- * resultant enrollment data. The print_data parameter will not be modified
- * during any other enrollment stages, hence it is actually legal to pass NULL
- * as this argument for all but the final stage.
- *
- * If the device is an imaging device, it can also return the image from
- * the scan, even when the enroll fails with a RETRY or FAIL code. It is legal
- * to call this function even on non-imaging devices, just don't expect them to
- * provide images.
- *
- * Returns: negative code on error, otherwise a code from #fp_enroll_result
- */
-API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
- struct fp_print_data **print_data, struct fp_img **img)
-{
- struct fp_driver *drv = dev->drv;
- int stage = dev->__enroll_stage;
- gboolean final = FALSE;
- gboolean stopped = FALSE;
- struct sync_enroll_data *edata = NULL;
- int r;
- G_DEBUG_HERE();
-
- /* FIXME __enroll_stage is ugly, can we replace it by some function that
- * says whether we're enrolling or not, and then put __enroll_stage into
- * edata? */
-
- if (stage == -1) {
- edata = g_malloc0(sizeof(struct sync_enroll_data));
- r = fp_async_enroll_start(dev, sync_enroll_cb, edata);
- if (r < 0) {
- g_free(edata);
- return r;
- }
-
- dev->__enroll_stage = ++stage;
- } else if (stage >= dev->nr_enroll_stages) {
- fp_err("exceeding number of enroll stages for device claimed by "
- "driver %s (%d stages)", drv->name, dev->nr_enroll_stages);
- dev->__enroll_stage = -1;
- r = -EINVAL;
- final = TRUE;
- goto out;
- }
- fp_dbg("%s will handle enroll stage %d/%d", drv->name, stage,
- dev->nr_enroll_stages - 1);
-
- /* FIXME this isn't very clean */
- edata = dev->enroll_stage_cb_data;
-
- while (!edata->populated) {
- r = fp_handle_events();
- if (r < 0) {
- g_free(edata);
- goto err;
- }
- }
-
- edata->populated = FALSE;
-
- if (img)
- *img = edata->img;
- else
- fp_img_free(edata->img);
-
- r = edata->result;
- switch (r) {
- case FP_ENROLL_PASS:
- fp_dbg("enroll stage passed");
- dev->__enroll_stage = stage + 1;
- break;
- case FP_ENROLL_COMPLETE:
- fp_dbg("enroll complete");
- dev->__enroll_stage = -1;
- *print_data = edata->data;
- final = TRUE;
- break;
- case FP_ENROLL_RETRY:
- fp_dbg("enroll should retry");
- break;
- case FP_ENROLL_RETRY_TOO_SHORT:
- fp_dbg("swipe was too short, enroll should retry");
- break;
- case FP_ENROLL_RETRY_CENTER_FINGER:
- fp_dbg("finger was not centered, enroll should retry");
- break;
- case FP_ENROLL_RETRY_REMOVE_FINGER:
- fp_dbg("scan failed, remove finger and retry");
- break;
- case FP_ENROLL_FAIL:
- fp_err("enroll failed");
- dev->__enroll_stage = -1;
- final = TRUE;
- break;
- default:
- fp_err("unrecognised return code %d", r);
- dev->__enroll_stage = -1;
- r = -EINVAL;
- final = TRUE;
- break;
- }
-
- if (!final)
- return r;
-
-out:
- if (final) {
- fp_dbg("ending enrollment");
- g_free(edata);
- }
-
-err:
- if (fp_async_enroll_stop(dev, enroll_stop_cb, &stopped) == 0)
- while (!stopped)
- if (fp_handle_events() < 0)
- break;
- return r;
-}
-
-/**
- * fp_enroll_finger:
- * @dev: the struct #fp_dev device
- * @print_data: a location to return the resultant enrollment data from
- * the final stage. Must be freed with fp_print_data_free() after use
- *
- * Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
- * for an explanation of enroll stages. This function is just a shortcut to
- * calling fp_enroll_finger_img() with a %NULL image parameter. Be sure to read
- * the description of fp_enroll_finger_img() in order to understand its behaviour.
- *
- * Returns: negative code on error, otherwise a code from #fp_enroll_result
- */
-API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
- struct fp_print_data **print_data)
-{
- return fp_enroll_finger_img(dev, print_data, NULL);
-}
-
-struct sync_verify_data {
- gboolean populated;
- int result;
- struct fp_img *img;
-};
-
-static void sync_verify_cb(struct fp_dev *dev, int result, struct fp_img *img,
- void *user_data)
-{
- struct sync_verify_data *vdata = user_data;
- vdata->result = result;
- vdata->img = img;
- vdata->populated = TRUE;
-}
-
-static void verify_stop_cb(struct fp_dev *dev, void *user_data)
-{
- gboolean *stopped = user_data;
- G_DEBUG_HERE();
- *stopped = TRUE;
-}
-
-/**
- * fp_verify_finger_img:
- * @dev: the struct #fp_dev device to perform the scan on
- * @enrolled_print: the print to verify against. Must have been previously
- * enrolled with a device compatible to the device selected to perform the scan
- * @img: location to store the scan image. accepts %NULL for no image
- * storage. If an image is returned, it must be freed with fp_img_free() after
- * use
-
- * Performs a new scan and verifies it against a previously enrolled print.
- * If the device is an imaging device, it can also return the image from
- * the scan, even when the verify fails with a RETRY code. It is legal to
- * call this function even on non-imaging devices, just don't expect them to
- * provide images.
- *
- * Returns: negative code on error, otherwise a code from #fp_verify_result
- */
-API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
- struct fp_print_data *enrolled_print, struct fp_img **img)
-{
- struct sync_verify_data *vdata;
- gboolean stopped = FALSE;
- int r;
-
- if (!enrolled_print) {
- fp_err("no print given");
- return -EINVAL;
- }
-
- if (!fp_dev_supports_print_data(dev, enrolled_print)) {
- fp_err("print is not compatible with device");
- return -EINVAL;
- }
-
- fp_dbg("to be handled by %s", dev->drv->name);
- vdata = g_malloc0(sizeof(struct sync_verify_data));
- r = fp_async_verify_start(dev, enrolled_print, sync_verify_cb, vdata);
- if (r < 0) {
- fp_dbg("verify_start error %d", r);
- g_free(vdata);
- return r;
- }
-
- while (!vdata->populated) {
- r = fp_handle_events();
- if (r < 0) {
- g_free(vdata);
- goto err;
- }
- }
-
- if (img)
- *img = vdata->img;
- else
- fp_img_free(vdata->img);
-
- r = vdata->result;
- g_free(vdata);
- switch (r) {
- case FP_VERIFY_NO_MATCH:
- fp_dbg("result: no match");
- break;
- case FP_VERIFY_MATCH:
- fp_dbg("result: match");
- break;
- case FP_VERIFY_RETRY:
- fp_dbg("verify should retry");
- break;
- case FP_VERIFY_RETRY_TOO_SHORT:
- fp_dbg("swipe was too short, verify should retry");
- break;
- case FP_VERIFY_RETRY_CENTER_FINGER:
- fp_dbg("finger was not centered, verify should retry");
- break;
- case FP_VERIFY_RETRY_REMOVE_FINGER:
- fp_dbg("scan failed, remove finger and retry");
- break;
- default:
- fp_err("unrecognised return code %d", r);
- r = -EINVAL;
- }
-
-err:
- fp_dbg("ending verification");
- if (fp_async_verify_stop(dev, verify_stop_cb, &stopped) == 0)
- while (!stopped)
- if (fp_handle_events() < 0)
- break;
-
- return r;
-}
-
-/**
- * fp_verify_finger:
- * @dev: the struct #fp_dev device to perform the scan on
- * @enrolled_print: the print to verify against. Must have been previously
- * enrolled with a device compatible to the device selected to perform the scan
- *
- * Performs a new scan and verify it against a previously enrolled print. This
- * function is just a shortcut to calling fp_verify_finger_img() with a NULL
- * image output parameter.
- *
- * See also fp_verify_finger_img().
- *
- * Returns: negative code on error, otherwise a code from #fp_verify_result
- */
-API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
- struct fp_print_data *enrolled_print)
-{
- return fp_verify_finger_img(dev, enrolled_print, NULL);
-}
-
-struct sync_identify_data {
- gboolean populated;
- int result;
- size_t match_offset;
- struct fp_img *img;
-};
-
-static void sync_identify_cb(struct fp_dev *dev, int result,
- size_t match_offset, struct fp_img *img, void *user_data)
-{
- struct sync_identify_data *idata = user_data;
- idata->result = result;
- idata->match_offset = match_offset;
- idata->img = img;
- idata->populated = TRUE;
-}
-
-static void identify_stop_cb(struct fp_dev *dev, void *user_data)
-{
- gboolean *stopped = user_data;
- G_DEBUG_HERE();
- *stopped = TRUE;
-}
-
-/**
- * fp_identify_finger_img:
- * @dev: the struct #fp_dev device to perform the scan on
- * @print_gallery: NULL-terminated array of pointers to the prints to
- * identify against. Each one must have been previously enrolled with a device
- * compatible to the device selected to perform the scan
- * @match_offset: output location to store the array index of the matched
- * gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
- * returned
- * @img: location to store the scan image. accepts %NULL for no image
- * storage. If an image is returned, it must be freed with fp_img_free() after
- * use
-
- * Performs a new scan and attempts to identify the scanned finger against
- * a collection of previously enrolled fingerprints.
- * If the device is an imaging device, it can also return the image from
- * the scan, even when identification fails with a RETRY code. It is legal to
- * call this function even on non-imaging devices, just don't expect them to
- * provide images.
- *
- * This function returns codes from #fp_verify_result. The return code
- * %FP_VERIFY_MATCH indicates that the scanned fingerprint
- * does appear in the print gallery, and the match_offset output parameter
- * will indicate the index into the print gallery array of the matched print.
- *
- * This function will not necessarily examine the whole print gallery, it
- * will return as soon as it finds a matching print.
- *
- * Not all devices support identification. -ENOTSUP will be returned when
- * this is the case.
- *
- * Returns: negative code on error, otherwise a code from #fp_verify_result
- */
-API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
- struct fp_print_data **print_gallery, size_t *match_offset,
- struct fp_img **img)
-{
- gboolean stopped = FALSE;
- struct sync_identify_data *idata
- = g_malloc0(sizeof(struct sync_identify_data));
- int r;
-
- fp_dbg("to be handled by %s", dev->drv->name);
-
- r = fp_async_identify_start(dev, print_gallery, sync_identify_cb, idata);
- if (r < 0) {
- fp_err("identify_start error %d", r);
- goto err;
- }
-
- while (!idata->populated) {
- r = fp_handle_events();
- if (r < 0)
- goto err_stop;
- }
-
- if (img)
- *img = idata->img;
- else
- fp_img_free(idata->img);
-
- r = idata->result;
- switch (idata->result) {
- case FP_VERIFY_NO_MATCH:
- fp_dbg("result: no match");
- break;
- case FP_VERIFY_MATCH:
- fp_dbg("result: match at offset %zd", idata->match_offset);
- *match_offset = idata->match_offset;
- break;
- case FP_VERIFY_RETRY:
- fp_dbg("verify should retry");
- break;
- case FP_VERIFY_RETRY_TOO_SHORT:
- fp_dbg("swipe was too short, verify should retry");
- break;
- case FP_VERIFY_RETRY_CENTER_FINGER:
- fp_dbg("finger was not centered, verify should retry");
- break;
- case FP_VERIFY_RETRY_REMOVE_FINGER:
- fp_dbg("scan failed, remove finger and retry");
- break;
- default:
- fp_err("unrecognised return code %d", r);
- r = -EINVAL;
- }
-
-err_stop:
- if (fp_async_identify_stop(dev, identify_stop_cb, &stopped) == 0)
- while (!stopped)
- if (fp_handle_events() < 0)
- break;
-
-err:
- g_free(idata);
- return r;
-}
-
-/**
- * fp_identify_finger:
- * @dev: the struct #fp_dev device to perform the scan on
- * @print_gallery: %NULL-terminated array of pointers to the prints to
- * identify against. Each one must have been previously enrolled with a device
- * compatible to the device selected to perform the scan
- * @match_offset: output location to store the array index of the matched
- * gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
- * returned
-
- * Performs a new scan and attempts to identify the scanned finger against a
- * collection of previously enrolled fingerprints. This function is just a
- * shortcut to calling fp_identify_finger_img() with a %NULL image output
- * parameter.
- *
- * See also fp_identify_finger_img().
- *
- * Returns: negative code on error, otherwise a code from #fp_verify_result
- */
-API_EXPORTED int fp_identify_finger(struct fp_dev *dev,
- struct fp_print_data **print_gallery, size_t *match_offset)
-{
- return fp_identify_finger_img(dev, print_gallery, match_offset, NULL);
-}
-
-struct sync_capture_data {
- gboolean populated;
- int result;
- struct fp_img *img;
-};
-
-static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img,
- void *user_data)
-{
- struct sync_capture_data *vdata = user_data;
- vdata->result = result;
- vdata->img = img;
- vdata->populated = TRUE;
-}
-
-static void capture_stop_cb(struct fp_dev *dev, void *user_data)
-{
- gboolean *stopped = user_data;
- G_DEBUG_HERE();
- *stopped = TRUE;
-}
-/**
- * fp_dev_img_capture:
- * @dev: the struct #fp_dev device
- * @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
- * @img: a location to return the captured image. Must be freed with
- * fp_img_free() after use
- *
- * Captures a #fp_img from a device. The returned image is the raw
- * image provided by the device, you may wish to [standardize](libfprint-Image-operations.html#img_std) it.
- *
- * If set, the @unconditional flag indicates that the device should
- * capture an image unconditionally, regardless of whether a finger is there
- * or not. If unset, this function will block until a finger is detected on
- * the sensor.
- *
- * See fp_dev_supports_imaging().
- *
- * Returns: 0 on success, non-zero on error. -ENOTSUP indicates that either the
- * @unconditional flag was set but the device does not support this, or that the
- * device does not support imaging
- */
-API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
- struct fp_img **img)
-{
- struct sync_capture_data *vdata;
- gboolean stopped = FALSE;
- int r;
-
- if (!dev->drv->capture_start) {
- fp_dbg("image capture is not supported on %s device", dev->drv->name);
- return -ENOTSUP;
- }
-
- fp_dbg("to be handled by %s", dev->drv->name);
- vdata = g_malloc0(sizeof(struct sync_capture_data));
- r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata);
- if (r < 0) {
- fp_dbg("capture_start error %d", r);
- g_free(vdata);
- return r;
- }
-
- while (!vdata->populated) {
- r = fp_handle_events();
- if (r < 0) {
- g_free(vdata);
- goto err;
- }
- }
-
- if (img)
- *img = vdata->img;
- else
- fp_img_free(vdata->img);
-
- r = vdata->result;
- g_free(vdata);
- switch (r) {
- case FP_CAPTURE_COMPLETE:
- fp_dbg("result: complete");
- break;
- case FP_CAPTURE_FAIL:
- fp_dbg("result: fail");
- break;
- default:
- fp_err("unrecognised return code %d", r);
- r = -EINVAL;
- }
-
-err:
- fp_dbg("ending capture");
- if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0)
- while (!stopped)
- if (fp_handle_events() < 0)
- break;
-
- return r;
-}
-
diff --git a/libfprint/fpi-usb-transfer.c b/libfprint/fpi-usb-transfer.c
new file mode 100644
index 0000000..6b29621
--- /dev/null
+++ b/libfprint/fpi-usb-transfer.c
@@ -0,0 +1,522 @@
+/*
+ * FPrint USB transfer handling
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "fpi-usb-transfer.h"
+
+/**
+ * SECTION:fpi-usb-transfer
+ * @title: USB transfer helpers
+ * @short_description: Helpers for libgusb to ease transfer handling
+ *
+ * #FpiUsbTransfer is a structure to simplify the USB transfer handling.
+ * The main goal is to ease memory management and provide more parameters
+ * to callbacks that are useful for libfprint drivers.
+ *
+ * Drivers should use this API only rather than accessing the GUsbDevice
+ * directly in most cases.
+ */
+
+
+G_DEFINE_BOXED_TYPE (FpiUsbTransfer, fpi_usb_transfer, fpi_usb_transfer_ref, fpi_usb_transfer_unref)
+
+static void
+log_transfer (FpiUsbTransfer *transfer, gboolean submit, GError *error)
+{
+ if (g_getenv ("FP_DEBUG_TRANSFER"))
+ {
+ if (!submit)
+ {
+ g_autofree gchar *error_str = NULL;
+ if (error)
+ error_str = g_strdup_printf ("with error (%s)", error->message);
+ else
+ error_str = g_strdup ("successfully");
+
+ g_debug ("Transfer %p completed %s, requested length %zd, actual length %zd, endpoint 0x%x",
+ transfer,
+ error_str,
+ transfer->length,
+ transfer->actual_length,
+ transfer->endpoint);
+ }
+ else
+ {
+ g_debug ("Transfer %p submitted, requested length %zd, endpoint 0x%x",
+ transfer,
+ transfer->length,
+ transfer->endpoint);
+ }
+
+ if (!submit == !!(transfer->endpoint & FPI_USB_ENDPOINT_IN))
+ {
+ g_autoptr(GString) line = NULL;
+ gssize dump_len;
+
+ dump_len = (transfer->endpoint & FPI_USB_ENDPOINT_IN) ? transfer->actual_length : transfer->length;
+
+ line = g_string_new ("");
+ /* Dump the buffer. */
+ for (gint i = 0; i < dump_len; i++)
+ {
+ g_string_append_printf (line, "%02x ", transfer->buffer[i]);
+ if ((i + 1) % 16 == 0)
+ {
+ g_debug ("%s", line->str);
+ g_string_set_size (line, 0);
+ }
+ }
+
+ if (line->len)
+ g_debug ("%s", line->str);
+ }
+ }
+}
+
+/**
+ * fpi_usb_transfer_new:
+ * @device: The #FpDevice the transfer is for
+ *
+ * Creates a new #FpiUsbTransfer.
+ *
+ * Returns: (transfer full): A newly created #FpiUsbTransfer
+ */
+FpiUsbTransfer *
+fpi_usb_transfer_new (FpDevice * device)
+{
+ FpiUsbTransfer *self;
+
+ g_assert (device != NULL);
+
+ self = g_slice_new0 (FpiUsbTransfer);
+ self->ref_count = 1;
+
+ self->device = device;
+
+ return self;
+}
+
+static void
+fpi_usb_transfer_free (FpiUsbTransfer *self)
+{
+ g_assert (self);
+ g_assert_cmpint (self->ref_count, ==, 0);
+
+ if (self->free_buffer && self->buffer)
+ self->free_buffer (self->buffer);
+ self->buffer = NULL;
+
+ g_slice_free (FpiUsbTransfer, self);
+}
+
+/**
+ * fpi_usb_transfer_ref:
+ * @self: A #FpiUsbTransfer
+ *
+ * Increments the reference count of @self by one.
+ *
+ * Returns: (transfer full): @self
+ */
+FpiUsbTransfer *
+fpi_usb_transfer_ref (FpiUsbTransfer *self)
+{
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (self->ref_count, NULL);
+
+ g_atomic_int_inc (&self->ref_count);
+
+ return self;
+}
+
+/**
+ * fpi_usb_transfer_unref:
+ * @self: A #FpiUsbTransfer
+ *
+ * Decrements the reference count of @self by one, freeing the structure when
+ * the reference count reaches zero.
+ */
+void
+fpi_usb_transfer_unref (FpiUsbTransfer *self)
+{
+ g_return_if_fail (self);
+ g_return_if_fail (self->ref_count);
+
+ if (g_atomic_int_dec_and_test (&self->ref_count))
+ fpi_usb_transfer_free (self);
+}
+
+/**
+ * fpi_usb_transfer_fill_bulk:
+ * @transfer: The #FpiUsbTransfer
+ * @endpoint: The endpoint to send the transfer to
+ * @length: The buffer size to allocate
+ *
+ * Prepare a bulk transfer. A buffer will be created for you, use
+ * fpi_usb_transfer_fill_bulk_full() if you want to send a static buffer
+ * or receive a pre-defined buffer.
+ */
+void
+fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ gsize length)
+{
+ fpi_usb_transfer_fill_bulk_full (transfer,
+ endpoint,
+ g_malloc0 (length),
+ length,
+ g_free);
+}
+
+/**
+ * fpi_usb_transfer_fill_bulk_full:
+ * @transfer: The #FpiUsbTransfer
+ * @endpoint: The endpoint to send the transfer to
+ * @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
+ * @length: The size of @buffer
+ * @free_func: (destroy buffer): Destroy notify for @buffer
+ *
+ * Prepare a bulk transfer.
+ */
+void
+fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ guint8 *buffer,
+ gsize length,
+ GDestroyNotify free_func)
+{
+ g_assert (transfer->type == FP_TRANSFER_NONE);
+ g_assert (buffer != NULL);
+
+ transfer->type = FP_TRANSFER_BULK;
+ transfer->endpoint = endpoint;
+
+ transfer->buffer = buffer;
+ transfer->length = length;
+ transfer->free_buffer = free_func;
+}
+
+/**
+ * fpi_usb_transfer_fill_control:
+ * @transfer: The #FpiUsbTransfer
+ * @direction: The direction of the control transfer
+ * @request_type: The request type
+ * @recipient: The recipient
+ * @request: The control transfer request
+ * @value: The control transfer value
+ * @idx: The control transfer index
+ * @length: The size of the transfer
+ *
+ * Prepare a control transfer. The function will create a new buffer,
+ * you can initialize the buffer after calling this function.
+ */
+void
+fpi_usb_transfer_fill_control (FpiUsbTransfer *transfer,
+ GUsbDeviceDirection direction,
+ GUsbDeviceRequestType request_type,
+ GUsbDeviceRecipient recipient,
+ guint8 request,
+ guint16 value,
+ guint16 idx,
+ gsize length)
+{
+ g_assert (transfer->type == FP_TRANSFER_NONE);
+
+ transfer->type = FP_TRANSFER_CONTROL;
+ transfer->direction = direction;
+ transfer->request_type = request_type;
+ transfer->recipient = recipient;
+ transfer->request = request;
+ transfer->value = value;
+ transfer->idx = idx;
+
+ transfer->length = length;
+ transfer->buffer = g_malloc0 (length);
+ transfer->free_buffer = g_free;
+}
+
+/**
+ * fpi_usb_transfer_fill_interrupt:
+ * @transfer: The #FpiUsbTransfer
+ * @endpoint: The endpoint to send the transfer to
+ * @length: The size of the transfer
+ *
+ * Prepare an interrupt transfer. The function will create a new buffer,
+ * you can initialize the buffer after calling this function.
+ */
+void
+fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ gsize length)
+{
+ fpi_usb_transfer_fill_interrupt_full (transfer,
+ endpoint,
+ g_malloc0 (length),
+ length,
+ g_free);
+}
+
+/**
+ * fpi_usb_transfer_fill_interrupt_full:
+ * @transfer: The #FpiUsbTransfer
+ * @endpoint: The endpoint to send the transfer to
+ * @buffer: The data to send. A buffer will be created and managed for you if you pass NULL.
+ * @length: The size of @buffer
+ * @free_func: (destroy buffer): Destroy notify for @buffer
+ *
+ * Prepare an interrupt transfer.
+ */
+void
+fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ guint8 *buffer,
+ gsize length,
+ GDestroyNotify free_func)
+{
+ g_assert (transfer->type == FP_TRANSFER_NONE);
+ g_assert (buffer != NULL);
+
+ transfer->type = FP_TRANSFER_INTERRUPT;
+ transfer->endpoint = endpoint;
+
+ transfer->buffer = buffer;
+ transfer->length = length;
+ transfer->free_buffer = free_func;
+}
+
+void
+transfer_finish_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ FpiUsbTransfer *transfer = user_data;
+ FpiUsbTransferCallback callback;
+
+ switch (transfer->type)
+ {
+ case FP_TRANSFER_BULK:
+ transfer->actual_length =
+ g_usb_device_bulk_transfer_finish (G_USB_DEVICE (source_object),
+ res,
+ &error);
+ break;
+
+ case FP_TRANSFER_CONTROL:
+ transfer->actual_length =
+ g_usb_device_control_transfer_finish (G_USB_DEVICE (source_object),
+ res,
+ &error);
+ break;
+
+ case FP_TRANSFER_INTERRUPT:
+ transfer->actual_length =
+ g_usb_device_interrupt_transfer_finish (G_USB_DEVICE (source_object),
+ res,
+ &error);
+ break;
+
+ case FP_TRANSFER_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+
+ log_transfer (transfer, FALSE, error);
+
+ /* Check for short error, and set an error if requested */
+ if (error == NULL &&
+ transfer->short_is_error &&
+ transfer->actual_length > 0 &&
+ transfer->actual_length != transfer->length)
+ {
+ error = g_error_new (G_USB_DEVICE_ERROR,
+ G_USB_DEVICE_ERROR_IO,
+ "Unexpected short error of %zd size (expected %zd)", transfer->actual_length, transfer->length);
+ }
+
+ callback = transfer->callback;
+ transfer->callback = NULL;
+ callback (transfer, transfer->device, transfer->user_data, error);
+
+ fpi_usb_transfer_unref (transfer);
+}
+
+
+/**
+ * fpi_usb_transfer_submit:
+ * @transfer: The transfer to submit, must have been filled.
+ * @timeout_ms: Timeout for the transfer in ms
+ * @cancellable: Cancellable to use, e.g. fpi_device_get_cancellable()
+ * @callback: Callback on completion or error
+ * @user_data: Data to pass to callback
+ *
+ * Submit a USB transfer with a specific timeout and callback functions.
+ *
+ * Note that #FpiUsbTransfer is owned by the user. In most cases, you
+ * should call fpi_usb_transfer_unref() just after calling this function.
+ * Doing so means that all associated data will be free'ed automatically
+ * after the callback ran.
+ */
+void
+fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
+ guint timeout_ms,
+ GCancellable *cancellable,
+ FpiUsbTransferCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (transfer);
+ g_return_if_fail (callback);
+
+ /* Recycling is allowed, but not two at the same time. */
+ g_return_if_fail (transfer->callback == NULL);
+
+ transfer->callback = callback;
+ transfer->user_data = user_data;
+
+ /* Grab a reference, this means that one can simply unref after submit and
+ * trust for the data to disappear without explicit management by the callback
+ * function. */
+ fpi_usb_transfer_ref (transfer);
+
+ log_transfer (transfer, TRUE, NULL);
+
+ switch (transfer->type)
+ {
+ case FP_TRANSFER_BULK:
+ g_usb_device_bulk_transfer_async (fpi_device_get_usb_device (transfer->device),
+ transfer->endpoint,
+ transfer->buffer,
+ transfer->length,
+ timeout_ms,
+ cancellable,
+ transfer_finish_cb,
+ transfer);
+ break;
+
+ case FP_TRANSFER_CONTROL:
+ g_usb_device_control_transfer_async (fpi_device_get_usb_device (transfer->device),
+ transfer->direction,
+ transfer->request_type,
+ transfer->recipient,
+ transfer->request,
+ transfer->value,
+ transfer->idx,
+ transfer->buffer,
+ transfer->length,
+ timeout_ms,
+ cancellable,
+ transfer_finish_cb,
+ transfer);
+ break;
+
+ case FP_TRANSFER_INTERRUPT:
+ g_usb_device_interrupt_transfer_async (fpi_device_get_usb_device (transfer->device),
+ transfer->endpoint,
+ transfer->buffer,
+ transfer->length,
+ timeout_ms,
+ cancellable,
+ transfer_finish_cb,
+ transfer);
+ break;
+
+ case FP_TRANSFER_NONE:
+ default:
+ fpi_usb_transfer_unref (transfer);
+ g_return_if_reached ();
+ }
+}
+
+/**
+ * fpi_usb_transfer_submit_sync:
+ * @transfer: The transfer to submit, must have been filled.
+ * @timeout_ms: Timeout for the transfer in millisecnods
+ * @error: Location to store #GError to
+ *
+ * Synchronously submit a USB transfer with a specific timeout.
+ * Only use this function with short timeouts as the application will
+ * be blocked otherwise.
+ *
+ * Note that you still need to fpi_usb_transfer_unref() the
+ * #FpiUsbTransfer afterwards.
+ *
+ * Returns: #TRUE on success, otherwise #FALSE and @error will be set
+ */
+gboolean
+fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
+ guint timeout_ms,
+ GError **error)
+{
+ gboolean res;
+
+ g_return_val_if_fail (transfer, FALSE);
+
+ /* Recycling is allowed, but not two at the same time. */
+ g_return_val_if_fail (transfer->callback == NULL, FALSE);
+
+ log_transfer (transfer, TRUE, NULL);
+
+ switch (transfer->type)
+ {
+ case FP_TRANSFER_BULK:
+ res = g_usb_device_bulk_transfer (fpi_device_get_usb_device (transfer->device),
+ transfer->endpoint,
+ transfer->buffer,
+ transfer->length,
+ &transfer->actual_length,
+ timeout_ms,
+ NULL,
+ error);
+ break;
+
+ case FP_TRANSFER_CONTROL:
+ res = g_usb_device_control_transfer (fpi_device_get_usb_device (transfer->device),
+ transfer->direction,
+ transfer->request_type,
+ transfer->recipient,
+ transfer->request,
+ transfer->value,
+ transfer->idx,
+ transfer->buffer,
+ transfer->length,
+ &transfer->actual_length,
+ timeout_ms,
+ NULL,
+ error);
+ break;
+
+ case FP_TRANSFER_INTERRUPT:
+ res = g_usb_device_interrupt_transfer (fpi_device_get_usb_device (transfer->device),
+ transfer->endpoint,
+ transfer->buffer,
+ transfer->length,
+ &transfer->actual_length,
+ timeout_ms,
+ NULL,
+ error);
+ break;
+
+ case FP_TRANSFER_NONE:
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ log_transfer (transfer, FALSE, *error);
+
+ if (!res)
+ transfer->actual_length = -1;
+
+ return res;
+}
diff --git a/libfprint/fpi-usb-transfer.h b/libfprint/fpi-usb-transfer.h
new file mode 100644
index 0000000..5b8fe9c
--- /dev/null
+++ b/libfprint/fpi-usb-transfer.h
@@ -0,0 +1,157 @@
+/*
+ * FPrint USB transfer handling
+ * Copyright (C) 2019 Benjamin Berg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include
+#include "fpi-device.h"
+
+G_BEGIN_DECLS
+
+#define FPI_TYPE_USB_TRANSFER (fpi_usb_transfer_get_type ())
+
+#define FPI_USB_ENDPOINT_IN 0x80
+#define FPI_USB_ENDPOINT_OUT 0x00
+
+typedef struct _FpiUsbTransfer FpiUsbTransfer;
+
+#include "fpi-ssm.h"
+
+typedef void (*FpiUsbTransferCallback)(FpiUsbTransfer *transfer,
+ FpDevice *dev,
+ gpointer user_data,
+ GError *error);
+
+/**
+ * FpiTransferType:
+ * @FP_TRANSFER_NONE: Type not set
+ * @FP_TRANSFER_BULK: Bulk transfer
+ * @FP_TRANSFER_CONTROL: Control transfer
+ * @FP_TRANSFER_INTERRUPT: Interrupt transfer
+ *
+ * Type of the transfer.
+ */
+typedef enum {
+ FP_TRANSFER_NONE = 0,
+ FP_TRANSFER_BULK,
+ FP_TRANSFER_CONTROL,
+ FP_TRANSFER_INTERRUPT,
+} FpiTransferType;
+
+/**
+ * FpiUsbTransfer:
+ * @device: The #FpDevice that the transfer belongs to.
+ * @ssm: Storage slot to associate the transfer with a state machine.
+ * Used by fpi_ssm_usb_transfer_cb() to modify the given state machine.
+ * @length: The requested length of the transfer in bytes.
+ * @actual_length: The actual length of the transfer
+ * (see also fpi_usb_transfer_set_short_error())
+ * @buffer: The transfered data.
+ *
+ * Helper for handling USB transfers.
+ */
+struct _FpiUsbTransfer
+{
+ /*< public >*/
+ FpDevice *device;
+
+ FpiSsm *ssm;
+
+ gssize length;
+ gssize actual_length;
+
+ guchar *buffer;
+
+ /*< private >*/
+ guint ref_count;
+
+ /* USB Transfer information */
+ FpiTransferType type;
+ guint8 endpoint;
+
+ /* Control Transfer options */
+ GUsbDeviceDirection direction;
+ GUsbDeviceRequestType request_type;
+ GUsbDeviceRecipient recipient;
+ guint8 request;
+ guint16 value;
+ guint16 idx;
+
+ /* Flags */
+ gboolean short_is_error;
+
+ /* Callbacks */
+ gpointer user_data;
+ FpiUsbTransferCallback callback;
+
+ /* Data free function */
+ GDestroyNotify free_buffer;
+};
+
+GType fpi_usb_transfer_get_type (void) G_GNUC_CONST;
+FpiUsbTransfer *fpi_usb_transfer_new (FpDevice *device);
+FpiUsbTransfer *fpi_usb_transfer_ref (FpiUsbTransfer *self);
+void fpi_usb_transfer_unref (FpiUsbTransfer *self);
+
+void fpi_usb_transfer_set_short_error (FpiUsbTransfer *transfer,
+ gboolean short_is_error);
+
+void fpi_usb_transfer_fill_bulk (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ gsize length);
+
+void fpi_usb_transfer_fill_bulk_full (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ guint8 *buffer,
+ gsize length,
+ GDestroyNotify free_func);
+
+void fpi_usb_transfer_fill_control (FpiUsbTransfer *transfer,
+ GUsbDeviceDirection direction,
+ GUsbDeviceRequestType request_type,
+ GUsbDeviceRecipient recipient,
+ guint8 request,
+ guint16 value,
+ guint16 idx,
+ gsize length);
+
+void fpi_usb_transfer_fill_interrupt (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ gsize length);
+
+void fpi_usb_transfer_fill_interrupt_full (FpiUsbTransfer *transfer,
+ guint8 endpoint,
+ guint8 *buffer,
+ gsize length,
+ GDestroyNotify free_func);
+
+void fpi_usb_transfer_submit (FpiUsbTransfer *transfer,
+ guint timeout_ms,
+ GCancellable *cancellable,
+ FpiUsbTransferCallback callback,
+ gpointer user_data);
+
+gboolean fpi_usb_transfer_submit_sync (FpiUsbTransfer *transfer,
+ guint timeout_ms,
+ GError **error);
+
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiUsbTransfer, fpi_usb_transfer_unref)
+
+G_END_DECLS
diff --git a/libfprint/fpi-usb.c b/libfprint/fpi-usb.c
deleted file mode 100644
index b140d7e..0000000
--- a/libfprint/fpi-usb.c
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Driver API definitions
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "fpi-usb.h"
-#include "drivers_api.h"
-
-/**
- * SECTION:fpi-usb
- * @title: Helpers for libusb
- * @short_description: libusb-related helpers
- *
- * A collection of [libusb helpers](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
- * to make driver development easier. Please refer to the libusb API documentation for more
- * information about the original API.
- */
-
-/* Helpers from glib */
-#include
-#include
-#include
-#include
-#include
-
-/* special helpers to avoid gmessage.c dependency */
-static void mem_error (const char *format, ...) G_GNUC_PRINTF (1,2);
-#define mem_assert(cond) do { if (G_LIKELY (cond)) ; else mem_error ("assertion failed: %s", #cond); } while (0)
-
-static void
-mem_error (const char *format,
- ...)
-{
- const char *pname;
- va_list args;
- /* at least, put out "MEMORY-ERROR", in case we segfault during the rest of the function */
- fputs ("\n***MEMORY-ERROR***: ", stderr);
- pname = g_get_prgname();
- g_fprintf (stderr, "%s[%ld]: ", pname ? pname : "", (long)getpid());
- va_start (args, format);
- g_vfprintf (stderr, format, args);
- va_end (args);
- fputs ("\n", stderr);
- abort();
- _exit (1);
-}
-
-struct fpi_usb_transfer {
- struct libusb_transfer *transfer;
- fpi_ssm *ssm;
- struct fp_dev *dev;
- fpi_usb_transfer_cb_fn callback;
- void *user_data;
-};
-
-/**
- * fpi_usb_alloc:
- *
- * Returns a struct libusb_transfer, similar to calling
- * `libusb_alloc_transfer(0)`[[1](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga13cc69ea40c702181c430c950121c000)]. As libfprint uses GLib internally,
- * and [memory allocation failures will make applications fail](https://developer.gnome.org/glib/stable/glib-Memory-Allocation.html#glib-Memory-Allocation.description),
- * this helper will assert when the libusb call fails.
- */
-struct libusb_transfer *
-fpi_usb_alloc(void)
-{
- struct libusb_transfer *transfer;
-
- transfer = libusb_alloc_transfer(0);
- mem_assert(transfer);
-
- return transfer;
-}
-
-static fpi_usb_transfer *
-fpi_usb_transfer_new(struct fp_dev *dev,
- fpi_ssm *ssm,
- fpi_usb_transfer_cb_fn callback,
- void *user_data)
-{
- fpi_usb_transfer *transfer;
-
- transfer = g_new0(fpi_usb_transfer, 1);
- transfer->transfer = fpi_usb_alloc();
- transfer->dev = dev;
- transfer->ssm = ssm;
- transfer->callback = callback;
- transfer->user_data = user_data;
-
- return transfer;
-}
-
-void
-fpi_usb_transfer_free(fpi_usb_transfer *transfer)
-{
- if (transfer == NULL)
- return;
-
- g_free(transfer->transfer->buffer);
- libusb_free_transfer(transfer->transfer);
- g_free(transfer);
-}
-
-static void
-fpi_usb_transfer_cb (struct libusb_transfer *transfer)
-{
- fpi_usb_transfer *t;
-
- g_assert(transfer);
- g_assert(transfer->user_data);
-
- t = transfer->user_data;
- BUG_ON(transfer->callback == NULL);
- (t->callback) (transfer, t->dev, t->ssm, t->user_data);
- fpi_usb_transfer_free(t);
-}
-
-/**
- * fpi_usb_fill_bulk_transfer:
- * @dev: a struct #fp_dev fingerprint device
- * @ssm: the current #fpi_ssm state machine
- * @endpoint: the USB end point
- * @buffer: a buffer allocated with g_malloc() or another GLib function.
- * Note that the returned #fpi_usb_transfer will own this buffer, so it
- * should not be freed manually.
- * @length: the size of @buffer
- * @callback: the callback function that will be called once the fpi_usb_submit_transfer()
- * call finishes.
- * @user_data: a user data pointer to pass to the callback
- * @timeout: timeout for the transfer in milliseconds, or 0 for no timeout
- *
- * This function is similar to calling [`libusb_alloc_transfer(0)`](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga13cc69ea40c702181c430c950121c000)]
- * followed by calling [`libusb_fill_bulk_transfer()`](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gad4ddb1a5c6c7fefc979a44d7300b95d7).
- * The #fpi_usb_transfer_cb_fn callback will however provide more arguments
- * relevant to libfprint drivers, making it a good replacement for the raw libusb
- * calls.
- *
- * Returns: a #fpi_usb_transfer transfer struct, to be passed to
- * fpi_usb_submit_transfer().
- */
-fpi_usb_transfer *
-fpi_usb_fill_bulk_transfer (struct fp_dev *dev,
- fpi_ssm *ssm,
- unsigned char endpoint,
- unsigned char *buffer,
- int length,
- fpi_usb_transfer_cb_fn callback,
- void *user_data,
- unsigned int timeout)
-{
- fpi_usb_transfer *transfer;
-
- g_return_val_if_fail (dev != NULL, NULL);
- g_return_val_if_fail (callback != NULL, NULL);
-
- transfer = fpi_usb_transfer_new(dev,
- ssm,
- callback,
- user_data);
-
- libusb_fill_bulk_transfer(transfer->transfer,
- fpi_dev_get_usb_dev(dev),
- endpoint,
- buffer,
- length,
- fpi_usb_transfer_cb,
- transfer,
- timeout);
-
- return transfer;
-}
-
-/**
- * fpi_usb_submit_transfer:
- * @transfer: a #fpi_usb_transfer struct
- *
- * Start a transfer to the device with the provided #fpi_usb_transfer.
- * On error, the #fpi_usb_transfer struct will be freed, otherwise it will
- * be freed once the callback provided to fpi_usb_fill_bulk_transfer() has
- * been called.
- *
- * Returns: 0 on success, or the same errors as [libusb_submit_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gabb0932601f2c7dad2fee4b27962848ce)
- * on failure.
- */
-int
-fpi_usb_submit_transfer(fpi_usb_transfer *transfer)
-{
- int r;
-
- g_return_val_if_fail (transfer != NULL, LIBUSB_ERROR_INVALID_PARAM);
-
- r = libusb_submit_transfer(transfer->transfer);
- if (r < 0)
- fpi_usb_transfer_free(transfer);
-
- return r;
-}
-
-/**
- * fpi_usb_cancel_transfer:
- * @transfer: a #fpi_usb_transfer struct
- *
- * Cancel a transfer to the device with the provided #fpi_usb_transfer.
- * Note that this will not complete the cancellation, as your transfer
- * callback will be called with the `LIBUSB_TRANSFER_CANCELLED` status,
- * as [libusb_cancel_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga685eb7731f9a0593f75beb99727bbe54)
- * would.
- *
- * You should not access anything but the given struct #libusb_transfer
- * in the callback before checking whether `LIBUSB_TRANSFER_CANCELLED` has
- * been called, as that might cause memory access violations.
- *
- * Returns: 0 on success, or the same errors as [libusb_cancel_transfer](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#ga685eb7731f9a0593f75beb99727bbe54)
- * on failure.
- */
-int
-fpi_usb_cancel_transfer(fpi_usb_transfer *transfer)
-{
- g_return_val_if_fail (transfer != NULL, LIBUSB_ERROR_NOT_FOUND);
-
- return libusb_cancel_transfer(transfer->transfer);
-}
diff --git a/libfprint/fpi-usb.h b/libfprint/fpi-usb.h
deleted file mode 100644
index 822b612..0000000
--- a/libfprint/fpi-usb.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 Bastien Nocera
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef __FPI_USB_H__
-#define __FPI_USB_H__
-
-#include
-#include "fpi-dev.h"
-#include "fpi-ssm.h"
-
-/**
- * fpi_usb_transfer:
- *
- * A structure containing the arguments passed to fpi_usb_fill_bulk_transfer()
- * to be used with fpi_usb_submit_transfer().
- */
-typedef struct fpi_usb_transfer fpi_usb_transfer;
-
-/**
- * fpi_usb_transfer_cb_fn:
- * @transfer: a struct #libusb_transfer
- * @dev: the struct #fp_dev on which the operation was performed
- * @ssm: the #fpi_ssm state machine
- * @user_data: the user data passed to fpi_usb_fill_bulk_transfer()
- *
- * This callback will be called in response to a libusb bulk transfer
- * triggered via fpi_usb_fill_bulk_transfer() finishing. Note that the
- * struct #libusb_transfer does not need to be freed, as it will be
- * freed after the callback returns, similarly to
- * the [LIBUSB_TRANSFER_FREE_TRANSFER flag](http://libusb.sourceforge.net/api-1.0/group__asyncio.html#gga1fb47dd0f7c209b60a3609ff0c03d56dacf3f064997b283a14097c9f4d6f8ccc1).
- *
- * Note that the cancelled status of the transfer should be checked
- * first thing, as the @dev, @ssm and @user_data pointers might not
- * be pointing to valid values anymore. See fpi_usb_cancel_transfer()
- * for more information.
- */
-typedef void(*fpi_usb_transfer_cb_fn) (struct libusb_transfer *transfer,
- struct fp_dev *dev,
- fpi_ssm *ssm,
- void *user_data);
-
-struct libusb_transfer *fpi_usb_alloc(void) __attribute__((returns_nonnull));
-
-fpi_usb_transfer *fpi_usb_fill_bulk_transfer (struct fp_dev *dev,
- fpi_ssm *ssm,
- unsigned char endpoint,
- unsigned char *buffer,
- int length,
- fpi_usb_transfer_cb_fn callback,
- void *user_data,
- unsigned int timeout);
-int fpi_usb_submit_transfer (fpi_usb_transfer *transfer);
-int fpi_usb_cancel_transfer (fpi_usb_transfer *transfer);
-
-#endif
diff --git a/libfprint/fprint-list-supported-devices.c b/libfprint/fprint-list-supported-devices.c
index ed49ee6..23096ea 100644
--- a/libfprint/fprint-list-supported-devices.c
+++ b/libfprint/fprint-list-supported-devices.c
@@ -2,6 +2,7 @@
* Copyright (C) 2009 Red Hat
* Copyright (C) 2008 Bastien Nocera
* Copyright (C) 2008 Timo Hoenig ,
+ * Coypright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,28 +23,47 @@
#include
#include
-#include "fp_internal.h"
+#include "fpi-context.h"
+#include "fpi-device.h"
GHashTable *printed = NULL;
-static GList *insert_driver (GList *list,
- struct fp_driver *driver)
+static GList *insert_drivers (GList *list)
{
- int i;
+ g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof(GType));
+ gint i;
- for (i = 0; driver->id_table[i].vendor != 0; i++) {
- char *key;
+ fpi_get_driver_types (drivers);
- key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
+ /* Find the best driver to handle this USB device. */
+ for (i = 0; i < drivers->len; i++)
+ {
+ GType driver = g_array_index (drivers, GType, i);
+ FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
+ const FpIdEntry *entry;
- if (g_hash_table_lookup (printed, key) != NULL) {
+ if (cls->type != FP_DEVICE_TYPE_USB) {
+ g_type_class_unref (cls);
+ continue;
+ }
+
+ for (entry = cls->id_table; entry->vid; entry++)
+ {
+ char *key;
+
+ key = g_strdup_printf ("%04x:%04x", entry->vid, entry->pid);
+
+ if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key);
continue;
- }
+ }
- g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
+ g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
- list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, driver->full_name));
+ list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, cls->full_name));
+ }
+
+ g_type_class_unref (cls);
}
return list;
@@ -51,14 +71,10 @@ static GList *insert_driver (GList *list,
int main (int argc, char **argv)
{
- struct fp_driver **driver_list;
- guint i;
GList *list, *l;
setlocale (LC_ALL, "");
- driver_list = fprint_get_drivers ();
-
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_print ("%% lifprint — Supported Devices\n");
@@ -76,8 +92,7 @@ int main (int argc, char **argv)
g_print ("------------ | ------------\n");
list = NULL;
- for (i = 0; driver_list[i] != NULL; i++)
- list = insert_driver (list, driver_list[i]);
+ list = insert_drivers (list);
list = g_list_sort (list, (GCompareFunc) g_strcmp0);
for (l = list; l != NULL; l = l->next)
diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c
index de291f1..76f8486 100644
--- a/libfprint/fprint-list-udev-rules.c
+++ b/libfprint/fprint-list-udev-rules.c
@@ -2,6 +2,7 @@
* Copyright (C) 2009 Red Hat
* Copyright (C) 2008 Bastien Nocera
* Copyright (C) 2008 Timo Hoenig ,
+ * Coypright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,54 +20,55 @@
*/
#include
-#include
-#include "fp_internal.h"
+#include "fpi-context.h"
+#include "fpi-device.h"
-static const struct usb_id whitelist_id_table[] = {
+static const FpIdEntry whitelist_id_table[] = {
/* Unsupported (for now) Validity Sensors finger print readers */
- { .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */
- { .vendor = 0x138a, .product = 0x0091 },
- { .vendor = 0x138a, .product = 0x0094 },
- { .vendor = 0x138a, .product = 0x0097 }, /* Found on e.g. Lenovo T470s */
- { 0, 0, 0, },
+ { .vid = 0x138a, .pid = 0x0090 }, /* Found on e.g. Lenovo T460s */
+ { .vid = 0x138a, .pid = 0x0091 },
+ { .vid = 0x138a, .pid = 0x0094 },
+ { .vid = 0x138a, .pid = 0x0097 }, /* Found on e.g. Lenovo T470s */
+ { .vid = 0 },
};
-static const struct usb_id blacklist_id_table[] = {
- { .vendor = 0x0483, .product = 0x2016 },
+static const FpIdEntry blacklist_id_table[] = {
+ { .vid = 0x0483, .pid = 0x2016 },
/* https://bugs.freedesktop.org/show_bug.cgi?id=66659 */
- { .vendor = 0x045e, .product = 0x00bb },
- { 0, 0, 0 },
+ { .vid = 0x045e, .pid = 0x00bb },
+ { .vid = 0 },
};
-struct fp_driver whitelist = {
+static const FpDeviceClass whitelist = {
+ .type = FP_DEVICE_TYPE_USB,
.id_table = whitelist_id_table,
.full_name = "Hardcoded whitelist"
};
GHashTable *printed = NULL;
-static void print_driver (struct fp_driver *driver)
+static void print_driver (const FpDeviceClass *cls)
{
- int i, j, blacklist, num_printed;
+ const FpIdEntry *entry;
+ gint num_printed = 0;
- num_printed = 0;
+ if (cls->type != FP_DEVICE_TYPE_USB)
+ return;
- for (i = 0; driver->id_table[i].vendor != 0; i++) {
+ for (entry = cls->id_table; entry->vid != 0; entry++) {
+ const FpIdEntry *bl_entry;
char *key;
- blacklist = 0;
- for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
- if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
- driver->id_table[i].product == blacklist_id_table[j].product) {
- blacklist = 1;
+ for (bl_entry = blacklist_id_table; bl_entry->vid != 0; bl_entry++) {
+ if (entry->vid == bl_entry->vid && entry->pid == bl_entry->pid) {
break;
}
}
- if (blacklist)
+ if (bl_entry->vid != 0)
continue;
- key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
+ key = g_strdup_printf ("%04x:%04x", entry->vid, entry->pid);
if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key);
@@ -76,28 +78,42 @@ static void print_driver (struct fp_driver *driver)
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
if (num_printed == 0)
- printf ("# %s\n", driver->full_name);
+ g_print ("# %s\n", cls->full_name);
- printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
- printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name);
+ g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n",
+ entry->vid, entry->pid);
+ g_print ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n",
+ entry->vid, entry->pid, cls->full_name);
num_printed++;
}
if (num_printed > 0)
- printf ("\n");
+ g_print ("\n");
}
int main (int argc, char **argv)
{
- struct fp_driver **list;
+ g_autoptr(GArray) drivers = g_array_new (FALSE, FALSE, sizeof(GType));
guint i;
- list = fprint_get_drivers ();
+ g_print ("%p\n", drivers);
+ g_print ("%p\n", fpi_get_driver_types);
+ fpi_get_driver_types (drivers);
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- for (i = 0; list[i] != NULL; i++) {
- print_driver (list[i]);
+ for (i = 0; i < drivers->len; i++) {
+ GType driver = g_array_index (drivers, GType, i);
+ FpDeviceClass *cls = FP_DEVICE_CLASS (g_type_class_ref (driver));
+
+ if (cls->type != FP_DEVICE_TYPE_USB) {
+ g_type_class_unref (cls);
+ continue;
+ }
+
+ print_driver (cls);
+
+ g_type_class_unref (cls);
}
print_driver (&whitelist);
diff --git a/libfprint/fprint.h b/libfprint/fprint.h
index c7e81b4..a4729ea 100644
--- a/libfprint/fprint.h
+++ b/libfprint/fprint.h
@@ -1,6 +1,6 @@
/*
* Main definitions for libfprint
- * Copyright (C) 2007 Daniel Drake
+ * Copyright (C) 2019 Benjamin Berg
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,411 +17,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef __FPRINT_H__
-#define __FPRINT_H__
+#pragma once
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-#include
-#include
-#include
-
-/**
- * LIBFPRINT_DEPRECATED:
- *
- * Expands to the GNU C deprecated attribute if the compiler is `gcc`. When
- * called with the `-Wdeprecated-declarations` option, `gcc` will generate warnings
- * when deprecated interfaces are used.
- */
-#define LIBFPRINT_DEPRECATED __attribute__((__deprecated__))
-
-/**
- * fp_dscv_dev:
- *
- * #fp_dscv_dev is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_dscv_dev;
-
-/**
- * fp_dev:
- *
- * #fp_dev is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_dev;
-
-/**
- * fp_driver:
- *
- * #fp_driver is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_driver;
-
-/**
- * fp_print_data:
- *
- * #fp_print_data is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_print_data;
-
-/**
- * fp_img:
- *
- * #fp_img is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_img;
-
-/* misc/general stuff */
-
-/**
- * fp_finger:
- * @LEFT_THUMB: Left thumb
- * @LEFT_INDEX: Left index finger
- * @LEFT_MIDDLE: Left middle finger
- * @LEFT_RING: Left ring finger
- * @LEFT_LITTLE: Left little finger
- * @RIGHT_THUMB: Right thumb
- * @RIGHT_INDEX: Right index finger
- * @RIGHT_MIDDLE: Right middle finger
- * @RIGHT_RING: Right ring finger
- * @RIGHT_LITTLE: Right little finger
- *
- * Numeric codes used to refer to fingers (and thumbs) of a human. These are
- * purposely not available as strings, to avoid getting the library tangled up
- * in localization efforts.
- */
-enum fp_finger {
- LEFT_THUMB = 1,
- LEFT_INDEX,
- LEFT_MIDDLE,
- LEFT_RING,
- LEFT_LITTLE,
- RIGHT_THUMB,
- RIGHT_INDEX,
- RIGHT_MIDDLE,
- RIGHT_RING,
- RIGHT_LITTLE,
-};
-
-/**
- * fp_scan_type:
- * @FP_SCAN_TYPE_PRESS: the reader has a surface area that covers the whole finger
- * @FP_SCAN_TYPE_SWIPE: the reader requires swiping the finger on a smaller area
- *
- * Numeric codes used to refer to the scan type of the device. Devices require
- * either swiping or pressing the finger on the device. This is useful for
- * front-ends.
- */
-enum fp_scan_type {
- FP_SCAN_TYPE_PRESS = 0,
- FP_SCAN_TYPE_SWIPE,
-};
-
-/* Drivers */
-const char *fp_driver_get_name(struct fp_driver *drv);
-const char *fp_driver_get_full_name(struct fp_driver *drv);
-uint16_t fp_driver_get_driver_id(struct fp_driver *drv);
-enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv);
-int fp_driver_supports_imaging(struct fp_driver *drv);
-
-/* Device discovery */
-struct fp_dscv_dev **fp_discover_devs(void);
-void fp_dscv_devs_free(struct fp_dscv_dev **devs);
-struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev);
-uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev);
-uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev);
-int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
- struct fp_print_data *print);
-
-/* Device handling */
-struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev);
-void fp_dev_close(struct fp_dev *dev);
-struct fp_driver *fp_dev_get_driver(struct fp_dev *dev);
-int fp_dev_get_nr_enroll_stages(struct fp_dev *dev);
-uint32_t fp_dev_get_devtype(struct fp_dev *dev);
-int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data);
-
-/**
- * fp_capture_result:
- * @FP_CAPTURE_COMPLETE: Capture completed successfully, the capture data has been returned to the caller.
- * @FP_CAPTURE_FAIL: Capture failed
- *
- * Whether a capture failed or completed.
- */
-enum fp_capture_result {
- FP_CAPTURE_COMPLETE = 0,
- FP_CAPTURE_FAIL,
-};
-
-int fp_dev_supports_imaging(struct fp_dev *dev);
-int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
- struct fp_img **img);
-int fp_dev_get_img_width(struct fp_dev *dev);
-int fp_dev_get_img_height(struct fp_dev *dev);
-
-/**
- * fp_enroll_result:
- * @FP_ENROLL_COMPLETE: Enrollment completed successfully, the enrollment data has been
- * returned to the caller.
- * @FP_ENROLL_FAIL: Enrollment failed due to incomprehensible data; this may occur when
- * the user scans a different finger on each enroll stage.
- * @FP_ENROLL_PASS: Enroll stage passed; more stages are need to complete the process.
- * @FP_ENROLL_RETRY: The enrollment scan did not succeed due to poor scan quality or
- * other general user scanning problem.
- * @FP_ENROLL_RETRY_TOO_SHORT: The enrollment scan did not succeed because the finger swipe was
- * too short.
- * @FP_ENROLL_RETRY_CENTER_FINGER: The enrollment scan did not succeed because the finger was not
- * centered on the scanner.
- * @FP_ENROLL_RETRY_REMOVE_FINGER: The verification scan did not succeed due to quality or pressure
- * problems; the user should remove their finger from the scanner before
- * retrying.
- *
- *
- * Enrollment result codes returned from fp_enroll_finger().
- * Result codes with RETRY in the name suggest that the scan failed due to
- * user error. Applications will generally want to inform the user of the
- * problem and then retry the enrollment stage. For more info on the semantics
- * of interpreting these result codes and tracking enrollment process, see
- * [Enrolling](libfprint-Devices-operations.html#enrolling)
- */
-enum fp_enroll_result {
- FP_ENROLL_COMPLETE = 1,
- FP_ENROLL_FAIL,
- FP_ENROLL_PASS,
- FP_ENROLL_RETRY = 100,
- FP_ENROLL_RETRY_TOO_SHORT,
- FP_ENROLL_RETRY_CENTER_FINGER,
- FP_ENROLL_RETRY_REMOVE_FINGER,
-};
-
-int fp_enroll_finger_img(struct fp_dev *dev, struct fp_print_data **print_data,
- struct fp_img **img);
-int fp_enroll_finger(struct fp_dev *dev,
- struct fp_print_data **print_data);
-
-/**
- * fp_verify_result:
- * @FP_VERIFY_NO_MATCH: The scan completed successfully, but the newly scanned fingerprint
- * does not match the fingerprint being verified against.
- * In the case of identification, this return code indicates that the
- * scanned finger could not be found in the print gallery.
- * @FP_VERIFY_MATCH: The scan completed successfully and the newly scanned fingerprint does
- * match the fingerprint being verified, or in the case of identification,
- * the scanned fingerprint was found in the print gallery.
- * @FP_VERIFY_RETRY: The scan did not succeed due to poor scan quality or other general
- * user scanning problem.
- * @FP_VERIFY_RETRY_TOO_SHORT: The scan did not succeed because the finger swipe was too short.
- * @FP_VERIFY_RETRY_CENTER_FINGER: The scan did not succeed because the finger was not centered on the
- * scanner.
- * @FP_VERIFY_RETRY_REMOVE_FINGER: The scan did not succeed due to quality or pressure problems; the user
- * should remove their finger from the scanner before retrying.
- *
- * Verification result codes returned from fp_verify_finger(). Return codes
- * are also shared with fp_identify_finger().
- * Result codes with RETRY in the name suggest that the scan failed due to
- * user error. Applications will generally want to inform the user of the
- * problem and then retry the verify operation.
- */
-enum fp_verify_result {
- FP_VERIFY_NO_MATCH = 0,
- FP_VERIFY_MATCH = 1,
- FP_VERIFY_RETRY = FP_ENROLL_RETRY,
- FP_VERIFY_RETRY_TOO_SHORT = FP_ENROLL_RETRY_TOO_SHORT,
- FP_VERIFY_RETRY_CENTER_FINGER = FP_ENROLL_RETRY_CENTER_FINGER,
- FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER,
-};
-
-int fp_verify_finger_img(struct fp_dev *dev,
- struct fp_print_data *enrolled_print, struct fp_img **img);
-int fp_verify_finger(struct fp_dev *dev,
- struct fp_print_data *enrolled_print);
-
-int fp_dev_supports_identification(struct fp_dev *dev);
-int fp_identify_finger_img(struct fp_dev *dev,
- struct fp_print_data **print_gallery, size_t *match_offset,
- struct fp_img **img);
-int fp_identify_finger(struct fp_dev *dev,
- struct fp_print_data **print_gallery, size_t *match_offset);
-
-/* Data handling */
-void fp_print_data_free(struct fp_print_data *data);
-size_t fp_print_data_get_data(struct fp_print_data *data, unsigned char **ret);
-struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
- size_t buflen);
-uint16_t fp_print_data_get_driver_id(struct fp_print_data *data);
-uint32_t fp_print_data_get_devtype(struct fp_print_data *data);
-
-/* Image handling */
-
-/**
- * fp_minutia:
- *
- * #fp_minutia is an opaque structure type. You must access it using the
- * functions in this section.
- */
-struct fp_minutia;
-
-int fp_img_get_height(struct fp_img *img);
-int fp_img_get_width(struct fp_img *img);
-unsigned char *fp_img_get_data(struct fp_img *img);
-int fp_img_save_to_file(struct fp_img *img, char *path);
-void fp_img_standardize(struct fp_img *img);
-struct fp_img *fp_img_binarize(struct fp_img *img);
-struct fp_minutia **fp_img_get_minutiae(struct fp_img *img, int *nr_minutiae);
-int fp_minutia_get_coords(struct fp_minutia *minutia, int *coord_x, int *coord_y);
-void fp_img_free(struct fp_img *img);
-
-/* Polling and timing */
-
-/**
- * fp_pollfd:
- * @fd: a file descriptor
- * @events: Event flags to poll for from ``
- *
- * A structure representing a file descriptor and the @events to poll
- * for, as returned by fp_get_pollfds().
- */
-struct fp_pollfd {
- int fd;
- short int events;
-};
-
-int fp_handle_events_timeout(struct timeval *timeout);
-int fp_handle_events(void);
-ssize_t fp_get_pollfds(struct fp_pollfd **pollfds);
-int fp_get_next_timeout(struct timeval *tv);
-
-/**
- * fp_pollfd_added_cb:
- * @fd: the new file descriptor
- * @events: events to monitor for, see `` for the possible values
- *
- * Type definition for a function that will be called when a new
- * event source is added. The @events argument is a flag as defined in
- * `` such as `POLLIN`, or `POLLOUT`. See fp_set_pollfd_notifiers().
- */
-typedef void (*fp_pollfd_added_cb)(int fd, short int events);
-
-/**
- * fp_pollfd_removed_cb:
- * @fd: the file descriptor to stop monitoring
- *
- * Type definition for a function that will be called when an
- * event source is removed. See fp_set_pollfd_notifiers().
- */
-typedef void (*fp_pollfd_removed_cb)(int fd);
-void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
- fp_pollfd_removed_cb removed_cb);
-
-/* Library */
-int fp_init(void);
-void fp_exit(void);
-
-/* Asynchronous I/O */
-
-/**
- * fp_operation_stop_cb:
- * @dev: the struct #fp_dev device
- * @user_data: user data passed to the callback
- *
- * Type definition for a function that will be called when fp_async_dev_close(),
- * fp_async_verify_stop(), fp_async_identify_stop() or fp_async_capture_stop()
- * finishes.
- */
-typedef void (*fp_operation_stop_cb)(struct fp_dev *dev, void *user_data);
-
-/**
- * fp_img_operation_cb:
- * @dev: the struct #fp_dev device
- * @result: an #fp_verify_result for fp_async_verify_start(), or an #fp_capture_result
- * for fp_async_capture_start(), or a negative value on error
- * @img: the captured #fp_img if capture or verification was successful
- * @user_data: user data passed to the callback
- *
- * Type definition for a function that will be called when fp_async_verify_start()
- * or fp_async_capture_start() finished.
- */
-typedef void (*fp_img_operation_cb)(struct fp_dev *dev, int result,
- struct fp_img *img, void *user_data);
-
-/**
- * fp_dev_open_cb:
- * @dev: the struct #fp_dev device
- * @status: 0 on success, or a negative value on error
- * @user_data: user data passed to the callback
- *
- * Type definition for a function that will be called when fp_async_dev_open
- * finishes.
- */
-typedef void (*fp_dev_open_cb)(struct fp_dev *dev, int status, void *user_data);
-
-int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
- void *user_data);
-
-void fp_async_dev_close(struct fp_dev *dev, fp_operation_stop_cb callback,
- void *user_data);
-
-/**
- * fp_enroll_stage_cb:
- * @dev: the struct #fp_dev device
- * @result: a #fp_enroll_result on success, or a negative value on failure
- * @print: the enrollment data from the final stage
- * @img: an #fp_img to free with fp_img_free()
- * @user_data: user data passed to the callback
- *
- * Type definition for a function that will be called when
- * fp_async_enroll_start() finishes. See fp_enroll_finger_img() for
- * the expected behaviour of your program based on the @result.
- */
-typedef void (*fp_enroll_stage_cb)(struct fp_dev *dev, int result,
- struct fp_print_data *print, struct fp_img *img, void *user_data);
-
-int fp_async_enroll_start(struct fp_dev *dev, fp_enroll_stage_cb callback,
- void *user_data);
-
-int fp_async_enroll_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
- void *user_data);
-
-int fp_async_verify_start(struct fp_dev *dev, struct fp_print_data *data,
- fp_img_operation_cb callback, void *user_data);
-
-int fp_async_verify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
- void *user_data);
-
-/**
- * fp_identify_cb:
- * @dev: the struct #fp_dev device
- * @result: a #fp_verify_result on success, or a negative value on error.
- * @match_offset: the array index of the matched gallery print (if any was found).
- * Only valid if %FP_VERIFY_MATCH was returned.
- * @img: the scan image, it must be freed with fp_img_free() after use.
- * @user_data: user data passed to the callback
- *
- * Type definition for a function that will be called when fp_async_identify_start()
- * finishes.
- */
-typedef void (*fp_identify_cb)(struct fp_dev *dev, int result,
- size_t match_offset, struct fp_img *img, void *user_data);
-int fp_async_identify_start(struct fp_dev *dev, struct fp_print_data **gallery,
- fp_identify_cb callback, void *user_data);
-
-int fp_async_identify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
- void *user_data);
-
-int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_img_operation_cb callback, void *user_data);
-
-int fp_async_capture_stop(struct fp_dev *dev, fp_operation_stop_cb callback, void *user_data);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+#include "fp-context.h"
+#include "fp-device.h"
+#include "fp-image.h"
diff --git a/libfprint/libfprint.ver b/libfprint/libfprint.ver
index d99a456..7b484f6 100644
--- a/libfprint/libfprint.ver
+++ b/libfprint/libfprint.ver
@@ -1,6 +1,9 @@
LIBFPRINT_2.0.0 {
global:
fp_*;
+
+ /* Needs to be public for the listing commands. */
+ fpi_get_driver_types;
local:
*;
};
diff --git a/libfprint/meson.build b/libfprint/meson.build
index 57264c0..96cf85a 100644
--- a/libfprint/meson.build
+++ b/libfprint/meson.build
@@ -1,15 +1,27 @@
libfprint_sources = [
- 'fpi-async.c',
+ 'fp-context.c',
+ 'fp-device.c',
+ 'fp-image.c',
+ 'fp-print.c',
+ 'fp-image-device.c',
'fpi-assembling.c',
- 'fpi-core.c',
- 'fpi-data.c',
- 'fpi-dev.c',
- 'fpi-dev-img.c',
- 'fpi-img.c',
'fpi-ssm.c',
- 'fpi-sync.c',
- 'fpi-poll.c',
- 'fpi-usb.c',
+ 'fpi-usb-transfer.c',
+]
+
+libfprint_public_headers = [
+ 'fp-context.h',
+ 'fp-device.h',
+ 'fp-image.h',
+ 'fp-print.h',
+]
+
+libfprint_private_headers = [
+ 'fpi-assembling.h',
+ 'fpi-device.h',
+ 'fpi-image.h',
+ 'fpi-image-device.h',
+ 'fpi-print.h',
]
nbis_sources = [
@@ -134,29 +146,32 @@ if aes3k
endif
other_sources = []
-if imaging_dep.found()
- other_sources += [ 'fpi-img-pixman.c' ]
-endif
-libfprint_sources += configure_file(input: 'empty_file',
- output: 'drivers_definitions.h',
- capture: true,
- command: [
- 'echo',
- drivers_struct_list
- ])
+fp_enums = gnome.mkenums_simple('fp-enums',
+ sources: libfprint_public_headers,
+ install_header : true)
+fp_enums_h = fp_enums[1]
-libfprint_sources += configure_file(input: 'empty_file',
- output: 'drivers_arrays.h',
- capture: true,
- command: [
- 'echo',
- drivers_primitive_array + '\n\n' + drivers_img_array
- ])
+fpi_enums = gnome.mkenums_simple('fpi-enums',
+ sources: libfprint_private_headers,
+ install_header : true)
+fpi_enums_h = fpi_enums[1]
-deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep ]
+drivers_sources += configure_file(input: 'empty_file',
+ output: 'fp-drivers.c',
+ capture: true,
+ command: [
+ 'echo',
+ drivers_type_list + '\n\n' + drivers_type_func
+ ])
+
+mapfile = 'libfprint.ver'
+vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
+
+deps = [ mathlib_dep, glib_dep, gusb_dep, nss_dep, imaging_dep, gio_dep ]
libfprint = library('fprint',
- libfprint_sources + drivers_sources + nbis_sources + other_sources,
+ libfprint_sources + fp_enums + fpi_enums +
+ drivers_sources + nbis_sources + other_sources,
soversion: soversion,
version: libversion,
c_args: common_cflags + drivers_cflags,
@@ -164,14 +179,17 @@ libfprint = library('fprint',
root_inc,
include_directories('nbis/include'),
],
+ link_args : vflag,
+ link_depends : mapfile,
dependencies: deps,
install: true)
libfprint_dep = declare_dependency(link_with: libfprint,
+ sources: [ fp_enums_h ],
include_directories: root_inc,
- dependencies: glib_dep)
+ dependencies: [glib_dep, gusb_dep, gio_dep])
-install_headers(['fprint.h'], subdir: 'libfprint')
+install_headers(['fprint.h'] + libfprint_public_headers, subdir: 'libfprint')
udev_rules = executable('fprint-list-udev-rules',
'fprint-list-udev-rules.c',
@@ -197,3 +215,35 @@ supported_devices = executable('fprint-list-supported-devices',
],
dependencies: [ deps, libfprint_dep ],
install: false)
+
+
+if get_option('introspection')
+ # We do *not* include the private header here
+ libfprint_girtarget = gnome.generate_gir(libfprint,
+ sources : fp_enums + [
+ libfprint_public_headers,
+ libfprint_sources,
+ ],
+ nsversion : '2.0',
+ namespace : 'FPrint',
+ symbol_prefix : 'fp_',
+ identifier_prefix : 'Fp',
+ export_packages : 'fprint',
+ extra_args : [
+ '--c-include=fprint.h',
+ ],
+ link_with : libfprint,
+ dependencies : [
+ gio_dep,
+ gusb_dep,
+ ],
+ includes : [
+ 'Gio-2.0',
+ 'GObject-2.0',
+ 'GUsb-1.0',
+ ],
+ install : true
+ )
+ libfprint_gir = libfprint_girtarget[0]
+ libfprint_typelib = libfprint_girtarget[1]
+endif
diff --git a/meson.build b/meson.build
index 2f04303..68010aa 100644
--- a/meson.build
+++ b/meson.build
@@ -8,6 +8,8 @@ project('libfprint', [ 'c', 'cpp' ],
],
meson_version: '>= 0.45.0')
+gnome = import('gnome')
+
add_project_arguments([ '-D_GNU_SOURCE' ], language: 'c')
add_project_arguments([ '-DG_LOG_DOMAIN="libfprint"' ], language: 'c')
@@ -19,7 +21,6 @@ host_system = host_machine.system()
common_cflags = cc.get_supported_arguments([
'-fgnu89-inline',
- '-fvisibility=hidden',
'-std=gnu99',
'-Wall',
'-Wundef',
@@ -42,6 +43,7 @@ libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
# Dependencies
glib_dep = dependency('glib-2.0', version: '>= 2.50')
+gio_dep = dependency('gio-unix-2.0', version: '>= 2.44.0')
gusb_dep = dependency('gusb', version: '>= 0.3.0')
mathlib_dep = cc.find_library('m', required: false)
@@ -67,6 +69,7 @@ endif
nss_dep = dependency('', required: false)
imaging_dep = dependency('', required: false)
+libfprint_conf.set10('HAVE_PIXMAN', false)
foreach driver: drivers
if driver == 'uru4000'
nss_dep = dependency('nss', required: false)
@@ -79,27 +82,23 @@ foreach driver: drivers
if not imaging_dep.found()
error('pixman is required for imaging support')
endif
+
+ libfprint_conf.set10('HAVE_PIXMAN', true)
endif
if not all_drivers.contains(driver)
error('Invalid driver \'' + driver + '\'')
endif
endforeach
-# Export the drivers' structures to the core code
-drivers_struct_list = ''
-drivers_img_array = 'static struct fp_img_driver * const img_drivers[] = {\n'
-drivers_primitive_array = 'static struct fp_driver * const primitive_drivers[] = {\n'
+# Export the drivers' types to the core code
+drivers_type_list = '#include \n'
+drivers_type_func = 'void fpi_get_driver_types(GArray *drivers)\n{\n\tGType t;\n'
foreach driver: drivers
- if primitive_drivers.contains(driver)
- drivers_struct_list += 'extern struct fp_driver ' + driver + '_driver;\n'
- drivers_primitive_array += ' &' + driver + '_driver,\n'
- else
- drivers_struct_list += 'extern struct fp_img_driver ' + driver + '_driver;\n'
- drivers_img_array += ' &' + driver + '_driver,\n'
- endif
+ drivers_type_list += 'extern GType (fpi_device_' + driver + '_get_type) (void);\n'
+ drivers_type_func += ' t = fpi_device_' + driver + '_get_type(); g_array_append_val (drivers, t);\n'
endforeach
-drivers_img_array += '};'
-drivers_primitive_array += '};'
+drivers_type_list += ''
+drivers_type_func += '};'
root_inc = include_directories('.')
@@ -129,7 +128,6 @@ if get_option('gtk-examples')
endif
endif
-libfprint_conf.set('API_EXPORTED', '__attribute__((visibility("default")))')
configure_file(output: 'config.h', configuration: libfprint_conf)
subdir('libfprint')
diff --git a/meson_options.txt b/meson_options.txt
index 78f16ef..746efdc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,6 +2,10 @@ option('drivers',
description: 'Drivers to integrate, "default" selects the default set, "all" selects all drivers',
type: 'string',
value: 'default')
+option('introspection',
+ description: 'Build GObject Introspection repository',
+ type: 'boolean',
+ value: true)
option('udev_rules',
description: 'Whether to create a udev rules file',
type: 'boolean',