diff --git a/libfprint/core.c b/libfprint/core.c index 1ac21d5..fdaa519 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -1,6 +1,6 @@ /* * Core functions for libfprint - * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2007-2008 Daniel Drake * * This library is free software; you can redistribute it and/or * 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[] = { - /* &upekts_driver, */ }; 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; int r; + fp_dbg(""); libusb_dev_handle *udevh = libusb_open(ddev->udev); if (!udevh) { 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->udev = udevh; dev->__enroll_stage = -1; + dev->state = DEV_STATE_INITIALIZING; - if (drv->init) { - r = drv->init(dev, ddev->driver_data); - if (r) { - fp_err("device initialisation failed, driver=%s", drv->name); - libusb_close(udevh); - g_free(dev); - return NULL; - } + r = fpi_drv_init(dev, ddev->driver_data); + if (r) { + fp_err("device initialisation failed, driver=%s", drv->name); + goto err; } - 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); 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 */ static void do_close(struct fp_dev *dev) { - if (dev->drv->exit) - dev->drv->exit(dev); + fpi_drv_deinit(dev); + while (dev->state == DEV_STATE_DEINITIALIZING) + if (libusb_poll() < 0) + break; + libusb_close(dev->udev); 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) { - return dev->drv->identify != NULL; + return dev->drv->identify_start != NULL; } /** \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); } +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. @@ -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_driver *drv = dev->drv; - struct fp_img *_img = NULL; - int ret; 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", drv->name); return -ENOTSUP; } if (stage == -1) { - initial = TRUE; - dev->__enroll_stage = ++stage; - } + 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 = 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 " "driver %s (%d stages)", drv->name, dev->nr_enroll_stages); 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, - dev->nr_enroll_stages - 1, initial ? " (initial)" : ""); + fp_dbg("%s will handle enroll stage %d/%d", drv->name, stage, + dev->nr_enroll_stages - 1); - ret = drv->enroll(dev, initial, stage, print_data, &_img); - if (ret < 0) { - fp_err("enroll failed with code %d", ret); - dev->__enroll_stage = -1; - return ret; + edata = dev->enroll_data; + while (!edata->populated) { + r = libusb_poll(); + if (r < 0) { + g_free(edata); + goto err; + } } + edata->populated = FALSE; + if (img) - *img = _img; + *img = edata->img; else - fp_img_free(_img); + fp_img_free(edata->img); - switch (ret) { + r = edata->result; + switch (r) { case FP_ENROLL_PASS: fp_dbg("enroll stage passed"); dev->__enroll_stage = stage + 1; @@ -926,6 +981,8 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev, 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"); @@ -942,13 +999,49 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev, case FP_ENROLL_FAIL: fp_err("enroll failed"); dev->__enroll_stage = -1; + final = TRUE; break; default: - fp_err("unrecognised return code %d", ret); + fp_err("unrecognised return code %d", r); 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 @@ -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_driver *drv = dev->drv; - struct fp_img *_img = NULL; + struct sync_verify_data *vdata; int r; if (!enrolled_print) { @@ -978,9 +1071,9 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev, return -EINVAL; } - if (!drv->verify) { + if (!drv->verify_start) { fp_err("driver %s has no verify func", drv->name); - return -EINVAL; + return -ENOTSUP; } 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); - r = drv->verify(dev, enrolled_print, &_img); + r = fpi_drv_verify_start(dev, sync_verify_cb, enrolled_print); if (r < 0) { - fp_dbg("verify error %d", r); + fp_dbg("verify_start error %d", 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) - *img = _img; + *img = vdata->img; else - fp_img_free(_img); + 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"); @@ -1021,12 +1136,38 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev, break; default: 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; } +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. @@ -1063,31 +1204,54 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev, struct fp_img **img) { struct fp_driver *drv = dev->drv; - struct fp_img *_img; + struct sync_identify_data *idata; int r; - if (!drv->identify) { + 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 = drv->identify(dev, print_gallery, match_offset, &_img); + + r = fpi_drv_identify_start(dev, sync_identify_cb, print_gallery); if (r < 0) { - fp_dbg("identify error %d", r); + fp_err("identify_start error %d", 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) - *img = _img; + *img = idata->img; else - fp_img_free(_img); + 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"); @@ -1103,7 +1267,16 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev, break; default: 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; diff --git a/libfprint/drv.c b/libfprint/drv.c new file mode 100644 index 0000000..e45ce78 --- /dev/null +++ b/libfprint/drv.c @@ -0,0 +1,395 @@ +/* + * Functions to assist with asynchronous driver <---> library communications + * Copyright (C) 2007-2008 Daniel Drake + * + * 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 + +#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); + } +} + diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index b30c15b..63d39cd 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -1,6 +1,6 @@ /* * Internal/private definitions for libfprint - * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2007-2008 Daniel Drake * * This library is free software; you can redistribute it and/or * 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_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_driver *drv; libusb_dev_handle *udev; @@ -70,8 +106,21 @@ struct fp_dev { 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; + 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 { @@ -108,13 +157,13 @@ struct fp_driver { /* Device operations */ int (*discover)(const struct usb_id *usb_id, uint32_t *devtype); int (*init)(struct fp_dev *dev, unsigned long driver_data); - void (*exit)(struct fp_dev *dev); - int (*enroll)(struct fp_dev *dev, gboolean initial, int stage, - struct fp_print_data **print_data, struct fp_img **img); - int (*verify)(struct fp_dev *dev, struct fp_print_data *data, - struct fp_img **img); - int (*identify)(struct fp_dev *dev, struct fp_print_data **print_gallery, - size_t *match_offset, struct fp_img **img); + void (*deinit)(struct fp_dev *dev); + int (*enroll_start)(struct fp_dev *dev); + int (*enroll_stop)(struct fp_dev *dev); + int (*verify_start)(struct fp_dev *dev); + int (*verify_stop)(struct fp_dev *dev, gboolean iterating); + int (*identify_start)(struct fp_dev *dev); + int (*identify_stop)(struct fp_dev *dev, gboolean iterating); }; 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, 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 diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index 8deb23c..a511a19 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -349,9 +349,9 @@ void fpi_img_driver_setup(struct fp_img_driver *idriver) { idriver->driver.type = DRIVER_IMAGING; idriver->driver.init = img_dev_init; - idriver->driver.exit = img_dev_exit; - idriver->driver.enroll = img_dev_enroll; - idriver->driver.verify = img_dev_verify; - idriver->driver.identify = img_dev_identify; + //idriver->driver.exit = img_dev_exit; + //idriver->driver.enroll = img_dev_enroll; + //idriver->driver.verify = img_dev_verify; + //idriver->driver.identify = img_dev_identify; }