Port primitive driver layer to asynchronous model

This commit is contained in:
Daniel Drake 2008-02-04 10:23:11 +00:00
parent 69760547df
commit 5b1f6a0df7
4 changed files with 742 additions and 64 deletions

View file

@ -1,6 +1,6 @@
/* /*
* Core functions for libfprint * Core functions for libfprint
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -325,7 +325,6 @@ static void register_driver(struct fp_driver *drv)
} }
static struct fp_driver * const primitive_drivers[] = { static struct fp_driver * const primitive_drivers[] = {
/* &upekts_driver, */
}; };
static struct fp_img_driver * const img_drivers[] = { static struct fp_img_driver * const img_drivers[] = {
@ -578,6 +577,7 @@ API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
struct fp_driver *drv = ddev->drv; struct fp_driver *drv = ddev->drv;
int r; int r;
fp_dbg("");
libusb_dev_handle *udevh = libusb_open(ddev->udev); libusb_dev_handle *udevh = libusb_open(ddev->udev);
if (!udevh) { if (!udevh) {
fp_err("usb_open failed"); fp_err("usb_open failed");
@ -588,27 +588,43 @@ API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
dev->drv = drv; dev->drv = drv;
dev->udev = udevh; dev->udev = udevh;
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
if (drv->init) { r = fpi_drv_init(dev, ddev->driver_data);
r = drv->init(dev, ddev->driver_data); if (r) {
if (r) { fp_err("device initialisation failed, driver=%s", drv->name);
fp_err("device initialisation failed, driver=%s", drv->name); goto err;
libusb_close(udevh);
g_free(dev);
return NULL;
}
} }
fp_dbg(""); while (dev->state == DEV_STATE_INITIALIZING)
if (libusb_poll() < 0)
goto err_deinit;
if (dev->state != DEV_STATE_INITIALIZED)
goto err_deinit;
opened_devices = g_slist_prepend(opened_devices, (gpointer) dev); opened_devices = g_slist_prepend(opened_devices, (gpointer) dev);
return dev; return dev;
err_deinit:
fpi_drv_deinit(dev);
while (dev->state == DEV_STATE_DEINITIALIZING) {
if (libusb_poll() < 0)
break;
}
err:
libusb_close(udevh);
g_free(dev);
return NULL;
} }
/* performs close operation without modifying opened_devices list */ /* performs close operation without modifying opened_devices list */
static void do_close(struct fp_dev *dev) static void do_close(struct fp_dev *dev)
{ {
if (dev->drv->exit) fpi_drv_deinit(dev);
dev->drv->exit(dev); while (dev->state == DEV_STATE_DEINITIALIZING)
if (libusb_poll() < 0)
break;
libusb_close(dev->udev); libusb_close(dev->udev);
g_free(dev); g_free(dev);
} }
@ -751,7 +767,7 @@ API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
*/ */
API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev) API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
{ {
return dev->drv->identify != NULL; return dev->drv->identify_start != NULL;
} }
/** \ingroup dev /** \ingroup dev
@ -824,6 +840,23 @@ 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 /** \ingroup dev
* Performs an enroll stage. See \ref enrolling for an explanation of enroll * Performs an enroll stage. See \ref enrolling for an explanation of enroll
* stages. * stages.
@ -881,44 +914,66 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
struct fp_print_data **print_data, struct fp_img **img) struct fp_print_data **print_data, struct fp_img **img)
{ {
struct fp_driver *drv = dev->drv; struct fp_driver *drv = dev->drv;
struct fp_img *_img = NULL;
int ret;
int stage = dev->__enroll_stage; int stage = dev->__enroll_stage;
gboolean initial = FALSE; gboolean final = FALSE;
struct sync_enroll_data *edata;
int r;
if (!dev->nr_enroll_stages || !drv->enroll) { if (!dev->nr_enroll_stages || !drv->enroll_start) {
fp_err("driver %s has 0 enroll stages or no enroll func", fp_err("driver %s has 0 enroll stages or no enroll func",
drv->name); drv->name);
return -ENOTSUP; return -ENOTSUP;
} }
if (stage == -1) { if (stage == -1) {
initial = TRUE; fp_dbg("starting enrollment");
dev->__enroll_stage = ++stage; 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 = libusb_poll();
if (r < 0)
goto err;
}
if (stage >= dev->nr_enroll_stages) { 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 " fp_err("exceeding number of enroll stages for device claimed by "
"driver %s (%d stages)", drv->name, dev->nr_enroll_stages); "driver %s (%d stages)", drv->name, dev->nr_enroll_stages);
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
return -EINVAL; r = -EINVAL;
final = TRUE;
goto out;
} }
fp_dbg("%s will handle enroll stage %d/%d%s", drv->name, stage, fp_dbg("%s will handle enroll stage %d/%d", drv->name, stage,
dev->nr_enroll_stages - 1, initial ? " (initial)" : ""); dev->nr_enroll_stages - 1);
ret = drv->enroll(dev, initial, stage, print_data, &_img); edata = dev->enroll_data;
if (ret < 0) { while (!edata->populated) {
fp_err("enroll failed with code %d", ret); r = libusb_poll();
dev->__enroll_stage = -1; if (r < 0) {
return ret; g_free(edata);
goto err;
}
} }
edata->populated = FALSE;
if (img) if (img)
*img = _img; *img = edata->img;
else else
fp_img_free(_img); fp_img_free(edata->img);
switch (ret) { r = edata->result;
switch (r) {
case FP_ENROLL_PASS: case FP_ENROLL_PASS:
fp_dbg("enroll stage passed"); fp_dbg("enroll stage passed");
dev->__enroll_stage = stage + 1; dev->__enroll_stage = stage + 1;
@ -926,6 +981,8 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
case FP_ENROLL_COMPLETE: case FP_ENROLL_COMPLETE:
fp_dbg("enroll complete"); fp_dbg("enroll complete");
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
*print_data = edata->data;
final = TRUE;
break; break;
case FP_ENROLL_RETRY: case FP_ENROLL_RETRY:
fp_dbg("enroll should retry"); fp_dbg("enroll should retry");
@ -942,13 +999,49 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
case FP_ENROLL_FAIL: case FP_ENROLL_FAIL:
fp_err("enroll failed"); fp_err("enroll failed");
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
final = TRUE;
break; break;
default: default:
fp_err("unrecognised return code %d", ret); fp_err("unrecognised return code %d", r);
dev->__enroll_stage = -1; dev->__enroll_stage = -1;
return -EINVAL; r = -EINVAL;
final = TRUE;
break;
} }
return ret;
out:
if (final) {
fp_dbg("ending enrollment");
if (fpi_drv_enroll_stop(dev) == 0)
while (dev->state == DEV_STATE_ENROLL_STOPPING) {
if (libusb_poll() < 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 (libusb_poll() < 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 /** \ingroup dev
@ -970,7 +1063,7 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
struct fp_print_data *enrolled_print, struct fp_img **img) struct fp_print_data *enrolled_print, struct fp_img **img)
{ {
struct fp_driver *drv = dev->drv; struct fp_driver *drv = dev->drv;
struct fp_img *_img = NULL; struct sync_verify_data *vdata;
int r; int r;
if (!enrolled_print) { if (!enrolled_print) {
@ -978,9 +1071,9 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
return -EINVAL; return -EINVAL;
} }
if (!drv->verify) { if (!drv->verify_start) {
fp_err("driver %s has no verify func", drv->name); fp_err("driver %s has no verify func", drv->name);
return -EINVAL; return -ENOTSUP;
} }
if (!fp_dev_supports_print_data(dev, enrolled_print)) { if (!fp_dev_supports_print_data(dev, enrolled_print)) {
@ -989,17 +1082,39 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
} }
fp_dbg("to be handled by %s", drv->name); fp_dbg("to be handled by %s", drv->name);
r = drv->verify(dev, enrolled_print, &_img); r = fpi_drv_verify_start(dev, sync_verify_cb, enrolled_print);
if (r < 0) { if (r < 0) {
fp_dbg("verify error %d", r); fp_dbg("verify_start error %d", r);
return r; return r;
} }
while (dev->state == DEV_STATE_VERIFY_STARTING) {
r = libusb_poll();
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 = libusb_poll();
if (r < 0) {
g_free(vdata);
goto err;
}
}
if (img) if (img)
*img = _img; *img = vdata->img;
else else
fp_img_free(_img); fp_img_free(vdata->img);
r = vdata->result;
g_free(dev->sync_verify_data);
switch (r) { switch (r) {
case FP_VERIFY_NO_MATCH: case FP_VERIFY_NO_MATCH:
fp_dbg("result: no match"); fp_dbg("result: no match");
@ -1021,12 +1136,38 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
break; break;
default: default:
fp_err("unrecognised return code %d", r); fp_err("unrecognised return code %d", r);
return -EINVAL; r = -EINVAL;
}
err:
fp_dbg("ending verification");
if (fpi_drv_verify_stop(dev) == 0) {
while (dev->state == DEV_STATE_VERIFY_STOPPING) {
if (libusb_poll() < 0)
break;
}
} }
return r; 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 /** \ingroup dev
* Performs a new scan and attempts to identify the scanned finger against * Performs a new scan and attempts to identify the scanned finger against
* a collection of previously enrolled fingerprints. * a collection of previously enrolled fingerprints.
@ -1063,31 +1204,54 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
struct fp_img **img) struct fp_img **img)
{ {
struct fp_driver *drv = dev->drv; struct fp_driver *drv = dev->drv;
struct fp_img *_img; struct sync_identify_data *idata;
int r; int r;
if (!drv->identify) { if (!drv->identify_start) {
fp_dbg("driver %s has no identify func", drv->name); fp_dbg("driver %s has no identify func", drv->name);
return -ENOTSUP; return -ENOTSUP;
} }
fp_dbg("to be handled by %s", drv->name); fp_dbg("to be handled by %s", drv->name);
r = drv->identify(dev, print_gallery, match_offset, &_img);
r = fpi_drv_identify_start(dev, sync_identify_cb, print_gallery);
if (r < 0) { if (r < 0) {
fp_dbg("identify error %d", r); fp_err("identify_start error %d", r);
return r; return r;
} }
while (dev->state == DEV_STATE_IDENTIFY_STARTING) {
r = libusb_poll();
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 = libusb_poll();
if (r < 0) {
g_free(idata);
goto err;
}
}
if (img) if (img)
*img = _img; *img = idata->img;
else else
fp_img_free(_img); fp_img_free(idata->img);
r = idata->result;
switch (r) { switch (r) {
case FP_VERIFY_NO_MATCH: case FP_VERIFY_NO_MATCH:
fp_dbg("result: no match"); fp_dbg("result: no match");
break; break;
case FP_VERIFY_MATCH: case FP_VERIFY_MATCH:
fp_dbg("result: match at offset %zd", match_offset); fp_dbg("result: match at offset %zd", match_offset);
*match_offset = idata->match_offset;
break; break;
case FP_VERIFY_RETRY: case FP_VERIFY_RETRY:
fp_dbg("verify should retry"); fp_dbg("verify should retry");
@ -1103,7 +1267,16 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
break; break;
default: default:
fp_err("unrecognised return code %d", r); fp_err("unrecognised return code %d", r);
return -EINVAL; r = -EINVAL;
}
g_free(dev->sync_identify_data);
err:
if (fpi_drv_identify_stop(dev) == 0) {
while (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
if (libusb_poll() < 0)
break;
}
} }
return r; return r;

395
libfprint/drv.c Normal file
View file

@ -0,0 +1,395 @@
/*
* Functions to assist with asynchronous driver <---> library communications
* 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 "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;
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;
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_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);
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
* 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 SSM functions help you
* implement such a machine.
*
* e.g. S1 --> S2 --> S3 --> S4
* S1 is the start state
* There is also an implicit error state and an implicit accepting state
* (both with implicit edges from every state).
*
* To create a ssm, you pass a state handler function and the total number of
* states (4 in the above example).
*
* To start a ssm, you pass in a completion callback function which gets
* called when the ssm completes (both on error and on failure).
*
* To iterate to the next state, call fpi_ssm_next_state(). It is legal to
* attempt to iterate beyond the final state - this is equivalent to marking
* the ssm as successfully completed.
*
* To mark successful completion of a SSM, either iterate beyond the final
* state or call fpi_ssm_mark_completed() from any state.
*
* To mark failed completion of a SSM, call fpi_ssm_mark_aborted() from any
* state. You must pass a non-zero error code.
*
* Your state handling function looks at ssm->cur_state in order to determine
* the current state and hence which operations to perform (a switch statement
* is appropriate).
* Typically, the state handling function fires off an asynchronous libusb
* transfer, and the callback function iterates the machine to the next state
* upon success (or aborts the machine on transfer failure).
*
* Your completion callback should examine ssm->error in order to determine
* whether the ssm completed or failed. An error code of zero indicates
* successful completion.
*/
/* Allocate a new ssm */
struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states)
{
struct fpi_ssm *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;
return machine;
}
/* Free a ssm */
void fpi_ssm_free(struct fpi_ssm *machine)
{
if (!machine)
return;
g_free(machine);
}
/* Invoke the state handler */
static void __ssm_call_handler(struct fpi_ssm *machine)
{
machine->handler(machine);
}
/* Start a ssm. You can also restart a completed or aborted ssm. */
void fpi_ssm_start(struct fpi_ssm *ssm, ssm_completed_fn callback)
{
BUG_ON(!ssm->completed);
ssm->callback = callback;
ssm->cur_state = 0;
ssm->completed = FALSE;
ssm->error = 0;
__ssm_call_handler(ssm);
}
/* Mark a ssm as completed successfully. */
void fpi_ssm_mark_completed(struct fpi_ssm *machine)
{
BUG_ON(machine->completed);
machine->completed = TRUE;
if (machine->callback)
machine->callback(machine);
}
/* Mark a ssm as aborted with error. */
void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error)
{
fp_dbg("error %d", error);
BUG_ON(error == 0);
machine->error = error;
fpi_ssm_mark_completed(machine);
}
/* Iterate to next state of a ssm */
void fpi_ssm_next_state(struct fpi_ssm *machine)
{
BUG_ON(machine->completed);
machine->cur_state++;
if (machine->cur_state == machine->nr_states) {
fpi_ssm_mark_completed(machine);
} else {
__ssm_call_handler(machine);
}
}

