Public async API implementation

Involved some internal overhaul/reorganisation. Nice side effect is that
the synchronous API is now expressed purely in terms of the public async
API.
This commit is contained in:
Daniel Drake 2008-02-26 18:04:54 +00:00
parent 83f9da1b87
commit 88e9f4a5f8
12 changed files with 1054 additions and 872 deletions

View file

@ -54,12 +54,14 @@ libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS) $(CRYP
libfprint_la_SOURCES = \ libfprint_la_SOURCES = \
fp_internal.h \ fp_internal.h \
async.c \
core.c \ core.c \
data.c \ data.c \
drv.c \ drv.c \
img.c \ img.c \
imgdev.c \ imgdev.c \
poll.c \ poll.c \
sync.c \
aeslib.c \ aeslib.c \
aeslib.h \ aeslib.h \
$(DRIVER_SRC) \ $(DRIVER_SRC) \

414
libfprint/async.c Normal file
View file

@ -0,0 +1,414 @@
/*
* 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 <config.h>
#include <errno.h>
#include <glib.h>
#include "fp_internal.h"
/* 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);
}
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
void *user_data)
{
struct fp_driver *drv = ddev->drv;
struct fp_dev *dev;
libusb_dev_handle *udevh;
int r;
fp_dbg("");
udevh = libusb_open(ddev->udev);
if (!udevh) {
fp_err("usb_open failed");
return -EIO;
}
dev = g_malloc0(sizeof(*dev));
dev->drv = drv;
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = cb;
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)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
libusb_close(dev->udev);
if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data);
g_free(dev);
}
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
fp_dev_close_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
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;
if (!drv->close) {
fpi_drvcb_close_complete(dev);
return;
}
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;
}
}
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
fp_enroll_stage_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
int r;
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)
{
fp_dbg("");
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);
}
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
fp_enroll_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
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;
}
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
struct fp_print_data *data, fp_verify_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
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)
{
fp_dbg("");
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)
{
fp_dbg("");
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);
}
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
fp_verify_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
int r;
fp_dbg("");
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;
}
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 = dev->drv;
int r;
fp_dbg("");
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");
}
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
fp_identify_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
int r;
fp_dbg("");
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)
{
fp_dbg("");
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);
}

View file

@ -26,6 +26,8 @@
#include "fp_internal.h" #include "fp_internal.h"
GSList *opened_devices = NULL;
/** /**
* \mainpage libfprint API Reference * \mainpage libfprint API Reference
* libfprint is an open source library to provide access to fingerprint * libfprint is an open source library to provide access to fingerprint
@ -273,7 +275,6 @@
*/ */
static GSList *registered_drivers = NULL; static GSList *registered_drivers = NULL;
static GSList *opened_devices = NULL;
void fpi_log(enum fpi_log_level level, const char *component, void fpi_log(enum fpi_log_level level, const char *component,
const char *function, const char *format, ...) const char *function, const char *format, ...)
@ -565,89 +566,6 @@ API_EXPORTED struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev *
return NULL; return NULL;
} }
/** \ingroup dev
* Opens and initialises a device. This is the function you call in order
* to convert a \ref dscv_dev "discovered device" into an actual device handle
* that you can perform operations with.
* \param ddev the discovered device to open
* \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;
struct fp_driver *drv = ddev->drv;
int r;
fp_dbg("");
libusb_dev_handle *udevh = libusb_open(ddev->udev);
if (!udevh) {
fp_err("usb_open failed");
return NULL;
}
dev = g_malloc0(sizeof(*dev));
dev->drv = drv;
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
r = fpi_drv_init(dev, ddev->driver_data);
if (r) {
fp_err("device initialisation failed, driver=%s", drv->name);
goto err;
}
while (dev->state == DEV_STATE_INITIALIZING)
if (fp_handle_events() < 0)
goto err_deinit;
if (dev->state != DEV_STATE_INITIALIZED)
goto err_deinit;
opened_devices = g_slist_prepend(opened_devices, (gpointer) dev);
return dev;
err_deinit:
fpi_drv_deinit(dev);
while (dev->state == DEV_STATE_DEINITIALIZING) {
if (fp_handle_events() < 0)
break;
}
err:
libusb_close(udevh);
g_free(dev);
return NULL;
}
/* performs close operation without modifying opened_devices list */
static void do_close(struct fp_dev *dev)
{
fpi_drv_deinit(dev);
while (dev->state == DEV_STATE_DEINITIALIZING)
if (fp_handle_events() < 0)
break;
libusb_close(dev->udev);
g_free(dev);
}
/** \ingroup dev
* Close a device. You must call this function when you are finished using
* a fingerprint device.
* \param dev the device to close. If NULL, function simply returns.
*/
API_EXPORTED void fp_dev_close(struct fp_dev *dev)
{
if (!dev)
return;
fp_dbg("");
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);
do_close(dev);
}
/** \ingroup dev /** \ingroup dev
* Get the \ref drv "driver" for a fingerprint device. * Get the \ref drv "driver" for a fingerprint device.
* \param dev the device * \param dev the device
@ -843,448 +761,6 @@ API_EXPORTED int fp_dev_get_img_height(struct fp_dev *dev)
return fpi_imgdev_get_img_height(imgdev); return fpi_imgdev_get_img_height(imgdev);
} }
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)
{
struct sync_enroll_data *edata = dev->enroll_data;
edata->result = result;
edata->data = data;
edata->img = img;
edata->populated = TRUE;
}
/** \ingroup dev
* Performs an enroll stage. See \ref 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 succesful in that the
* user did not position their finger correctly or similar. When a RETRY code
* is returned, the enrollment stage is <b>not</b> 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_result#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_result#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_result#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.
*
* \param dev the device
* \param print_data a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
* \param 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.
* \return 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;
struct sync_enroll_data *edata;
int r;
if (!dev->nr_enroll_stages || !drv->enroll_start) {
fp_err("driver %s has 0 enroll stages or no enroll func",
drv->name);
return -ENOTSUP;
}
if (stage == -1) {
fp_dbg("starting enrollment");
r = fpi_drv_enroll_start(dev, sync_enroll_cb);
if (r < 0) {
fp_err("failed to start enrollment");
return r;
}
while (dev->state == DEV_STATE_ENROLL_STARTING) {
r = fp_handle_events();
if (r < 0)
goto err;
}
if (dev->state != DEV_STATE_ENROLLING) {
r = -EIO;
goto err;
}
dev->__enroll_stage = ++stage;
dev->enroll_data = g_malloc0(sizeof(struct sync_enroll_data));
} 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);
edata = dev->enroll_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;
}
out:
if (final) {
fp_dbg("ending enrollment");
if (fpi_drv_enroll_stop(dev) == 0)
while (dev->state == DEV_STATE_ENROLL_STOPPING) {
if (fp_handle_events() < 0)
break;
}
g_free(dev->enroll_data);
}
return r;
err:
if (fpi_drv_enroll_stop(dev) == 0)
while (dev->state == DEV_STATE_ENROLL_STOPPING)
if (fp_handle_events() < 0)
break;
return r;
}
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)
{
struct sync_verify_data *vdata = dev->sync_verify_data;
vdata->result = result;
vdata->img = img;
vdata->populated = TRUE;
}
/** \ingroup dev
* Performs a new scan and verify 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.
*
* \param dev the device to perform the scan.
* \param enrolled_print the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
* \param 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.
* \return 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 fp_driver *drv = dev->drv;
struct sync_verify_data *vdata;
int r;
if (!enrolled_print) {
fp_err("no print given");
return -EINVAL;
}
if (!drv->verify_start) {
fp_err("driver %s has no verify func", drv->name);
return -ENOTSUP;
}
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", drv->name);
r = fpi_drv_verify_start(dev, sync_verify_cb, enrolled_print);
if (r < 0) {
fp_dbg("verify_start error %d", r);
return r;
}
while (dev->state == DEV_STATE_VERIFY_STARTING) {
r = fp_handle_events();
if (r < 0)
goto err;
}
if (dev->state != DEV_STATE_VERIFYING) {
r = -EIO;
goto err;
}
dev->sync_verify_data = g_malloc0(sizeof(struct sync_verify_data));
vdata = dev->sync_verify_data;
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(dev->sync_verify_data);
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 (fpi_drv_verify_stop(dev) == 0) {
while (dev->state == DEV_STATE_VERIFY_STOPPING) {
if (fp_handle_events() < 0)
break;
}
}
return r;
}
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)
{
struct sync_identify_data *idata = dev->sync_identify_data;
idata->result = result;
idata->match_offset = match_offset;
idata->img = img;
idata->populated = TRUE;
}
/** \ingroup dev
* 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_result#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.
*
* \param dev the device to perform the scan.
* \param 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.
* \param 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.
* \param 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.
* \return 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)
{
struct fp_driver *drv = dev->drv;
struct sync_identify_data *idata;
int r;
if (!drv->identify_start) {
fp_dbg("driver %s has no identify func", drv->name);
return -ENOTSUP;
}
fp_dbg("to be handled by %s", drv->name);
r = fpi_drv_identify_start(dev, sync_identify_cb, print_gallery);
if (r < 0) {
fp_err("identify_start error %d", r);
return r;
}
while (dev->state == DEV_STATE_IDENTIFY_STARTING) {
r = fp_handle_events();
if (r < 0)
goto err;
}
if (dev->state != DEV_STATE_IDENTIFYING) {
r = -EIO;
goto err;
}
dev->sync_identify_data = g_malloc0(sizeof(struct sync_identify_data));
idata = dev->sync_identify_data;
while (!idata->populated) {
r = fp_handle_events();
if (r < 0) {
g_free(idata);
goto err;
}
}
if (img)
*img = idata->img;
else
fp_img_free(idata->img);
r = idata->result;
switch (r) {
case FP_VERIFY_NO_MATCH:
fp_dbg("result: no match");
break;
case FP_VERIFY_MATCH:
fp_dbg("result: match at offset %zd", 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;
}
g_free(dev->sync_identify_data);
err:
if (fpi_drv_identify_stop(dev) == 0) {
while (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
if (fp_handle_events() < 0)
break;
}
}
return r;
}
/** \ingroup core /** \ingroup core
* Initialise libfprint. This function must be called before you attempt to * Initialise libfprint. This function must be called before you attempt to
* use the library in any way. * use the library in any way.
@ -1310,14 +786,18 @@ API_EXPORTED int fp_init(void)
*/ */
API_EXPORTED void fp_exit(void) API_EXPORTED void fp_exit(void)
{ {
GSList *elem = opened_devices;
fp_dbg(""); fp_dbg("");
if (elem != NULL) { if (opened_devices) {
do { GSList *copy = g_slist_copy(opened_devices);
fp_dbg("naughty app left a device open on exit!"); GSList *elem = copy;
do_close((struct fp_dev *) elem->data); fp_dbg("naughty app left devices open on exit!");
} while ((elem = g_slist_next(elem)));
do
fp_dev_close((struct fp_dev *) elem->data);
while ((elem = g_slist_next(elem)));
g_slist_free(copy);
g_slist_free(opened_devices); g_slist_free(opened_devices);
opened_devices = NULL; opened_devices = NULL;
} }
@ -1329,4 +809,3 @@ API_EXPORTED void fp_exit(void)
libusb_exit(); libusb_exit();
} }

