examples: Add back examples using the async APIs

Add the examples back by using the new async API, support verification and
enroll for devices with own storage.
This commit is contained in:
Marco Trevisan (Treviño) 2019-11-19 20:18:13 +01:00 committed by Benjamin Berg
parent 7d6b0c1376
commit b46d336d2b
6 changed files with 434 additions and 245 deletions

View file

@ -6,6 +6,10 @@
int main (int argc, char **argv)
{
fp_init ();
FpContext *ctx;
ctx = fp_context_new ();
g_object_unref (ctx);
return 0;
}

View file

@ -2,6 +2,7 @@
* Example fingerprint enrollment program
* Enrolls your right index finger and saves the print to disk
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,91 +20,128 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
typedef struct _EnrollData {
GMainLoop *loop;
int ret_value;
} EnrollData;
static void
enroll_data_free (EnrollData *enroll_data)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
struct fp_driver *drv;
if (!ddev)
g_main_loop_unref (enroll_data->loop);
g_free (enroll_data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (EnrollData, enroll_data_free)
FpDevice *discover_device (GPtrArray *devices)
{
FpDevice *dev;
if (!devices->len)
return NULL;
drv = fp_dscv_dev_get_driver(ddev);
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
return ddev;
dev = g_ptr_array_index (devices, 0);
printf("Found device claimed by %s driver\n", fp_device_get_driver (dev));
return dev;
}
struct fp_print_data *enroll(struct fp_dev *dev) {
struct fp_print_data *enrolled_print = NULL;
int r;
static void
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) {
EnrollData *enroll_data = user_data;
g_autoptr(GError) error = NULL;
printf("You will need to successfully scan your finger %d times to "
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
fp_device_close_finish (dev, res, &error);
do {
struct fp_img *img = NULL;
if (error)
g_warning ("Failed closing device %s\n", error->message);
printf("\nScan your finger now.\n");
g_main_loop_quit (enroll_data->loop);
}
r = fp_enroll_finger_img(dev, &enrolled_print, &img);
if (img) {
fp_img_save_to_file(img, "enrolled.pgm");
printf("Wrote scanned image to enrolled.pgm\n");
fp_img_free(img);
static void
on_enroll_completed (FpDevice *dev, GAsyncResult *res, void *user_data) {
EnrollData *enroll_data = user_data;
g_autoptr(FpPrint) print = NULL;
g_autoptr(GError) error = NULL;
print = fp_device_enroll_finish (dev, res, &error);
if (!error) {
enroll_data->ret_value = EXIT_SUCCESS;
if (!fp_device_has_storage (dev)) {
g_debug("Device has not storage, saving locally");
int r = print_data_save(print, FP_FINGER_RIGHT_INDEX);
if (r < 0) {
g_warning("Data save failed, code %d", r);
enroll_data->ret_value = EXIT_FAILURE;
}
}
if (r < 0) {
printf("Enroll failed with error %d\n", r);
return NULL;
}
switch (r) {
case FP_ENROLL_COMPLETE:
printf("Enroll complete!\n");
break;
case FP_ENROLL_FAIL:
printf("Enroll failed, something wen't wrong :(\n");
return NULL;
case FP_ENROLL_PASS:
printf("Enroll stage passed. Yay!\n");
break;
case FP_ENROLL_RETRY:
printf("Didn't quite catch that. Please try again.\n");
break;
case FP_ENROLL_RETRY_TOO_SHORT:
printf("Your swipe was too short, please try again.\n");
break;
case FP_ENROLL_RETRY_CENTER_FINGER:
printf("Didn't catch that, please center your finger on the "
"sensor and try again.\n");
break;
case FP_ENROLL_RETRY_REMOVE_FINGER:
printf("Scan failed, please remove your finger and then try "
"again.\n");
break;
}
} while (r != FP_ENROLL_COMPLETE);
if (!enrolled_print) {
fprintf(stderr, "Enroll complete but no print?\n");
return NULL;
} else {
g_warning("Enroll failed with error %s\n", error->message);
}
printf("Enrollment completed!\n\n");
return enrolled_print;
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
enroll_data);
}
static void
on_enroll_progress (FpDevice *device,
gint completed_stages,
FpPrint *print,
gpointer user_data,
GError *error)
{
if (error) {
g_warning ("Enroll stage %d of %d failed with error %s",
completed_stages,
fp_device_get_nr_enroll_stages (device),
error->message);
return;
}
if (fp_device_supports_capture (device) &&
print_image_save (print, "enrolled.pgm")) {
printf ("Wrote scanned image to enrolled.pgm\n");
}
printf ("Enroll stage %d of %d passed. Yay!\n", completed_stages,
fp_device_get_nr_enroll_stages (device));
}
static void
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
{
EnrollData *enroll_data = user_data;
FpPrint *print_template;
g_autoptr(GError) error = NULL;
if (!fp_device_open_finish (dev, res, &error)) {
g_warning ("Failed to open device: %s", error->message);
g_main_loop_quit (enroll_data->loop);
return;
}
printf ("Opened device. It's now time to enroll your finger.\n\n");
printf ("You will need to successfully scan your finger %d times to "
"complete the process.\n\n", fp_device_get_nr_enroll_stages (dev));
printf ("Scan your finger now.\n");
print_template = print_create_template (dev, FP_FINGER_RIGHT_INDEX);
fp_device_enroll (dev, print_template, NULL, on_enroll_progress, NULL,
NULL, (GAsyncReadyCallback) on_enroll_completed,
enroll_data);
}
int main(void)
{
int r = 1;
struct fp_dscv_dev *ddev;
struct fp_dscv_dev **discovered_devs;
struct fp_dev *dev;
struct fp_print_data *data;
g_autoptr (FpContext) ctx = NULL;
g_autoptr (EnrollData) enroll_data = NULL;
GPtrArray *devices;
FpDevice *dev;
printf("This program will enroll your right index finger, "
"unconditionally overwriting any right-index print that was enrolled "
@ -112,48 +150,29 @@ int main(void)
getchar();
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
ctx = fp_context_new ();
devices = fp_context_get_devices (ctx);
if (!devices) {
g_warning("Impossible to get devices");
return EXIT_FAILURE;
}
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
fprintf(stderr, "Could not discover devices\n");
goto out;
}
ddev = discover_device(discovered_devs);
if (!ddev) {
fprintf(stderr, "No devices detected.\n");
goto out;
}
dev = fp_dev_open(ddev);
fp_dscv_devs_free(discovered_devs);
dev = discover_device (devices);
if (!dev) {
fprintf(stderr, "Could not open device.\n");
goto out;
g_warning("No devices detected.");
return EXIT_FAILURE;
}
printf("Opened device. It's now time to enroll your finger.\n\n");
data = enroll(dev);
if (!data)
goto out_close;
enroll_data = g_new0 (EnrollData, 1);
enroll_data->ret_value = EXIT_FAILURE;
enroll_data->loop = g_main_loop_new (NULL, FALSE);
r = print_data_save(data, RIGHT_INDEX);
if (r < 0)
fprintf(stderr, "Data save failed, code %d\n", r);
fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened,
enroll_data);
fp_print_data_free(data);
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
g_main_loop_run (enroll_data->loop);
return enroll_data->ret_value;
}

View file

@ -1,22 +1,22 @@
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
examples = [ 'enroll', 'verify', 'manage-prints' ]
foreach example: examples
executable(example,
[example + '.c', 'storage.c'],
dependencies: [libfprint_dep],
dependencies: [libfprint_dep, glib_dep],
include_directories: [
root_inc,
],
c_args: common_cflags)
endforeach
# executable('cpp-test',
# 'cpp-test.cpp',
# dependencies: libfprint_dep,
# include_directories: [
# root_inc,
# ],
# c_args: common_cflags)
executable('cpp-test',
'cpp-test.cpp',
dependencies: libfprint_dep,
include_directories: [
root_inc,
],
c_args: common_cflags)
# if get_option('x11-examples')
# executable('img_capture_continuous',

View file

@ -2,6 +2,7 @@
* Trivial storage driver for example programs
*
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -18,41 +19,41 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <glib.h>
#include <libfprint/fprint.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#define STORAGE_FILE "test-storage.variant"
static char *
get_print_data_descriptor (struct fp_print_data *data, struct fp_dev *dev, enum fp_finger finger)
get_print_data_descriptor (FpPrint *print, FpDevice *dev, FpFinger finger)
{
gint drv_id;
gint devtype;
const char *driver;
const char *dev_id;
if (data) {
drv_id = fp_print_data_get_driver_id (data);
devtype = fp_print_data_get_devtype (data);
if (print) {
driver = fp_print_get_driver (print);
dev_id = fp_print_get_device_id (print);
} else {
drv_id = fp_driver_get_driver_id(fp_dev_get_driver (dev));
devtype = fp_dev_get_devtype (dev);
driver = fp_device_get_driver (dev);
dev_id = fp_device_get_device_id (dev);
}
return g_strdup_printf("%x/%08x/%x",
drv_id,
devtype,
return g_strdup_printf("%s/%s/%x",
driver,
dev_id,
finger);
}
static GVariantDict*
static GVariantDict *
load_data(void)
{
GVariantDict *res;
GVariant *var;
gchar *contents = NULL;
g_autofree gchar *contents = NULL;
gssize length = 0;
if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) {
@ -88,49 +89,125 @@ save_data(GVariant *data)
}
int
print_data_save(struct fp_print_data *fp_data, enum fp_finger finger)
print_data_save(FpPrint *print, FpFinger finger)
{
gchar *descr = get_print_data_descriptor (fp_data, NULL, finger);
GVariantDict *dict;
g_autofree gchar *descr = get_print_data_descriptor (print, NULL, finger);
g_autoptr (GError) error = NULL;
g_autoptr (GVariantDict) dict = NULL;
g_autofree guchar *data = NULL;
GVariant *val;
guchar *data;
gsize size;
int res;
dict = load_data();
size = fp_print_data_get_data(fp_data, &data);
fp_print_serialize (print, &data, &size, &error);
if (error) {
g_warning ("Error serializing data: %s", error->message);
return -1;
}
val = g_variant_new_fixed_array (G_VARIANT_TYPE("y"), data, size, 1);
g_variant_dict_insert_value (dict, descr, val);
res = save_data(g_variant_dict_end(dict));
g_variant_dict_unref(dict);
return res;
}
struct fp_print_data*
print_data_load(struct fp_dev *dev, enum fp_finger finger)
FpPrint *
print_data_load(FpDevice *dev, FpFinger finger)
{
gchar *descr = get_print_data_descriptor (NULL, dev, finger);
GVariantDict *dict;
guchar *stored_data;
g_autofree gchar *descr = get_print_data_descriptor (NULL, dev, finger);
g_autoptr (GVariant) val = NULL;
g_autoptr (GVariantDict) dict = NULL;
g_autofree guchar *stored_data = NULL;
gsize stored_len;
GVariant *val;
struct fp_print_data *res = NULL;
dict = load_data();
val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay"));
if (val) {
stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1);
res = fp_print_data_from_data(stored_data, stored_len);
FpPrint *print;
g_autoptr (GError) error = NULL;
g_variant_unref(val);
stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1);
print = fp_print_deserialize (stored_data, stored_len, &error);
if (error)
g_warning ("Error deserializing data: %s", error->message);
return print;
}
g_variant_dict_unref(dict);
g_free(descr);
return res;
return NULL;
}
FpPrint *
print_create_template (FpDevice *dev, FpFinger finger)
{
g_autoptr(GDateTime) datetime = NULL;
FpPrint *template = NULL;
GDate *date = NULL;
gint year, month, day;
template = fp_print_new (dev);
fp_print_set_finger (template, finger);
fp_print_set_username (template, g_get_user_name ());
datetime = g_date_time_new_now_local ();
g_date_time_get_ymd (datetime, &year, &month, &day);
date = g_date_new_dmy (day, month, year);
fp_print_set_enroll_date (template, date);
g_date_free (date);
return template;
}
static gboolean
save_image_to_pgm (FpImage *img, const char *path)
{
FILE *fd = fopen (path, "w");
size_t write_size;
const guchar *data = fp_image_get_data (img, &write_size);
int r;
if (!fd) {
g_warning("could not open '%s' for writing: %d", path, errno);
return FALSE;
}
r = fprintf (fd, "P5 %d %d 255\n",
fp_image_get_width (img), fp_image_get_height (img));
if (r < 0) {
fclose(fd);
g_critical("pgm header write failed, error %d", r);
return FALSE;
}
r = fwrite (data, 1, write_size, fd);
if (r < write_size) {
fclose(fd);
g_critical("short write (%d)", r);
return FALSE;
}
fclose (fd);
g_debug ("written to '%s'", path);
return TRUE;
}
gboolean print_image_save (FpPrint *print, const char *path)
{
g_autoptr(FpImage) img = NULL;
g_return_val_if_fail (FP_IS_PRINT (print), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
img = fp_print_get_image (print);
if (img)
return save_image_to_pgm (img, path);
return FALSE;
}

View file

@ -21,7 +21,10 @@
#ifndef __STORAGE_H
#define __STORAGE_H
int print_data_save(struct fp_print_data *fp_data, enum fp_finger finger);
struct fp_print_data* print_data_load(struct fp_dev *dev, enum fp_finger finger);
int print_data_save(FpPrint *print, FpFinger finger);
FpPrint * print_data_load(FpDevice *dev, FpFinger finger);
FpPrint * print_create_template(FpDevice *dev, FpFinger finger);
gboolean print_image_save(FpPrint *print, const char *path);
#endif /* __STORAGE_H */

View file

@ -2,6 +2,7 @@
* Example fingerprint verification program, which verifies the right index
* finger which has been previously enrolled to disk.
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2019 Marco Trevisan <marco.trevisan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,131 +20,216 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
#include "storage.h"
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
typedef struct _VerifyData {
GMainLoop *loop;
int ret_value;
} VerifyData;
static void
verify_data_free (VerifyData *verify_data)
{
struct fp_dscv_dev *ddev = discovered_devs[0];
struct fp_driver *drv;
if (!ddev)
g_main_loop_unref (verify_data->loop);
g_free (verify_data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (VerifyData, verify_data_free)
FpDevice *discover_device (GPtrArray *devices)
{
FpDevice *dev;
if (!devices->len)
return NULL;
drv = fp_dscv_dev_get_driver(ddev);
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
return ddev;
dev = g_ptr_array_index (devices, 0);
printf("Found device claimed by %s driver\n", fp_device_get_driver (dev));
return dev;
}
int verify(struct fp_dev *dev, struct fp_print_data *data)
static void
on_device_closed (FpDevice *dev, GAsyncResult *res, void *user_data) {
VerifyData *verify_data = user_data;
g_autoptr(GError) error = NULL;
fp_device_close_finish (dev, res, &error);
if (error)
g_warning ("Failed closing device %s\n", error->message);
g_main_loop_quit (verify_data->loop);
}
static void start_verification (FpDevice *dev, VerifyData *verify_data);
static void
on_verify_completed (FpDevice *dev, GAsyncResult *res, void *user_data)
{
int r;
VerifyData *verify_data = user_data;
g_autoptr(FpPrint) print = NULL;
g_autoptr(GError) error = NULL;
char buffer[20];
gboolean match;
do {
struct fp_img *img = NULL;
if (!fp_device_verify_finish (dev, res, &match, &print, &error)) {
g_warning ("Failed to verify print: %s", error->message);
g_main_loop_quit (verify_data->loop);
return;
}
sleep(1);
printf("\nScan your finger now.\n");
r = fp_verify_finger_img(dev, data, &img);
if (img) {
fp_img_save_to_file(img, "verify.pgm");
printf("Wrote scanned image to verify.pgm\n");
fp_img_free(img);
if (match) {
g_print ("MATCH!\n");
if (fp_device_supports_capture (dev) &&
print_image_save (print, "verify.pgm")) {
g_print("Print image saved as verify.pgm");
}
if (r < 0) {
printf("verification failed with error %d :(\n", r);
return r;
verify_data->ret_value = EXIT_SUCCESS;
} else {
g_print ("NO MATCH!\n");
verify_data->ret_value = EXIT_FAILURE;
}
g_print ("Verify again? [Y/n]? ");
if (fgets (buffer, sizeof (buffer), stdin) &&
(buffer[0] == 'Y' || buffer[0] == 'y')) {
start_verification (dev, verify_data);
return;
}
fp_device_close (dev, NULL, (GAsyncReadyCallback) on_device_closed,
verify_data);
}
static void
on_list_completed (FpDevice *dev, GAsyncResult *res, gpointer user_data)
{
VerifyData *verify_data = user_data;
g_autoptr(GPtrArray) prints = NULL;
g_autoptr(GError) error = NULL;
prints = fp_device_list_prints_finish (dev, res, &error);
if (!error) {
FpPrint *verify_print = NULL;
guint i;
if (!prints->len)
g_warning ("No prints saved on device");
for (i = 0; i < prints->len; ++i) {
FpPrint *print = prints->pdata[i];
if (fp_print_get_finger (print) == FP_FINGER_RIGHT_INDEX &&
g_strcmp0 (fp_print_get_username (print), g_get_user_name ()) == 0) {
if (!verify_print ||
(g_date_compare (fp_print_get_enroll_date (print),
fp_print_get_enroll_date (verify_print)) >= 0))
verify_print = print;
}
}
switch (r) {
case FP_VERIFY_NO_MATCH:
printf("NO MATCH!\n");
return 0;
case FP_VERIFY_MATCH:
printf("MATCH!\n");
return 0;
case FP_VERIFY_RETRY:
printf("Scan didn't quite work. Please try again.\n");
break;
case FP_VERIFY_RETRY_TOO_SHORT:
printf("Swipe was too short, please try again.\n");
break;
case FP_VERIFY_RETRY_CENTER_FINGER:
printf("Please center your finger on the sensor and try again.\n");
break;
case FP_VERIFY_RETRY_REMOVE_FINGER:
printf("Please remove finger from the sensor and try again.\n");
break;
if (!verify_print) {
g_warning ("Did you remember to enroll your right index "
"finger first?");
g_main_loop_quit (verify_data->loop);
return;
}
} while (1);
g_debug ("Comparing print with %s",
fp_print_get_description (verify_print));
g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL,
(GAsyncReadyCallback) on_verify_completed,
verify_data);
} else {
g_warning ("Loading prints failed with error %s", error->message);
g_main_loop_quit (verify_data->loop);
}
}
static void
start_verification (FpDevice *dev, VerifyData *verify_data)
{
if (fp_device_has_storage (dev)) {
g_print ("Creating finger template, using device storage...\n");
fp_device_list_prints (dev, NULL,
(GAsyncReadyCallback) on_list_completed,
verify_data);
} else {
g_print ("Loading previously enrolled right index finger data...\n");
g_autoptr(FpPrint) verify_print;
verify_print = print_data_load (dev, FP_FINGER_RIGHT_INDEX);
if (!verify_print) {
g_warning ("Failed to load fingerprint data");
g_warning ("Did you remember to enroll your right index "
"finger first?");
g_main_loop_quit (verify_data->loop);
return;
}
g_print ("Print loaded. Time to verify!\n");
fp_device_verify (dev, verify_print, NULL,
(GAsyncReadyCallback) on_verify_completed,
verify_data);
}
}
static void
on_device_opened (FpDevice *dev, GAsyncResult *res, void *user_data)
{
VerifyData *verify_data = user_data;
g_autoptr(GError) error = NULL;
if (!fp_device_open_finish (dev, res, &error)) {
g_warning ("Failed to open device: %s", error->message);
g_main_loop_quit (verify_data->loop);
return;
}
g_print ("Opened device. ");
start_verification (dev, verify_data);
}
int main(void)
{
int r = 1;
struct fp_dscv_dev *ddev;
struct fp_dscv_dev **discovered_devs;
struct fp_dev *dev;
struct fp_print_data *data;
g_autoptr (FpContext) ctx = NULL;
g_autoptr (VerifyData) verify_data = NULL;
GPtrArray *devices;
FpDevice *dev;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
ctx = fp_context_new ();
devices = fp_context_get_devices (ctx);
if (!devices) {
g_warning("Impossible to get devices");
return EXIT_FAILURE;
}
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
fprintf(stderr, "Could not discover devices\n");
goto out;
}
ddev = discover_device(discovered_devs);
if (!ddev) {
fprintf(stderr, "No devices detected.\n");
goto out;
}
dev = fp_dev_open(ddev);
fp_dscv_devs_free(discovered_devs);
dev = discover_device (devices);
if (!dev) {
fprintf(stderr, "Could not open device.\n");
goto out;
g_warning("No devices detected.");
return EXIT_FAILURE;
}
printf("Opened device. Loading previously enrolled right index finger "
"data...\n");
verify_data = g_new0 (VerifyData, 1);
verify_data->ret_value = EXIT_FAILURE;
verify_data->loop = g_main_loop_new (NULL, FALSE);
data = print_data_load(dev, RIGHT_INDEX);
if (!data) {
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
fprintf(stderr, "Did you remember to enroll your right index finger "
"first?\n");
goto out_close;
}
fp_device_open (dev, NULL, (GAsyncReadyCallback) on_device_opened,
verify_data);
printf("Print loaded. Time to verify!\n");
do {
char buffer[20];
g_main_loop_run (verify_data->loop);
verify(dev, data);
printf("Verify again? [Y/n]? ");
fgets(buffer, sizeof(buffer), stdin);
if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y')
break;
} while (1);
fp_print_data_free(data);
out_close:
fp_dev_close(dev);
out:
fp_exit();
return r;
return verify_data->ret_value;
}