mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2025-11-15 07:38:12 +00:00
Public async API implementation
Involved some internal overhaul/reorganisation. Nice side effect is that the synchronous API is now expressed purely in terms of the public async API.
This commit is contained in:
545
libfprint/core.c
545
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 <b>not</b> advanced, so the next call
|
||||
* into this function will retry the current stage again. The current stage may
|
||||
* need to be retried several times.
|
||||
*
|
||||
* The fp_enroll_result#FP_ENROLL_FAIL code may be returned from any enroll
|
||||
* stage. This code indicates that even though the scans themselves have been
|
||||
* acceptable, data processing applied to these scans produces incomprehensible
|
||||
* results. In other words, the user may have been scanning a different finger
|
||||
* for each stage or something like that. Like negative error codes, this
|
||||
* return code indicates that the enrollment process has been aborted.
|
||||
*
|
||||
* The fp_enroll_result#FP_ENROLL_PASS code will only ever be returned for
|
||||
* non-final stages. This return code indicates that the scan was acceptable
|
||||
* and the next call into this function will advance onto the next enroll
|
||||
* stage.
|
||||
*
|
||||
* The fp_enroll_result#FP_ENROLL_COMPLETE code will only ever be returned
|
||||
* from the final enroll stage. It indicates that enrollment completed
|
||||
* successfully, and that print_data has been assigned to point to the
|
||||
* resultant enrollment data. The print_data parameter will not be modified
|
||||
* during any other enrollment stages, hence it is actually legal to pass NULL
|
||||
* as this argument for all but the final stage.
|
||||
*
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when the enroll fails with a RETRY or FAIL code. It is legal
|
||||
* to call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* \param dev the device
|
||||
* \param print_data a location to return the resultant enrollment data from
|
||||
* the final stage. Must be freed with fp_print_data_free() after use.
|
||||
* \param img location to store the scan image. accepts NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use.
|
||||
* \return negative code on error, otherwise a code from #fp_enroll_result
|
||||
*/
|
||||
API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data **print_data, struct fp_img **img)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int stage = dev->__enroll_stage;
|
||||
gboolean final = FALSE;
|
||||
struct sync_enroll_data *edata;
|
||||
int r;
|
||||
|
||||
if (!dev->nr_enroll_stages || !drv->enroll_start) {
|
||||
fp_err("driver %s has 0 enroll stages or no enroll func",
|
||||
drv->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (stage == -1) {
|
||||
fp_dbg("starting enrollment");
|
||||
r = fpi_drv_enroll_start(dev, sync_enroll_cb);
|
||||
if (r < 0) {
|
||||
fp_err("failed to start enrollment");
|
||||
return r;
|
||||
}
|
||||
while (dev->state == DEV_STATE_ENROLL_STARTING) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (dev->state != DEV_STATE_ENROLLING) {
|
||||
r = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->__enroll_stage = ++stage;
|
||||
dev->enroll_data = g_malloc0(sizeof(struct sync_enroll_data));
|
||||
} else if (stage >= dev->nr_enroll_stages) {
|
||||
fp_err("exceeding number of enroll stages for device claimed by "
|
||||
"driver %s (%d stages)", drv->name, dev->nr_enroll_stages);
|
||||
dev->__enroll_stage = -1;
|
||||
r = -EINVAL;
|
||||
final = TRUE;
|
||||
goto out;
|
||||
}
|
||||
fp_dbg("%s will handle enroll stage %d/%d", drv->name, stage,
|
||||
dev->nr_enroll_stages - 1);
|
||||
|
||||
edata = dev->enroll_data;
|
||||
while (!edata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0) {
|
||||
g_free(edata);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
edata->populated = FALSE;
|
||||
|
||||
if (img)
|
||||
*img = edata->img;
|
||||
else
|
||||
fp_img_free(edata->img);
|
||||
|
||||
r = edata->result;
|
||||
switch (r) {
|
||||
case FP_ENROLL_PASS:
|
||||
fp_dbg("enroll stage passed");
|
||||
dev->__enroll_stage = stage + 1;
|
||||
break;
|
||||
case FP_ENROLL_COMPLETE:
|
||||
fp_dbg("enroll complete");
|
||||
dev->__enroll_stage = -1;
|
||||
*print_data = edata->data;
|
||||
final = TRUE;
|
||||
break;
|
||||
case FP_ENROLL_RETRY:
|
||||
fp_dbg("enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, enroll should retry");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
case FP_ENROLL_FAIL:
|
||||
fp_err("enroll failed");
|
||||
dev->__enroll_stage = -1;
|
||||
final = TRUE;
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
dev->__enroll_stage = -1;
|
||||
r = -EINVAL;
|
||||
final = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (final) {
|
||||
fp_dbg("ending enrollment");
|
||||
if (fpi_drv_enroll_stop(dev) == 0)
|
||||
while (dev->state == DEV_STATE_ENROLL_STOPPING) {
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
}
|
||||
g_free(dev->enroll_data);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
err:
|
||||
if (fpi_drv_enroll_stop(dev) == 0)
|
||||
while (dev->state == DEV_STATE_ENROLL_STOPPING)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
return r;
|
||||
}
|
||||
|
||||
struct sync_verify_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_verify_cb(struct fp_dev *dev, int result, struct fp_img *img)
|
||||
{
|
||||
struct sync_verify_data *vdata = dev->sync_verify_data;
|
||||
vdata->result = result;
|
||||
vdata->img = img;
|
||||
vdata->populated = TRUE;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Performs a new scan and verify it against a previously enrolled print.
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when the verify fails with a RETRY code. It is legal to
|
||||
* call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* \param dev the device to perform the scan.
|
||||
* \param enrolled_print the print to verify against. Must have been previously
|
||||
* enrolled with a device compatible to the device selected to perform the scan.
|
||||
* \param img location to store the scan image. accepts NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use.
|
||||
* \return negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data *enrolled_print, struct fp_img **img)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
struct sync_verify_data *vdata;
|
||||
int r;
|
||||
|
||||
if (!enrolled_print) {
|
||||
fp_err("no print given");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!drv->verify_start) {
|
||||
fp_err("driver %s has no verify func", drv->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!fp_dev_supports_print_data(dev, enrolled_print)) {
|
||||
fp_err("print is not compatible with device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fp_dbg("to be handled by %s", drv->name);
|
||||
r = fpi_drv_verify_start(dev, sync_verify_cb, enrolled_print);
|
||||
if (r < 0) {
|
||||
fp_dbg("verify_start error %d", r);
|
||||
return r;
|
||||
}
|
||||
while (dev->state == DEV_STATE_VERIFY_STARTING) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0)
|
||||
goto err;
|
||||
}
|
||||
if (dev->state != DEV_STATE_VERIFYING) {
|
||||
r = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->sync_verify_data = g_malloc0(sizeof(struct sync_verify_data));
|
||||
vdata = dev->sync_verify_data;
|
||||
|
||||
while (!vdata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0) {
|
||||
g_free(vdata);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (img)
|
||||
*img = vdata->img;
|
||||
else
|
||||
fp_img_free(vdata->img);
|
||||
|
||||
r = vdata->result;
|
||||
g_free(dev->sync_verify_data);
|
||||
switch (r) {
|
||||
case FP_VERIFY_NO_MATCH:
|
||||
fp_dbg("result: no match");
|
||||
break;
|
||||
case FP_VERIFY_MATCH:
|
||||
fp_dbg("result: match");
|
||||
break;
|
||||
case FP_VERIFY_RETRY:
|
||||
fp_dbg("verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
err:
|
||||
fp_dbg("ending verification");
|
||||
if (fpi_drv_verify_stop(dev) == 0) {
|
||||
while (dev->state == DEV_STATE_VERIFY_STOPPING) {
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
struct sync_identify_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
size_t match_offset;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_identify_cb(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img)
|
||||
{
|
||||
struct sync_identify_data *idata = dev->sync_identify_data;
|
||||
idata->result = result;
|
||||
idata->match_offset = match_offset;
|
||||
idata->img = img;
|
||||
idata->populated = TRUE;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Performs a new scan and attempts to identify the scanned finger against
|
||||
* a collection of previously enrolled fingerprints.
|
||||
* If the device is an imaging device, it can also return the image from
|
||||
* the scan, even when identification fails with a RETRY code. It is legal to
|
||||
* call this function even on non-imaging devices, just don't expect them to
|
||||
* provide images.
|
||||
*
|
||||
* This function returns codes from #fp_verify_result. The return code
|
||||
* fp_verify_result#FP_VERIFY_MATCH indicates that the scanned fingerprint
|
||||
* does appear in the print gallery, and the match_offset output parameter
|
||||
* will indicate the index into the print gallery array of the matched print.
|
||||
*
|
||||
* This function will not necessarily examine the whole print gallery, it
|
||||
* will return as soon as it finds a matching print.
|
||||
*
|
||||
* Not all devices support identification. -ENOTSUP will be returned when
|
||||
* this is the case.
|
||||
*
|
||||
* \param dev the device to perform the scan.
|
||||
* \param print_gallery NULL-terminated array of pointers to the prints to
|
||||
* identify against. Each one must have been previously enrolled with a device
|
||||
* compatible to the device selected to perform the scan.
|
||||
* \param match_offset output location to store the array index of the matched
|
||||
* gallery print (if any was found). Only valid if FP_VERIFY_MATCH was
|
||||
* returned.
|
||||
* \param img location to store the scan image. accepts NULL for no image
|
||||
* storage. If an image is returned, it must be freed with fp_img_free() after
|
||||
* use.
|
||||
* \return negative code on error, otherwise a code from #fp_verify_result
|
||||
*/
|
||||
API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
|
||||
struct fp_print_data **print_gallery, size_t *match_offset,
|
||||
struct fp_img **img)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
struct sync_identify_data *idata;
|
||||
int r;
|
||||
|
||||
if (!drv->identify_start) {
|
||||
fp_dbg("driver %s has no identify func", drv->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
fp_dbg("to be handled by %s", drv->name);
|
||||
|
||||
r = fpi_drv_identify_start(dev, sync_identify_cb, print_gallery);
|
||||
if (r < 0) {
|
||||
fp_err("identify_start error %d", r);
|
||||
return r;
|
||||
}
|
||||
while (dev->state == DEV_STATE_IDENTIFY_STARTING) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0)
|
||||
goto err;
|
||||
}
|
||||
if (dev->state != DEV_STATE_IDENTIFYING) {
|
||||
r = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev->sync_identify_data = g_malloc0(sizeof(struct sync_identify_data));
|
||||
idata = dev->sync_identify_data;
|
||||
|
||||
while (!idata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0) {
|
||||
g_free(idata);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (img)
|
||||
*img = idata->img;
|
||||
else
|
||||
fp_img_free(idata->img);
|
||||
|
||||
r = idata->result;
|
||||
switch (r) {
|
||||
case FP_VERIFY_NO_MATCH:
|
||||
fp_dbg("result: no match");
|
||||
break;
|
||||
case FP_VERIFY_MATCH:
|
||||
fp_dbg("result: match at offset %zd", match_offset);
|
||||
*match_offset = idata->match_offset;
|
||||
break;
|
||||
case FP_VERIFY_RETRY:
|
||||
fp_dbg("verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
fp_dbg("swipe was too short, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
fp_dbg("finger was not centered, verify should retry");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
fp_dbg("scan failed, remove finger and retry");
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
r = -EINVAL;
|
||||
}
|
||||
g_free(dev->sync_identify_data);
|
||||
|
||||
err:
|
||||
if (fpi_drv_identify_stop(dev) == 0) {
|
||||
while (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/** \ingroup core
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user