View file

@ -1,6 +1,6 @@
/* /*
* Internal/private definitions for libfprint * Internal/private definitions for libfprint
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -62,6 +62,42 @@ void fpi_log(enum fpi_log_level, const char *component, const char *function,
#define fp_warn(fmt...) _fpi_log(LOG_LEVEL_WARNING, fmt) #define fp_warn(fmt...) _fpi_log(LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(LOG_LEVEL_ERROR, fmt) #define fp_err(fmt...) _fpi_log(LOG_LEVEL_ERROR, fmt)
#ifdef NDEBUG
#define BUG_ON(condition) \
if ((condition)) fp_err("BUG at %s:%d", __FILE__, __LINE__)
#else
#define BUG_ON(condition)
#endif
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,
};
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;
@ -70,8 +106,21 @@ struct fp_dev {
int nr_enroll_stages; int nr_enroll_stages;
/* drivers should not mess with these */ /* read-only to drivers */
struct fp_print_data *verify_data;
/* drivers should not mess with any of the below */
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;
void *sync_verify_data;
fp_verify_cb verify_cb;
void *identify_data;
void *sync_identify_data;
fp_identify_cb identify_cb;
}; };
struct fp_img_dev { struct fp_img_dev {
@ -108,13 +157,13 @@ 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 (*init)(struct fp_dev *dev, unsigned long driver_data);
void (*exit)(struct fp_dev *dev); void (*deinit)(struct fp_dev *dev);
int (*enroll)(struct fp_dev *dev, gboolean initial, int stage, int (*enroll_start)(struct fp_dev *dev);
struct fp_print_data **print_data, struct fp_img **img); int (*enroll_stop)(struct fp_dev *dev);
int (*verify)(struct fp_dev *dev, struct fp_print_data *data, int (*verify_start)(struct fp_dev *dev);
struct fp_img **img); int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
int (*identify)(struct fp_dev *dev, struct fp_print_data **print_gallery, int (*identify_start)(struct fp_dev *dev);
size_t *match_offset, struct fp_img **img); int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
}; };
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv); enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
@ -230,5 +279,66 @@ int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print, int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, int *match_offset); struct fp_print_data **gallery, int match_threshold, int *match_offset);
/* async drv <--> lib comms */
struct fpi_ssm;
typedef void (*ssm_completed_fn)(struct fpi_ssm *ssm);
typedef void (*ssm_handler_fn)(struct fpi_ssm *ssm);
/* sequential state machine: state machine that iterates sequentially over
* a predefined series of states. can be aborted by either completion or
* abortion error conditions. */
struct fpi_ssm {
struct fp_dev *dev;
void *priv;
int nr_states;
int cur_state;
gboolean completed;
int error;
ssm_completed_fn callback;
ssm_handler_fn handler;
};
/* for library and drivers */
struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states);
void fpi_ssm_free(struct fpi_ssm *machine);
void fpi_ssm_start(struct fpi_ssm *machine, ssm_completed_fn callback);
int fpi_ssm_has_completed(struct fpi_ssm *machine);
/* for drivers */
void fpi_ssm_next_state(struct fpi_ssm *machine);
void fpi_ssm_mark_completed(struct fpi_ssm *machine);
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_init_complete(struct fp_dev *dev, int status);
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_stage_completed(struct fp_dev *dev, int result,
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);
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_report_verify_result(struct fp_dev *dev, int result,
struct fp_img *img);
int fpi_drv_verify_stop(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_report_identify_result(struct fp_dev *dev, int result,
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);
#endif #endif

View file

@ -349,9 +349,9 @@ 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.init = img_dev_init;
idriver->driver.exit = img_dev_exit; //idriver->driver.exit = img_dev_exit;
idriver->driver.enroll = img_dev_enroll; //idriver->driver.enroll = img_dev_enroll;
idriver->driver.verify = img_dev_verify; //idriver->driver.verify = img_dev_verify;
idriver->driver.identify = img_dev_identify; //idriver->driver.identify = img_dev_identify;
} }