lib: Major rewrite of the libfprint core and API

This is a rewrite of the core based on GObject and Gio. This commit
breaks the build in a lot of ways, but basic functionality will start
working again with the next commits.
This commit is contained in:
Benjamin Berg 2019-07-03 23:29:05 +02:00
parent 30a449841c
commit 689aff0232
53 changed files with 8358 additions and 6759 deletions

View File

@ -60,21 +60,18 @@
<para>
In summary, libfprint represents fingerprints in several internal structures
and each representation will offer you a way of determining the
<ulink url="#driver_id">driver ID</ulink> and <ulink url="#device-types">devtype</ulink> of the print in
<ulink url="#driver">driver</ulink> and <ulink url="#device-id">device ID</ulink> of the print in
question. Prints are only compatible if the driver ID <emphasis role="strong">and</emphasis> 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.
</para>
</refsect2>
<refsect2 id="driver_id">
<title>Driver IDs</title>
<refsect2 id="driver">
<title>Driver</title>
<para>
Each driver is assigned a unique ID by the project maintainer. These
assignments are
<ulink url="https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/libfprint/drivers/driver_ids.h">
documented in the sources</ulink> and will never change.
Each driver is assigned a unique string identifier by the project maintainer.
</para>
<para>
@ -89,22 +86,23 @@
</para>
</refsect2>
<refsect2 id="device-types">
<title>Device types</title>
<refsect2 id="device-id">
<title>Device ID</title>
<para>
Internally, the <ulink url="libfprint-Driver-operations.html#libfprint-Driver-operations.description">driver</ulink> behind a device assigns a 32-bit
<emphasis>devtype</emphasis> 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".
</para>
<para>
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

View File

@ -13,12 +13,12 @@
<para>
Usually the first thing you want to do is determine which fingerprint
devices are present. This is done through <ulink url="libfprint-Device-discovery.html">device discovery</ulink>.
devices are present. This is done using the <ulink url="fp-context.html">FpContext</ulink> object.
</para>
<para>
Once you have found a device you would like to operate, you should open it.
Refer to <ulink url="libfprint-Devices-operations.html">device operations</ulink>. This section also details enrollment,
Refer to <ulink url="fp-context.html">device operations</ulink>. This section also details enrollment,
image capture, and verification.
</para>

View File

@ -25,39 +25,43 @@
<part>
<title>Library API Documentation</title>
<xi:include href="xml/events.xml"/>
<xi:include href="xml/discovery.xml"/>
<xi:include href="xml/drv.xml"/>
<xi:include href="xml/dev.xml"/>
<xi:include href="xml/print_data.xml"/>
<xi:include href="xml/img.xml"/>
<xi:include href="xml/fp-context.xml"/>
<xi:include href="xml/fp-device.xml"/>
<xi:include href="xml/fp-image-device.xml"/>
<xi:include href="xml/fp-print.xml"/>
<xi:include href="xml/fp-image.xml"/>
</part>
<part>
<title>Writing Drivers</title>
<chapter id="driver-helpers">
<title>Logging, and async machinery</title>
<xi:include href="xml/fpi-log.xml"/>
<xi:include href="xml/fpi-ssm.xml"/>
<xi:include href="xml/fpi-poll.xml"/>
<chapter id="driver-dev">
<title>Device methods for drivers</title>
<xi:include href="xml/fpi-device.xml"/>
<xi:include href="xml/fpi-image-device.xml"/>
</chapter>
<chapter id="driver-dev">
<title>Device and driver structures</title>
<xi:include href="xml/fpi-dev.xml"/>
<xi:include href="xml/fpi-dev-img.xml"/>
<xi:include href="xml/fpi-core.xml"/>
<xi:include href="xml/fpi-core-img.xml"/>
<chapter id="driver-helpers">
<title>USB and State Machine helpers</title>
<xi:include href="xml/fpi-usb-transfer.xml"/>
<xi:include href="xml/fpi-ssm.xml"/>
<xi:include href="xml/fpi-log.xml"/>
</chapter>
<chapter id="driver-img">
<title>Image manipulation</title>
<xi:include href="xml/fpi-img.xml"/>
<xi:include href="xml/fpi-image.xml"/>
<xi:include href="xml/fpi-assembling.xml"/>
</chapter>
<xi:include href="xml/fpi-usb.xml"/>
<chapter id="driver-print">
<title>Print handling</title>
<xi:include href="xml/fpi-print.xml"/>
</chapter>
<chapter id="driver-misc">
<title>Listing drivers</title>
<xi:include href="xml/fpi-context.xml"/>
</chapter>
</part>
<index id="api-index">

