libfprint/libfprint/async.c
Bastien Nocera c91819f551 lib: Remove drv->close absence support in fp_async_dev_close()
The driver will at least need to close its hardware resources, and
free memory, so it must have had one.

This case was never actually used as can be seen from the fact that
we would assert in fpi_drvcb_close_complete() if the state was wrong
but never set it to the expected value.
2018-05-31 11:06:31 +02:00

630 lines
14 KiB
C

/*
* 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 <config.h>
#include <errno.h>
#include <glib.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);
}
/**
* fp_async_dev_open:
* @ddev:
* @callback:
* @user_data
*
* Returns:
*/
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);
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;
libusb_close(dev->udev);
if (dev->close_cb)
dev->close_cb(dev, dev->close_cb_data);
g_free(dev);
}
/**
* fp_async_dev_close:
* @dev:
* @callback:
* @user_data
*/
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:
* @callback:
* @user_data:
*
* Returns:
*/
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);
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:
* @callback:
* @user_data:
*
* Returns:
*/
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:
* @data:
* @callback:
* @user_data:
*
* Returns:
*/
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);
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:
* @callback:
* @user_data:
*
* Returns:
*/
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);
drv = dev->drv;
G_DEBUG_HERE();
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:
* @gallery:
* @callback:
* @user_data:
*
* Returns:
*/
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);
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:
* @callback:
* @user_data:
*
* Returns:
*/
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);
drv = dev->drv;
G_DEBUG_HERE();
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:
* @unconditional:
* @callback:
* @user_data:
*
* Returns:
*/
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);
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 verification, 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:
* @callback:
* @user_data:
*
* Returns:
*/
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 verification");
dev->capture_stop_cb = NULL;
}
return r;
}