libfprint/libfprint/fpi-sync.c
Vincent Huang ef3519854d lib: Add API to delete prints stored on sensors
Some new fingerprint devices store the information required for matching
on the sensor itself, without ever disclosing this data to the host. In
this case we need to delete prints not only from local storage but also
from the device storage.

This commits adds the internal and external API required to do so.
2019-06-12 17:48:39 +02:00

755 lines
21 KiB
C

/*
* Synchronous I/O functionality
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "sync"
#include "fp_internal.h"
#include "fpi-dev.h"
#include <config.h>
#include <errno.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;
}
/**
* fp_dev_open:
* @ddev: the struct #fp_dscv_dev discovered device to open
*
* Opens and initialises a device. This is the function you call in order
* to convert a #fp_dscv_dev discovered device into an actual device handle
* that you can perform operations with.
*
* 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;
G_DEBUG_HERE();
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)
{
G_DEBUG_HERE();
gboolean *closed = user_data;
*closed = TRUE;
}
/**
* fp_dev_close:
* @dev: the struct #fp_dev device to close. If %NULL, function simply returns
*
* Closes a device. You must call this function when you have finished using
* a fingerprint device.
*/
API_EXPORTED void fp_dev_close(struct fp_dev *dev)
{
gboolean closed = FALSE;
if (!dev)
return;
G_DEBUG_HERE();
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;
G_DEBUG_HERE();
*stopped = TRUE;
}
/**
* fp_enroll_finger_img:
* @dev: the struct #fp_dev device
* @print_data: a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use
* @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
*
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#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 <emphasis role="strong">not</emphasis> 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_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_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_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.
*
* Returns: 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 = NULL;
int r;
G_DEBUG_HERE();
/* 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;
}
if (!final)
return r;
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;
}
/**
* fp_enroll_finger:
* @dev: the struct #fp_dev device
* @print_data: a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use
*
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
* for an explanation of enroll stages. This function is just a shortcut to
* calling fp_enroll_finger_img() with a %NULL image parameter. Be sure to read
* the description of fp_enroll_finger_img() in order to understand its behaviour.
*
* Returns: negative code on error, otherwise a code from #fp_enroll_result
*/
API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
struct fp_print_data **print_data)
{
return fp_enroll_finger_img(dev, print_data, NULL);
}
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;
G_DEBUG_HERE();
*stopped = TRUE;
}
/**
* fp_verify_finger_img:
* @dev: the struct #fp_dev device to perform the scan on
* @enrolled_print: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan
* @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
* Performs a new scan and verifies 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.
*
* Returns: 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 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", dev->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;
}
/**
* fp_verify_finger:
* @dev: the struct #fp_dev device to perform the scan on
* @enrolled_print: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan
*
* Performs a new scan and verify it against a previously enrolled print. This
* function is just a shortcut to calling fp_verify_finger_img() with a NULL
* image output parameter.
*
* See also fp_verify_finger_img().
*
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print)
{
return fp_verify_finger_img(dev, enrolled_print, NULL);
}
struct sync_delete_data {
gboolean populated;
int result;
};
static void sync_delete_cb(struct fp_dev *dev, int result, void *user_data)
{
struct sync_delete_data *ddata = user_data;
ddata->result = result;
ddata->populated = TRUE;
}
/**
* fp_delete_finger:
* @dev: the struct #fp_dev device to perform the operation.
* @enrolled_data: the id need to delete on sensor. This id is
* returned in previously enrolled with a MIS device.
*
* Perform a delete data operation on sensor. When print data is stored on
* sensor, this function is needed when host deletes enrolled finger.
*
* Returns: negative code on error, otherwise a code from #fp_delete_result
*/
API_EXPORTED int fp_delete_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_data)
{
struct sync_delete_data *ddata;
gboolean stopped = FALSE;
int r;
if (!enrolled_data) {
fp_err("no print given");
return -EINVAL;
}
if (!fp_dev_supports_print_data(dev, enrolled_data)) {
fp_err("print is not compatible with device");
return -EINVAL;
}
fp_dbg("to be handled by %s", dev->drv->name);
ddata = g_malloc0(sizeof(struct sync_delete_data));
r = fp_async_delete_finger(dev, enrolled_data, sync_delete_cb, ddata);
if (r < 0) {
fp_dbg("delete_finger error %d", r);
g_free(ddata);
return r;
}
while (!ddata->populated) {
r = fp_handle_events();
if (r < 0)
goto out;
}
r = ddata->result;
fp_dbg("delete_finger result %d", r);
out:
g_free(ddata);
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;
G_DEBUG_HERE();
*stopped = TRUE;
}
/**
* fp_identify_finger_img:
* @dev: the struct #fp_dev device to perform the scan on
* @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
* @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
* @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
* 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_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.
*
* Returns: 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)
{
gboolean stopped = FALSE;
struct sync_identify_data *idata
= g_malloc0(sizeof(struct sync_identify_data));
int r;
fp_dbg("to be handled by %s", dev->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);
r = idata->result;
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;
}
/**
* fp_identify_finger:
* @dev: the struct #fp_dev device to perform the scan on
* @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
* @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
* Performs a new scan and attempts to identify the scanned finger against a
* collection of previously enrolled fingerprints. This function is just a
* shortcut to calling fp_identify_finger_img() with a %NULL image output
* parameter.
*
* See also fp_identify_finger_img().
*
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_identify_finger(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset)
{
return fp_identify_finger_img(dev, print_gallery, match_offset, NULL);
}
struct sync_capture_data {
gboolean populated;
int result;
struct fp_img *img;
};
static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img,
void *user_data)
{
struct sync_capture_data *vdata = user_data;
vdata->result = result;
vdata->img = img;
vdata->populated = TRUE;
}
static void capture_stop_cb(struct fp_dev *dev, void *user_data)
{
gboolean *stopped = user_data;
G_DEBUG_HERE();
*stopped = TRUE;
}
/**
* fp_dev_img_capture:
* @dev: the struct #fp_dev device
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
* @img: a location to return the captured image. Must be freed with
* fp_img_free() after use
*
* Captures a #fp_img from a device. The returned image is the raw
* image provided by the device, you may wish to [standardize](libfprint-Image-operations.html#img_std) it.
*
* If set, the @unconditional flag indicates that the device should
* capture an image unconditionally, regardless of whether a finger is there
* or not. If unset, this function will block until a finger is detected on
* the sensor.
*
* See fp_dev_supports_imaging().
*
* Returns: 0 on success, non-zero on error. -ENOTSUP indicates that either the
* @unconditional flag was set but the device does not support this, or that the
* device does not support imaging
*/
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **img)
{
struct sync_capture_data *vdata;
gboolean stopped = FALSE;
int r;
if (!dev->drv->capture_start) {
fp_dbg("image capture is not supported on %s device", dev->drv->name);
return -ENOTSUP;
}
fp_dbg("to be handled by %s", dev->drv->name);
vdata = g_malloc0(sizeof(struct sync_capture_data));
r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata);
if (r < 0) {
fp_dbg("capture_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_CAPTURE_COMPLETE:
fp_dbg("result: complete");
break;
case FP_CAPTURE_FAIL:
fp_dbg("result: fail");
break;
default:
fp_err("unrecognised return code %d", r);
r = -EINVAL;
}
err:
fp_dbg("ending capture");
if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0)
while (!stopped)
if (fp_handle_events() < 0)
break;
return r;
}