View File

@ -1,125 +1,191 @@
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>events</FILE>
<TITLE>Initialisation and events handling</TITLE>
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
<FILE>drivers_api</FILE>
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>discovery</FILE>
<TITLE>Device discovery</TITLE>
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
<FILE>fp-context</FILE>
<TITLE>FpContext</TITLE>
FP_TYPE_CONTEXT
FpContextClass
fp_context_new
fp_context_enumerate
fp_context_get_devices
FpContext
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>drv</FILE>
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
<FILE>fp-device</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>dev</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>print_data</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>img</FILE>
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
<FILE>fp-image</FILE>
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
</SECTION>
<SECTION>
<FILE>fp-image-device</FILE>
FP_TYPE_IMAGE_DEVICE
FpImageDevice
</SECTION>
<SECTION>
<FILE>fp-print</FILE>
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
</SECTION>
<SECTION>
<FILE>fpi-assembling</FILE>
fpi_frame
fpi_frame_asmbl_ctx
fpi_do_movement_estimation
fpi_assemble_frames
fpi_line_asmbl_ctx
fpi_assemble_lines
</SECTION>
<SECTION>
<FILE>fpi-context</FILE>
fpi_get_driver_types
</SECTION>
<SECTION>
<FILE>fpi-device</FILE>
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
</SECTION>
<SECTION>
<FILE>fpi-image</FILE>
FpiImageFlags
FpImage
fpi_std_sq_dev
fpi_mean_sq_diff_norm
fpi_image_resize
</SECTION>
<SECTION>
<FILE>fpi-image-device</FILE>
<TITLE>FpImageDevice</TITLE>
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
</SECTION>
<SECTION>
<INCLUDE>fpi-log.h</INCLUDE>
<FILE>fpi-log</FILE>
fp_dbg
fp_info
@ -130,121 +196,57 @@ BUG
</SECTION>
<SECTION>
<INCLUDE>fpi-ssm.h</INCLUDE>
<FILE>fpi-ssm</FILE>
fpi_ssm
ssm_completed_fn
ssm_handler_fn
<FILE>fpi-print</FILE>
FpPrintType
FpiMatchResult
fpi_print_add_print
fpi_print_set_type
fpi_print_set_device_stored
fpi_print_add_from_image
fpi_print_bz3_match
</SECTION>
<SECTION>
<FILE>fpi-ssm</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fpi-poll.h</INCLUDE>
<FILE>fpi-poll</FILE>
fpi_timeout
fpi_timeout_fn
fpi_timeout_add
fpi_timeout_set_name
fpi_timeout_cancel
<FILE>fpi-usb-transfer</FILE>
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
<SUBSECTION Standard>
FPI_TYPE_USB_TRANSFER
fpi_usb_transfer_get_type
</SECTION>
<SECTION>
<INCLUDE>fpi-dev.h</INCLUDE>
<FILE>fpi-dev</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fpi-dev-img.h</INCLUDE>
<FILE>fpi-dev-img</FILE>
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
</SECTION>
<SECTION>
<INCLUDE>fpi-core.h</INCLUDE>
<FILE>fpi-core</FILE>
usb_id
fp_driver_type
</SECTION>
<SECTION>
<INCLUDE>fpi-core.h</INCLUDE>
<FILE>fpi-core-img</FILE>
FpiImgDriverFlags
fp_img_driver
</SECTION>
<SECTION>
<INCLUDE>fpi-img.h</INCLUDE>
<FILE>fpi-img</FILE>
FpiImgFlags
fpi_img_new
fpi_img_new_for_imgdev
fpi_img_realloc
fpi_img_resize
fpi_std_sq_dev
fpi_mean_sq_diff_norm
</SECTION>
<SECTION>
<INCLUDE>fpi-assembling.h</INCLUDE>
<FILE>fpi-assembling</FILE>
fpi_frame
fpi_frame_asmbl_ctx
fpi_line_asmbl_ctx
fpi_do_movement_estimation
fpi_assemble_frames
fpi_assemble_lines
</SECTION>
<SECTION>
<INCLUDE>fpi-usb.h</INCLUDE>
<FILE>fpi-usb</FILE>
fpi_usb_transfer
fpi_usb_transfer_cb_fn
fpi_usb_alloc
fpi_usb_fill_bulk_transfer
fpi_usb_submit_transfer
fpi_usb_cancel_transfer
</SECTION>