View file

@ -907,7 +907,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
} }
dev->priv = g_malloc0(sizeof(struct aes2501_dev)); dev->priv = g_malloc0(sizeof(struct aes2501_dev));
fpi_imgdev_init_complete(dev, 0); fpi_imgdev_open_complete(dev, 0);
return 0; return 0;
} }
@ -915,7 +915,7 @@ static void dev_deinit(struct fp_img_dev *dev)
{ {
g_free(dev->priv); g_free(dev->priv);
libusb_release_interface(dev->udev, 0); libusb_release_interface(dev->udev, 0);
fpi_imgdev_deinit_complete(dev); fpi_imgdev_close_complete(dev);
} }
static const struct usb_id id_table[] = { static const struct usb_id id_table[] = {
@ -934,8 +934,8 @@ struct fp_img_driver aes2501_driver = {
.img_height = -1, .img_height = -1,
.img_width = 192, .img_width = 192,
.init = dev_init, .open = dev_init,
.deinit = dev_deinit, .close = dev_deinit,
.activate = dev_activate, .activate = dev_activate,
.deactivate = dev_deactivate, .deactivate = dev_deactivate,
}; };

View file

@ -213,7 +213,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
dev->priv = g_malloc0(sizeof(struct aes4k_dev)); dev->priv = g_malloc0(sizeof(struct aes4k_dev));
if (r == 0) if (r == 0)
fpi_imgdev_init_complete(dev, 0); fpi_imgdev_open_complete(dev, 0);
return r; return r;
} }
@ -222,7 +222,7 @@ static void dev_deinit(struct fp_img_dev *dev)
{ {
g_free(dev->priv); g_free(dev->priv);
libusb_release_interface(dev->udev, 0); libusb_release_interface(dev->udev, 0);
fpi_imgdev_deinit_complete(dev); fpi_imgdev_close_complete(dev);
} }
static const struct usb_id id_table[] = { static const struct usb_id id_table[] = {
@ -245,8 +245,8 @@ struct fp_img_driver aes4000_driver = {
/* temporarily lowered until image quality improves */ /* temporarily lowered until image quality improves */
.bz3_threshold = 9, .bz3_threshold = 9,
.init = dev_init, .open = dev_init,
.deinit = dev_deinit, .close = dev_deinit,
.activate = dev_activate, .activate = dev_activate,
.deactivate = dev_deactivate, .deactivate = dev_deactivate,
}; };

View file

@ -840,7 +840,7 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
dev->priv = upekdev; dev->priv = upekdev;
dev->nr_enroll_stages = 3; dev->nr_enroll_stages = 3;
fpi_drvcb_init_complete(dev, 0); fpi_drvcb_open_complete(dev, 0);
return 0; return 0;
} }
@ -848,7 +848,7 @@ static void dev_exit(struct fp_dev *dev)
{ {
libusb_release_interface(dev->udev, 0); libusb_release_interface(dev->udev, 0);
g_free(dev->priv); g_free(dev->priv);
fpi_drvcb_deinit_complete(dev); fpi_drvcb_close_complete(dev);
} }
static const unsigned char enroll_init[] = { static const unsigned char enroll_init[] = {
@ -1415,8 +1415,8 @@ struct fp_driver upekts_driver = {
.name = FP_COMPONENT, .name = FP_COMPONENT,
.full_name = "UPEK TouchStrip", .full_name = "UPEK TouchStrip",
.id_table = id_table, .id_table = id_table,
.init = dev_init, .open = dev_init,
.deinit = dev_exit, .close = dev_exit,
.enroll_start = enroll_start, .enroll_start = enroll_start,
.enroll_stop = enroll_stop, .enroll_stop = enroll_stop,
.verify_start = verify_start, .verify_start = verify_start,

View file

@ -1096,7 +1096,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
urudev->interface = iface_desc->bInterfaceNumber; urudev->interface = iface_desc->bInterfaceNumber;
AES_set_encrypt_key(crkey, 128, &urudev->aeskey); AES_set_encrypt_key(crkey, 128, &urudev->aeskey);
dev->priv = urudev; dev->priv = urudev;
fpi_imgdev_init_complete(dev, 0); fpi_imgdev_open_complete(dev, 0);
return 0; return 0;
} }
@ -1105,7 +1105,7 @@ static void dev_deinit(struct fp_img_dev *dev)
struct uru4k_dev *urudev = dev->priv; struct uru4k_dev *urudev = dev->priv;
libusb_release_interface(dev->udev, urudev->interface); libusb_release_interface(dev->udev, urudev->interface);
g_free(urudev); g_free(urudev);
fpi_imgdev_deinit_complete(dev); fpi_imgdev_close_complete(dev);
} }
static const struct usb_id id_table[] = { static const struct usb_id id_table[] = {
@ -1142,8 +1142,8 @@ struct fp_img_driver uru4000_driver = {
.img_height = 289, .img_height = 289,
.img_width = 384, .img_width = 384,
.init = dev_init, .open = dev_init,
.deinit = dev_deinit, .close = dev_deinit,
.activate = dev_activate, .activate = dev_activate,
.deactivate = dev_deactivate, .deactivate = dev_deactivate,
.change_state = dev_change_state, .change_state = dev_change_state,

View file

@ -19,277 +19,11 @@
#define FP_COMPONENT "drv" #define FP_COMPONENT "drv"
#include <config.h>
#include <errno.h> #include <errno.h>
#include "fp_internal.h" #include "fp_internal.h"
/* Lib-driver: start device initialisation */
int fpi_drv_init(struct fp_dev *dev, unsigned long driver_data)
{
struct fp_driver *drv = dev->drv;
if (!drv->init) {
fpi_drvcb_init_complete(dev, 0);
return 0;
}
dev->state = DEV_STATE_INITIALIZING;
return drv->init(dev, driver_data);
}
/* Driver-lib: device initialisation complete */
void fpi_drvcb_init_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;
}
/* Lib-driver: start device deinitialisation */
void fpi_drv_deinit(struct fp_dev *dev)
{
struct fp_driver *drv = dev->drv;
if (!drv->deinit) {
fpi_drvcb_deinit_complete(dev);
return;
}
dev->state = DEV_STATE_DEINITIALIZING;
drv->deinit(dev);
}
/* Driver-lib: device deinitialisation complete */
void fpi_drvcb_deinit_complete(struct fp_dev *dev)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
}
/* Lib-driver: start enrollment */
int fpi_drv_enroll_start(struct fp_dev *dev, fp_enroll_stage_cb callback)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
if (!drv->enroll_start)
return -ENOTSUP;
dev->state = DEV_STATE_ENROLL_STARTING;
dev->enroll_cb = callback;
r = drv->enroll_start(dev);
if (r < 0) {
dev->enroll_cb = NULL;
dev->state = DEV_STATE_ERROR;
}
return r;
}
/* Driver-lib: enrollment has now started, expect results soon */
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
{
fp_dbg("status %d", status);
BUG_ON(dev->state != DEV_STATE_ENROLL_STARTING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_ENROLLING;
}
/* Driver-lib: 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_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_cb(dev, result, data, img);
}
/* Lib-driver: stop enrollment */
int fpi_drv_enroll_stop(struct fp_dev *dev)
{
struct fp_driver *drv = dev->drv;
fp_dbg("");
dev->enroll_cb = NULL;
if (!drv->enroll_start)
return -ENOTSUP;
if (!drv->enroll_stop) {
dev->state = DEV_STATE_INITIALIZED;
return 0;
}
dev->state = DEV_STATE_ENROLL_STOPPING;
return drv->enroll_stop(dev);
}
/* Driver-lib: enrollment has stopped */
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
}
/* Lib-driver: start verification */
int fpi_drv_verify_start(struct fp_dev *dev, fp_verify_cb callback,
struct fp_print_data *data)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
if (!drv->verify_start)
return -ENOTSUP;
dev->state = DEV_STATE_VERIFY_STARTING;
dev->verify_cb = callback;
dev->verify_data = data;
r = drv->verify_start(dev);
if (r < 0) {
dev->verify_cb = NULL;
dev->state = DEV_STATE_ERROR;
}
return r;
}
/* Driver-lib: verification has started, expect results soon */
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_VERIFYING;
}
/* Driver-lib: 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) {
fp_dbg("ignoring verify result as no callback is subscribed");
return;
}
dev->verify_cb(dev, result, img);
}
/* Lib-driver: stop verification */
int fpi_drv_verify_stop(struct fp_dev *dev)
{
struct fp_driver *drv = dev->drv;
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_VERIFYING
&& dev->state != DEV_STATE_VERIFY_DONE);
dev->verify_cb = NULL;
if (!drv->verify_start)
return -ENOTSUP;
if (!drv->verify_stop) {
dev->state = DEV_STATE_INITIALIZED;
return 0;
}
dev->state = DEV_STATE_VERIFY_STOPPING;
return drv->verify_stop(dev, iterating);
}
/* Driver-lib: verification has stopped */
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
}
/* Lib-driver: start identification */
int fpi_drv_identify_start(struct fp_dev *dev, fp_identify_cb callback,
struct fp_print_data **gallery)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
if (!drv->identify_start)
return -ENOTSUP;
dev->state = DEV_STATE_IDENTIFY_STARTING;
dev->identify_cb = callback;
dev->identify_data = gallery;
r = drv->identify_start(dev);
if (r < 0) {
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("");
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STARTING);
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_IDENTIFYING;
}
/* Driver-lib: report a verify 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) {
fp_dbg("ignoring verify result as no callback is subscribed");
return;
}
dev->identify_cb(dev, result, match_offset, img);
}
/* Lib-driver: stop identification */
int fpi_drv_identify_stop(struct fp_dev *dev)
{
struct fp_driver *drv = dev->drv;
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_IDENTIFY_DONE);
dev->identify_cb = NULL;
if (!drv->identify_start)
return -ENOTSUP;
if (!drv->identify_stop) {
dev->state = DEV_STATE_INITIALIZED;
return 0;
}
dev->state = DEV_STATE_IDENTIFY_STOPPING;
return drv->identify_stop(dev, iterating);
}
/* Driver-lib: identification has stopped */
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
}
/* SSM: sequential state machine /* SSM: sequential state machine
* Asynchronous driver design encourages some kind of state machine behind it. * 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 * In most cases, the state machine is entirely linear - you only go to the

View file

@ -91,15 +91,6 @@ enum fp_dev_state {
DEV_STATE_IDENTIFY_STOPPING, DEV_STATE_IDENTIFY_STOPPING,
}; };
typedef void (*fp_enroll_stage_cb)(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img);
typedef void (*fp_verify_cb)(struct fp_dev *dev, int result,
struct fp_img *img);
typedef void (*fp_identify_cb)(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img);
struct fp_dev { struct fp_dev {
struct fp_driver *drv; struct fp_driver *drv;
libusb_dev_handle *udev; libusb_dev_handle *udev;
@ -114,15 +105,29 @@ struct fp_dev {
/* drivers should not mess with any of the below */ /* drivers should not mess with any of the below */
enum fp_dev_state state; enum fp_dev_state state;
/* FIXME: convert this to generic state operational data mechanism? */
int __enroll_stage; int __enroll_stage;
fp_enroll_stage_cb enroll_cb;
void *enroll_data; /* async I/O callbacks and data */
void *sync_verify_data; /* FIXME: convert this to generic state operational data mechanism? */
fp_dev_open_cb open_cb;
void *open_cb_data;
fp_dev_close_cb close_cb;
void *close_cb_data;
fp_enroll_stage_cb enroll_stage_cb;
void *enroll_stage_cb_data;
fp_enroll_stop_cb enroll_stop_cb;
void *enroll_stop_cb_data;
fp_verify_cb verify_cb; fp_verify_cb verify_cb;
void *identify_data; void *verify_cb_data;
void *sync_identify_data; fp_verify_stop_cb verify_stop_cb;
void *verify_stop_cb_data;
fp_identify_cb identify_cb; fp_identify_cb identify_cb;
void *identify_cb_data;
fp_identify_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
}; };
enum fp_imgdev_state { enum fp_imgdev_state {
@ -197,8 +202,8 @@ struct fp_driver {
/* Device operations */ /* Device operations */
int (*discover)(const struct usb_id *usb_id, uint32_t *devtype); int (*discover)(const struct usb_id *usb_id, uint32_t *devtype);
int (*init)(struct fp_dev *dev, unsigned long driver_data); int (*open)(struct fp_dev *dev, unsigned long driver_data);
void (*deinit)(struct fp_dev *dev); void (*close)(struct fp_dev *dev);
int (*enroll_start)(struct fp_dev *dev); int (*enroll_start)(struct fp_dev *dev);
int (*enroll_stop)(struct fp_dev *dev); int (*enroll_stop)(struct fp_dev *dev);
int (*verify_start)(struct fp_dev *dev); int (*verify_start)(struct fp_dev *dev);
@ -221,8 +226,8 @@ struct fp_img_driver {
int bz3_threshold; int bz3_threshold;
/* Device operations */ /* Device operations */
int (*init)(struct fp_img_dev *dev, unsigned long driver_data); int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
void (*deinit)(struct fp_img_dev *dev); void (*close)(struct fp_img_dev *dev);
int (*activate)(struct fp_img_dev *dev, enum fp_imgdev_state state); int (*activate)(struct fp_img_dev *dev, enum fp_imgdev_state state);
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state); int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
void (*deactivate)(struct fp_img_dev *dev); void (*deactivate)(struct fp_img_dev *dev);
@ -236,6 +241,8 @@ extern struct fp_img_driver aes2501_driver;
extern struct fp_img_driver aes4000_driver; extern struct fp_img_driver aes4000_driver;
extern struct fp_img_driver fdu2000_driver; extern struct fp_img_driver fdu2000_driver;
extern GSList *opened_devices;
void fpi_img_driver_setup(struct fp_img_driver *idriver); void fpi_img_driver_setup(struct fp_img_driver *idriver);
#define fpi_driver_to_img_driver(drv) \ #define fpi_driver_to_img_driver(drv) \
@ -363,37 +370,27 @@ void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state);
void fpi_ssm_mark_completed(struct fpi_ssm *machine); void fpi_ssm_mark_completed(struct fpi_ssm *machine);
void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error); void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error);
int fpi_drv_init(struct fp_dev *dev, unsigned long driver_data); void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
void fpi_drvcb_init_complete(struct fp_dev *dev, int status); void fpi_drvcb_close_complete(struct fp_dev *dev);
void fpi_drv_deinit(struct fp_dev *dev);
void fpi_drvcb_deinit_complete(struct fp_dev *dev);
int fpi_drv_enroll_start(struct fp_dev *dev, fp_enroll_stage_cb callback);
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status); void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result, void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img); struct fp_print_data *data, struct fp_img *img);
int fpi_drv_enroll_stop(struct fp_dev *dev);
void fpi_drvcb_enroll_stopped(struct fp_dev *dev); void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
int fpi_drv_verify_start(struct fp_dev *dev, fp_verify_cb callback,
struct fp_print_data *data);
void fpi_drvcb_verify_started(struct fp_dev *dev, int status); void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result, void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
struct fp_img *img); struct fp_img *img);
int fpi_drv_verify_stop(struct fp_dev *dev);
void fpi_drvcb_verify_stopped(struct fp_dev *dev); void fpi_drvcb_verify_stopped(struct fp_dev *dev);
int fpi_drv_identify_start(struct fp_dev *dev, fp_identify_cb callback,
struct fp_print_data **gallery);
void fpi_drvcb_identify_started(struct fp_dev *dev, int status); void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result, void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img); size_t match_offset, struct fp_img *img);
int fpi_drv_identify_stop(struct fp_dev *dev);
void fpi_drvcb_identify_stopped(struct fp_dev *dev); void fpi_drvcb_identify_stopped(struct fp_dev *dev);
/* for image drivers */ /* for image drivers */
void fpi_imgdev_init_complete(struct fp_img_dev *imgdev, int status); void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_deinit_complete(struct fp_img_dev *imgdev); 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_activate_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev); void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,

