diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index 1bef246..b4e022c 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -54,12 +54,14 @@ libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS) $(CRYP libfprint_la_SOURCES = \ fp_internal.h \ + async.c \ core.c \ data.c \ drv.c \ img.c \ imgdev.c \ poll.c \ + sync.c \ aeslib.c \ aeslib.h \ $(DRIVER_SRC) \ diff --git a/libfprint/async.c b/libfprint/async.c new file mode 100644 index 0000000..00d9727 --- /dev/null +++ b/libfprint/async.c @@ -0,0 +1,414 @@ +/* + * Asynchronous I/O functionality + * Copyright (C) 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 + */ + +#define FP_COMPONENT "async" + +#include +#include +#include + +#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); +} + diff --git a/libfprint/core.c b/libfprint/core.c index cf46755..e1382fa 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -26,6 +26,8 @@ #include "fp_internal.h" +GSList *opened_devices = NULL; + /** * \mainpage libfprint API Reference * libfprint is an open source library to provide access to fingerprint @@ -273,7 +275,6 @@ */ static GSList *registered_drivers = NULL; -static GSList *opened_devices = NULL; void fpi_log(enum fpi_log_level level, const char *component, 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; } -/** \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 * Get the \ref drv "driver" for a fingerprint 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); } -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 not 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 * Initialise libfprint. This function must be called before you attempt to * use the library in any way. @@ -1310,14 +786,18 @@ API_EXPORTED int fp_init(void) */ API_EXPORTED void fp_exit(void) { - GSList *elem = opened_devices; fp_dbg(""); - if (elem != NULL) { - do { - fp_dbg("naughty app left a device open on exit!"); - do_close((struct fp_dev *) elem->data); - } while ((elem = g_slist_next(elem))); + if (opened_devices) { + GSList *copy = g_slist_copy(opened_devices); + GSList *elem = copy; + fp_dbg("naughty app left devices open on exit!"); + + do + fp_dev_close((struct fp_dev *) elem->data); + while ((elem = g_slist_next(elem))); + + g_slist_free(copy); g_slist_free(opened_devices); opened_devices = NULL; } @@ -1329,4 +809,3 @@ API_EXPORTED void fp_exit(void) libusb_exit(); } - diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c index 37439b5..f0ea95d 100644 --- a/libfprint/drivers/aes2501.c +++ b/libfprint/drivers/aes2501.c @@ -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)); - fpi_imgdev_init_complete(dev, 0); + fpi_imgdev_open_complete(dev, 0); return 0; } @@ -915,7 +915,7 @@ static void dev_deinit(struct fp_img_dev *dev) { g_free(dev->priv); libusb_release_interface(dev->udev, 0); - fpi_imgdev_deinit_complete(dev); + fpi_imgdev_close_complete(dev); } static const struct usb_id id_table[] = { @@ -934,8 +934,8 @@ struct fp_img_driver aes2501_driver = { .img_height = -1, .img_width = 192, - .init = dev_init, - .deinit = dev_deinit, + .open = dev_init, + .close = dev_deinit, .activate = dev_activate, .deactivate = dev_deactivate, }; diff --git a/libfprint/drivers/aes4000.c b/libfprint/drivers/aes4000.c index cc838e7..ff3b3c2 100644 --- a/libfprint/drivers/aes4000.c +++ b/libfprint/drivers/aes4000.c @@ -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)); if (r == 0) - fpi_imgdev_init_complete(dev, 0); + fpi_imgdev_open_complete(dev, 0); return r; } @@ -222,7 +222,7 @@ static void dev_deinit(struct fp_img_dev *dev) { g_free(dev->priv); libusb_release_interface(dev->udev, 0); - fpi_imgdev_deinit_complete(dev); + fpi_imgdev_close_complete(dev); } static const struct usb_id id_table[] = { @@ -245,8 +245,8 @@ struct fp_img_driver aes4000_driver = { /* temporarily lowered until image quality improves */ .bz3_threshold = 9, - .init = dev_init, - .deinit = dev_deinit, + .open = dev_init, + .close = dev_deinit, .activate = dev_activate, .deactivate = dev_deactivate, }; diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index c63f779..214d4fd 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -840,7 +840,7 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data) dev->priv = upekdev; dev->nr_enroll_stages = 3; - fpi_drvcb_init_complete(dev, 0); + fpi_drvcb_open_complete(dev, 0); return 0; } @@ -848,7 +848,7 @@ static void dev_exit(struct fp_dev *dev) { libusb_release_interface(dev->udev, 0); g_free(dev->priv); - fpi_drvcb_deinit_complete(dev); + fpi_drvcb_close_complete(dev); } static const unsigned char enroll_init[] = { @@ -1415,8 +1415,8 @@ struct fp_driver upekts_driver = { .name = FP_COMPONENT, .full_name = "UPEK TouchStrip", .id_table = id_table, - .init = dev_init, - .deinit = dev_exit, + .open = dev_init, + .close = dev_exit, .enroll_start = enroll_start, .enroll_stop = enroll_stop, .verify_start = verify_start, diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index e5ed85c..93b79b0 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -1096,7 +1096,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) urudev->interface = iface_desc->bInterfaceNumber; AES_set_encrypt_key(crkey, 128, &urudev->aeskey); dev->priv = urudev; - fpi_imgdev_init_complete(dev, 0); + fpi_imgdev_open_complete(dev, 0); return 0; } @@ -1105,7 +1105,7 @@ static void dev_deinit(struct fp_img_dev *dev) struct uru4k_dev *urudev = dev->priv; libusb_release_interface(dev->udev, urudev->interface); g_free(urudev); - fpi_imgdev_deinit_complete(dev); + fpi_imgdev_close_complete(dev); } static const struct usb_id id_table[] = { @@ -1142,8 +1142,8 @@ struct fp_img_driver uru4000_driver = { .img_height = 289, .img_width = 384, - .init = dev_init, - .deinit = dev_deinit, + .open = dev_init, + .close = dev_deinit, .activate = dev_activate, .deactivate = dev_deactivate, .change_state = dev_change_state, diff --git a/libfprint/drv.c b/libfprint/drv.c index 9ee54cf..a7ba846 100644 --- a/libfprint/drv.c +++ b/libfprint/drv.c @@ -19,277 +19,11 @@ #define FP_COMPONENT "drv" +#include #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; - 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 * 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 diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index e9676e8..6a365c1 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -91,15 +91,6 @@ enum fp_dev_state { 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; @@ -114,15 +105,29 @@ struct fp_dev { /* 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; + + /* async I/O callbacks and data */ + /* FIXME: convert this to generic state operational data mechanism? */ + fp_dev_open_cb open_cb; + void *open_cb_data; + fp_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; - void *identify_data; - void *sync_identify_data; + void *verify_cb_data; + fp_verify_stop_cb verify_stop_cb; + void *verify_stop_cb_data; 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 { @@ -197,8 +202,8 @@ 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 (*deinit)(struct fp_dev *dev); + int (*open)(struct fp_dev *dev, unsigned long driver_data); + void (*close)(struct fp_dev *dev); int (*enroll_start)(struct fp_dev *dev); int (*enroll_stop)(struct fp_dev *dev); int (*verify_start)(struct fp_dev *dev); @@ -221,8 +226,8 @@ struct fp_img_driver { int bz3_threshold; /* Device operations */ - int (*init)(struct fp_img_dev *dev, unsigned long driver_data); - void (*deinit)(struct fp_img_dev *dev); + int (*open)(struct fp_img_dev *dev, unsigned long driver_data); + void (*close)(struct fp_img_dev *dev); int (*activate)(struct fp_img_dev *dev, 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); @@ -236,6 +241,8 @@ extern struct fp_img_driver aes2501_driver; extern struct fp_img_driver aes4000_driver; extern struct fp_img_driver fdu2000_driver; +extern GSList *opened_devices; + void fpi_img_driver_setup(struct fp_img_driver *idriver); #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_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); +void fpi_drvcb_open_complete(struct fp_dev *dev, int status); +void fpi_drvcb_close_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); /* for image drivers */ -void fpi_imgdev_init_complete(struct fp_img_dev *imgdev, int status); -void fpi_imgdev_deinit_complete(struct fp_img_dev *imgdev); +void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status); +void fpi_imgdev_close_complete(struct fp_img_dev *imgdev); void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status); void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev); void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, diff --git a/libfprint/fprint.h b/libfprint/fprint.h index f762675..4cebba3 100644 --- a/libfprint/fprint.h +++ b/libfprint/fprint.h @@ -274,5 +274,42 @@ int fp_handle_events(void); int fp_init(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 diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index e3eb8c5..1fd8ee1 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -27,7 +27,7 @@ #define MIN_ACCEPTABLE_MINUTIAE 10 #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_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 */ imgdev->udev = dev->udev; - if (imgdrv->init) { - r = imgdrv->init(imgdev, driver_data); + if (imgdrv->open) { + r = imgdrv->open(imgdev, driver_data); if (r) goto err; } else { - fpi_drvcb_init_complete(dev, 0); + fpi_drvcb_open_complete(dev, 0); } return 0; @@ -54,25 +54,25 @@ err: 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_driver *imgdrv = fpi_driver_to_img_driver(dev->drv); - if (imgdrv->deinit) - imgdrv->deinit(imgdev); + if (imgdrv->close) + imgdrv->close(imgdev); 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); } @@ -173,6 +173,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, gboolean present) { int r = imgdev->action_result; + struct fp_print_data *data = imgdev->acquire_data; + struct fp_img *img = imgdev->acquire_img; + fp_dbg(present ? "finger on sensor" : "finger removed"); if (present && imgdev->action_state == IMG_ACQUIRE_STATE_AWAIT_FINGER_ON) { @@ -185,25 +188,29 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev, return; } + /* clear these before reporting results to avoid complications with + * call cascading in and out of the library */ + imgdev->acquire_img = NULL; + imgdev->acquire_data = NULL; + + /* finger removed, report results */ switch (imgdev->action) { case IMG_ACTION_ENROLL: - fpi_drvcb_enroll_stage_completed(imgdev->dev, r, imgdev->acquire_data, - imgdev->acquire_img); + fp_dbg("reporting enroll result"); + fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img); break; case IMG_ACTION_VERIFY: - fpi_drvcb_report_verify_result(imgdev->dev, r, imgdev->acquire_img); - fp_print_data_free(imgdev->acquire_data); + fpi_drvcb_report_verify_result(imgdev->dev, r, img); + fp_print_data_free(data); break; case IMG_ACTION_IDENTIFY: fpi_drvcb_report_identify_result(imgdev->dev, r, - imgdev->identify_match_offset, imgdev->acquire_img); - fp_print_data_free(imgdev->acquire_data); + imgdev->identify_match_offset, img); + fp_print_data_free(data); default: fp_err("unhandled action %d", imgdev->action); break; } - imgdev->acquire_img = NULL; - imgdev->acquire_data = NULL; } 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; 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->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) { idriver->driver.type = DRIVER_IMAGING; - idriver->driver.init = img_dev_init; - idriver->driver.deinit = img_dev_deinit; + idriver->driver.open = img_dev_open; + idriver->driver.close = img_dev_close; idriver->driver.enroll_start = img_dev_enroll_start; idriver->driver.enroll_stop = img_dev_enroll_stop; idriver->driver.verify_start = img_dev_verify_start; diff --git a/libfprint/sync.c b/libfprint/sync.c new file mode 100644 index 0000000..87c033a --- /dev/null +++ b/libfprint/sync.c @@ -0,0 +1,512 @@ +/* + * Synchronous I/O functionality + * 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 + */ + +#define FP_COMPONENT "sync" + +#include +#include + +#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 not 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; +} +