View File

@ -0,0 +1,118 @@
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>context</FILE>
<TITLE>Device discovery and hotplugging</TITLE>
FP_TYPE_CONTEXT
FpContext
fp_context_new
fp_context_enumerate
fp_context_get_devices
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>device</FILE>
<TITLE>Device</TITLE>
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
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>print</FILE>
<TITLE>Fingerprint handling</TITLE>
FpPrint
fp_print_new
</SECTION>
<SECTION>
<INCLUDE>fprint.h</INCLUDE>
<FILE>image</FILE>
<TITLE>Image handling</TITLE>
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
</SECTION>
<SECTION>
<FILE>internal-image-device</FILE>
<INCLUDE>drivers_api.h</INCLUDE>
<TITLE>Base class for image devices</TITLE>
FpImageDevice
FpImageDeviceClass
FpImageDeviceState
</SECTION>
<SECTION>
<FILE>internal-usb-transfers</FILE>
<INCLUDE>drivers_api.h</INCLUDE>
<TITLE>USB Transfers</TITLE>
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
</SECTION>

View File

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

View File

@ -23,17 +23,12 @@
#include <config.h>
#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

364
libfprint/fp-context.c Normal file
View File

@ -0,0 +1,364 @@
/*
* FpContext - A FPrint context
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <fpi-log.h>
#include "fpi-context.h"
#include "fpi-device.h"
#include <gusb.h>
/**
* 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 <link linkend="device-added">device-added</link> 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;
}

52
libfprint/fp-context.h Normal file
View File

@ -0,0 +1,52 @@
/*
* FpContext - A FPrint context
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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

2574
libfprint/fp-device.c Normal file

File diff suppressed because it is too large Load Diff

255
libfprint/fp-device.h Normal file
View File

@ -0,0 +1,255 @@
/*
* FpDevice - A fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <glib-object.h>
#include <gio/gio.h>
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

795
libfprint/fp-image-device.c Normal file
View File

@ -0,0 +1,795 @@
/*
* FpImageDevice - An image based fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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);
}

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
* FpImageDevice - An image based fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <fp-device.h>
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

610
libfprint/fp-image.c Normal file
View File

@ -0,0 +1,610 @@
/*
* FPrint Image
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <pixman.h>
#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 <ulink
* url="libfprint-FpImage.html">FpImage routines</ulink>.
*/
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

59
libfprint/fp-image.h Normal file
View File