View file

@ -274,5 +274,42 @@ int fp_handle_events(void);
int fp_init(void); int fp_init(void);
void fp_exit(void); void fp_exit(void);
/* Asynchronous I/O */
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);
typedef void (*fp_dev_close_cb)(struct fp_dev *dev, void *user_data);
void fp_async_dev_close(struct fp_dev *dev, fp_dev_close_cb callback,
void *user_data);
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);
typedef void (*fp_enroll_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_enroll_stop(struct fp_dev *dev, fp_enroll_stop_cb callback,
void *user_data);
typedef void (*fp_verify_cb)(struct fp_dev *dev, int result,
struct fp_img *img, void *user_data);
int fp_async_verify_start(struct fp_dev *dev, struct fp_print_data *data,
fp_verify_cb callback, void *user_data);
typedef void (*fp_verify_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_verify_stop(struct fp_dev *dev, fp_verify_stop_cb callback,
void *user_data);
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);
typedef void (*fp_identify_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_identify_stop(struct fp_dev *dev, fp_identify_stop_cb callback,
void *user_data);
#endif #endif

View file

@ -27,7 +27,7 @@
#define MIN_ACCEPTABLE_MINUTIAE 10 #define MIN_ACCEPTABLE_MINUTIAE 10
#define BOZORTH3_DEFAULT_THRESHOLD 40 #define BOZORTH3_DEFAULT_THRESHOLD 40
static int img_dev_init(struct fp_dev *dev, unsigned long driver_data) 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_dev *imgdev = g_malloc0(sizeof(*imgdev));
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv); struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
@ -40,12 +40,12 @@ static int img_dev_init(struct fp_dev *dev, unsigned long driver_data)
/* for consistency in driver code, allow udev access through imgdev */ /* for consistency in driver code, allow udev access through imgdev */
imgdev->udev = dev->udev; imgdev->udev = dev->udev;
if (imgdrv->init) { if (imgdrv->open) {
r = imgdrv->init(imgdev, driver_data); r = imgdrv->open(imgdev, driver_data);
if (r) if (r)
goto err; goto err;
} else { } else {
fpi_drvcb_init_complete(dev, 0); fpi_drvcb_open_complete(dev, 0);
} }
return 0; return 0;
@ -54,25 +54,25 @@ err:
return r; return r;
} }
void fpi_imgdev_init_complete(struct fp_img_dev *imgdev, int status) void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status)
{ {
fpi_drvcb_init_complete(imgdev->dev, status); fpi_drvcb_open_complete(imgdev->dev, status);
} }
static void img_dev_deinit(struct fp_dev *dev) static void img_dev_close(struct fp_dev *dev)
{ {
struct fp_img_dev *imgdev = dev->priv; struct fp_img_dev *imgdev = dev->priv;
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv); struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(dev->drv);
if (imgdrv->deinit) if (imgdrv->close)
imgdrv->deinit(imgdev); imgdrv->close(imgdev);
else else
fpi_drvcb_deinit_complete(dev); fpi_drvcb_close_complete(dev);
} }
void fpi_imgdev_deinit_complete(struct fp_img_dev *imgdev) void fpi_imgdev_close_complete(struct fp_img_dev *imgdev)
{ {
fpi_drvcb_deinit_complete(imgdev->dev); fpi_drvcb_close_complete(imgdev->dev);
g_free(imgdev); g_free(imgdev);
} }
@ -173,6 +173,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
gboolean present) gboolean present)
{ {
int r = imgdev->action_result; 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"); fp_dbg(present ? "finger on sensor" : "finger removed");
if (present && imgdev->action_state == IMG_ACQUIRE_STATE_AWAIT_FINGER_ON) { if (present && imgdev->action_state == IMG_ACQUIRE_STATE_AWAIT_FINGER_ON) {
@ -185,25 +188,29 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
return; 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) { switch (imgdev->action) {
case IMG_ACTION_ENROLL: case IMG_ACTION_ENROLL:
fpi_drvcb_enroll_stage_completed(imgdev->dev, r, imgdev->acquire_data, fp_dbg("reporting enroll result");
imgdev->acquire_img); fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img);
break; break;
case IMG_ACTION_VERIFY: case IMG_ACTION_VERIFY:
fpi_drvcb_report_verify_result(imgdev->dev, r, imgdev->acquire_img); fpi_drvcb_report_verify_result(imgdev->dev, r, img);
fp_print_data_free(imgdev->acquire_data); fp_print_data_free(data);
break; break;
case IMG_ACTION_IDENTIFY: case IMG_ACTION_IDENTIFY:
fpi_drvcb_report_identify_result(imgdev->dev, r, fpi_drvcb_report_identify_result(imgdev->dev, r,
imgdev->identify_match_offset, imgdev->acquire_img); imgdev->identify_match_offset, img);
fp_print_data_free(imgdev->acquire_data); fp_print_data_free(data);
default: default:
fp_err("unhandled action %d", imgdev->action); fp_err("unhandled action %d", imgdev->action);
break; break;
} }
imgdev->acquire_img = NULL;
imgdev->acquire_data = NULL;
} }
static void verify_process_img(struct fp_img_dev *imgdev) static void verify_process_img(struct fp_img_dev *imgdev)
@ -237,7 +244,7 @@ static void identify_process_img(struct fp_img_dev *imgdev)
match_score = BOZORTH3_DEFAULT_THRESHOLD; match_score = BOZORTH3_DEFAULT_THRESHOLD;
r = fpi_img_compare_print_data_to_gallery(imgdev->acquire_data, r = fpi_img_compare_print_data_to_gallery(imgdev->acquire_data,
imgdev->dev->identify_data, match_score, &match_offset); imgdev->dev->identify_gallery, match_score, &match_offset);
imgdev->action_result = r; imgdev->action_result = r;
imgdev->identify_match_offset = match_offset; imgdev->identify_match_offset = match_offset;
@ -486,8 +493,8 @@ static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
void fpi_img_driver_setup(struct fp_img_driver *idriver) void fpi_img_driver_setup(struct fp_img_driver *idriver)
{ {
idriver->driver.type = DRIVER_IMAGING; idriver->driver.type = DRIVER_IMAGING;
idriver->driver.init = img_dev_init; idriver->driver.open = img_dev_open;
idriver->driver.deinit = img_dev_deinit; idriver->driver.close = img_dev_close;
idriver->driver.enroll_start = img_dev_enroll_start; idriver->driver.enroll_start = img_dev_enroll_start;
idriver->driver.enroll_stop = img_dev_enroll_stop; idriver->driver.enroll_stop = img_dev_enroll_stop;
idriver->driver.verify_start = img_dev_verify_start; idriver->driver.verify_start = img_dev_verify_start;

512
libfprint/sync.c Normal file
View file

@ -0,0 +1,512 @@
/*
* 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 <config.h>
#include <errno.h>
#include "fp_internal.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;
}
/** \ingroup dev
* Opens and initialises a device. This is the function you call in order
* to convert a \ref dscv_dev "discovered device" into an actual device handle
* that you can perform operations with.
* \param ddev the discovered device to open
* \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;
fp_dbg("");
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)
{
fp_dbg("");
gboolean *closed = user_data;
*closed = TRUE;
}
/** \ingroup dev
* Close a device. You must call this function when you are finished using
* a fingerprint device.
* \param dev the device to close. If NULL, function simply returns.
*/
API_EXPORTED void fp_dev_close(struct fp_dev *dev)
{
gboolean closed = FALSE;
if (!dev)
return;
fp_dbg("");
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;
fp_dbg("");
*stopped = TRUE;
}
/** \ingroup dev
* Performs an enroll stage. See \ref 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 succesful in that the
* user did not position their finger correctly or similar. When a RETRY code
* is returned, the enrollment stage is <b>not</b> 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_result#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_result#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_result#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.
*
* \param dev the device
* \param print_data a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
* \param 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.
* \return 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;
int r;
fp_dbg("");
/* 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;
}
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;
}
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;
fp_dbg("");
*stopped = TRUE;
}
/** \ingroup dev
* Performs a new scan and verify 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.
*
* \param dev the device to perform the scan.
* \param enrolled_print the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
* \param 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.
* \return 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 fp_driver *drv = dev->drv;
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", 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;
}
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;
fp_dbg("");
*stopped = TRUE;
}
/** \ingroup dev
* 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_result#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.
*
* \param dev the device to perform the scan.
* \param 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.
* \param 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.
* \param 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.
* \return 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)
{
struct fp_driver *drv = dev->drv;
gboolean stopped = FALSE;
struct sync_identify_data *idata
= g_malloc0(sizeof(struct sync_identify_data));
int r;
fp_dbg("to be handled by %s", 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);
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;
}