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

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