@ -0,0 +1,59 @@
/*
* FPrint Image
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <gio/gio.h>
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

1118
libfprint/fp-print.c Normal file

File diff suppressed because it is too large Load Diff

101
libfprint/fp-print.h Normal file
View File

@ -0,0 +1,101 @@
/*
* FPrint Print handling
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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

View File

@ -20,147 +20,10 @@
#ifndef __FPRINT_INTERNAL_H__
#define __FPRINT_INTERNAL_H__
#include <config.h>
#include <stdint.h>
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#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

View File

@ -23,12 +23,8 @@
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#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;
}

View File

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

View File

@ -1,706 +0,0 @@
/*
* Asynchronous I/O functionality
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <glib.h>
/*
* 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;
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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

33
libfprint/fpi-context.h Normal file
View File

@ -0,0 +1,33 @@
/*
* FpContext - A FPrint context
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <gusb.h>
#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);

View File

@ -1,730 +0,0 @@
/*
* Core functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <libusb.h>
#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 <emphasis role="strong">before</emphasis> 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);
}

View File

@ -1,118 +0,0 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <fprint.h>
#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:
*
* |[<!-- language="C" -->
* 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

View File

@ -1,340 +0,0 @@
/*
* Fingerprint data handling and storage
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#include <glib/gstdio.h>
#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;
}

View File

@ -1,660 +0,0 @@
/*
* Core imaging device functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <errno.h>
#include <glib.h>
#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;
}

View File

@ -1,101 +0,0 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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

View File

@ -1,150 +0,0 @@
/*
* fp_dev types manipulation
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <glib.h>
/**
* 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;
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <libusb.h>
#include <fprint.h>
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

252
libfprint/fpi-device.h Normal file
View File

@ -0,0 +1,252 @@
/*
* FpDevice - A fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <gusb.h>
#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

View File

@ -0,0 +1,118 @@
/*
* FpImageDevice - An image based fingerprint reader device
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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);

83
libfprint/fpi-image.h Normal file
View File

@ -0,0 +1,83 @@
/*
* FPrint Image
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <config.h>
#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

View File

@ -1,75 +0,0 @@
/*
* Imaging utility functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 <pixman.h>
#include <string.h>
#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;
}

View File

@ -1,682 +0,0 @@
/*
* Image management functions for libfprint
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
*
* 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 <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#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;
}

View File

@ -1,85 +0,0 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <stdint.h>
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

View File

@ -1,485 +0,0 @@
/*
* Polling/timing management
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <glib.h>
#include <libusb.h>
/**
* 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);
}
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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

50
libfprint/fpi-print.h Normal file
View File

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

View File

@ -1,6 +1,7 @@
/*
* Functions to assist with asynchronous driver <---> library communications
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <config.h>
#include <errno.h>
/**
* 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);
}

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <stdint.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include <libusb.h>
#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);

View File

@ -1,690 +0,0 @@
/*
* Synchronous I/O functionality
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 <config.h>
#include <errno.h>
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 <emphasis role="strong">not</emphasis> 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;
}

View File

@ -0,0 +1,522 @@
/*
* FPrint USB transfer handling
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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;
}

View File

@ -0,0 +1,157 @@
/*
* FPrint USB transfer handling
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <gusb.h>
#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

View File

@ -1,236 +0,0 @@
/*
* Driver API definitions
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <glib.h>
#include <glib/gprintf.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
/* 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);
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.net>
*
* 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 <libusb.h>
#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

View File

@ -2,6 +2,7 @@
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <stdio.h>
#include <locale.h>
#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)

View File

@ -2,6 +2,7 @@
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
* Coypright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <config.h>
#include <stdio.h>
#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);

View File

@ -1,6 +1,6 @@
/*
* Main definitions for libfprint
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
*
* 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 <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/time.h>
/**
* 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 `<poll.h>`
*
* 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 `<poll.h>` 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
* `<poll.h>` 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"

View File

@ -1,6 +1,9 @@
LIBFPRINT_2.0.0 {
global:
fp_*;
/* Needs to be public for the listing commands. */
fpi_get_driver_types;
local:
*;
};

View File

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

View File

@ -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 <glib-object.h>\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')

View File

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