Compare commits

...

44 Commits

Author SHA1 Message Date
Vasily Khoruzhick
d82847a6b4 0.7.0 2017-05-14 09:26:30 -07:00
Vasily Khoruzhick
5a7e6e07ff etes603: fix compilation error caused by a0bbbd7d3 2016-11-07 08:10:44 -08:00
Vasily Khoruzhick
2162aa9f48 uru4k: decrypt image if necessary
Add naive detection of image encryption: if stddev between two
adjacent rows is higher than threshold assume that image is encrypted
and decrypt it.

Fixes fd.o bug 88945
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
a1f36c71c9 upeksonly: decrease bz3_threshold for 147e:1001
Its width is only 216 pixels, and it appears not to be enough for matching at
default threshold.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
e4eedef27e upeksonly: use fpi_std_sq_dev() and fpi_mean_sq_diff_norm()
Use fpi_mean_sq_diff_norm() for blank line detection and
fpi_mean_sq_diff_norm() for duplicate line detection.
Fixes finger presence and removal detection.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
5e29695969 lib: move some functions from vfs5011.c into img.c 2016-11-05 20:58:18 -07:00
Konstantin Semenov
49a46668ad lib: Add VFS0050 driver
New driver for 138a:0050 device

https://bugs.freedesktop.org/show_bug.cgi?id=91616
2016-11-05 20:58:18 -07:00
Bastien Nocera
76269decdd aes4000: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.

https://bugzilla.gnome.org/show_bug.cgi?id=98594
2016-11-05 00:13:51 +01:00
Bastien Nocera
8454a25ecf aes3500: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.
2016-11-05 00:11:30 +01:00
Bastien Nocera
a0bbbd7d32 drivers: Print USB error when libusb_claim_interface() fails
https://bugs.freedesktop.org/show_bug.cgi?id=98594
2016-11-05 00:10:17 +01:00
Bastien Nocera
12f6dae8cd build: Fix 9570c36 on Debian
Debian's ash doesn't implement pushd/popd.
2016-11-03 14:05:46 +01:00
Bastien Nocera
9570c36fd4 build: Fix running autogen.sh out-of-tree 2016-07-19 11:18:58 +02:00
Bastien Nocera
487dae0d2f Work-around kernel's lack of USB PM
The device is already supported by the vfs5011 driver and non-
blacklisted, so will show up in the udev rules, but for ease of
backporting to older versions, add it to the whitelist anyway.

https://bugzilla.redhat.com/show_bug.cgi?id=1173367
2016-04-12 16:39:39 +02:00
Vasily Khoruzhick
d71018bd8f vfs5011: add USB IDs of device found in some Toshiba laptops 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
61fa57b05d aes1610: decrease bz3_threshold to 20 2015-12-16 13:24:39 -08:00
Volkau Siarhei
fc1781e317 vfs101: fix broken enrolling by prev commit to vfs101.c 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
ffef6c2bcc vfs5011: add proper dev_deactivate() support
Driver is not able to cancel imaging process without this change
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
67d29f7936 aes1610: improve gain settings 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
9437c98d54 lib: frame assembling: flip image for non-reverse direction
It was here for aes2501 and aes1610 before moving assembling routines
into common code.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
f7d00a828d lib: use normalized error to make decision about movement direction
Height is not a good determinant in movement direction, normalized
error is much better. Should fix aes1610 and aes2501 driver issues.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
0f0a4b2da6 aes2660: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
c9cdbaf880 aes2550: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
83f29dad9f aes2501: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
c2a11c5918 aes1610: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
3746b2ad5c upeksonly: use line assembling routines to account variable swiping speed 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
b51fa446e3 lib: move line assembling routines out of vfs5011 into common code 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6fc5293e83 upeksonly: add support for 147e:1001 device 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
aab3daa28b upeksonly: move regwrite sequences into a header 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
e40f7bd1f7 lib: move frame assembling routines into a separate file and make them usable by non-aes drivers
Frame assembling routines are not aes-specific, so move them into a separate file
and add an accessor for peeking a pixel.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6664f87d8f aes1610: fix memory corruption introduced by a457658f1b 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6e8d5cd6a1 vfs5011: do duplicate line search for 30 lines
It seems that on faster devices, the driver can get more duplicate lines.
Without this change scan looks too stretched.

Suggested by: Thomas Rinsma <thomasrinsma@gmail.com>
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9f7e1ecf40 vfs5011: make '5 scans per enroll' work with this driver
Restart scan if core did not ask to terminate it explicitly.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
d9567002e4 imgdev: reset action_result after reporting it
Otherwise we end up in reporting the same result on next iteration.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
a656a4a9f3 upektc_img: Handle scan status reported by sensor properly
Sensor can report "short scan" or "finger not centered" instead of image,
so it's necessary to handle them gracefully.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
2944a35e74 imgdev: Add fpi_imgdev_abort_scan() to abort scan gracefully from within the driver.
Smart sensors like Upek TouchChip Coprocessor can provide scan status instead
of image, as result we need to report such status as "short scan" or "finger not centered"
from within the driver, since it's not a session error.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
391373fb0c upektc_img: Fix copy/paste error
Replace upekts_img with upektc_img
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
985e8c4577 aes1660: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 25,
since for wrong finger score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
bd0d4258e4 upektc_img: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 20,
since for wrong fingerprint score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9bbd9b208a lib: add partial image flag
And activate perimeter points removal if this flag is set.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
59fe0fb699 nbis: add one more step of false minutia removal
Remove minutiae that are located on scan perimeter
2015-12-16 13:24:38 -08:00
Bastien Nocera
f1fdd71613 rules: Add driver name to the USB properties
Makes it easier to see which driver is being used.
2015-03-15 23:13:08 +01:00
Bastien Nocera
bb66780cb5 build: Document why some drivers are disabled
This avoids confusion when looking at the build logs
2015-03-15 23:10:03 +01:00
Carlos Olmedo Escobar
45fb6d7908 vfs101: Logical conjunction always evaluates to false. 2015-02-09 12:47:22 +01:00
Timo Teräs
9f408bf51b imgdev: fix enum mismatch for dev_change_state() call
This bug has existed long time, but it was uncovered by commit
e215b05094 which enabled multiple enrollment rounds.

In practice this broke (at least) URU4000 driver state machine
causing it to enter indefinite loop - due to the invalid state
change callback.

Patch originally posted at:
http://lists.freedesktop.org/archives/fprint/2014-June/000603.html

Test and verification results:
http://lists.freedesktop.org/archives/fprint/2014-June/000607.html

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
2015-02-03 18:26:33 +01:00
41 changed files with 2928 additions and 779 deletions

22
NEWS
View File

@@ -1,6 +1,28 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2017-05-14: v0.7.0 release
* Drivers:
- Add VFS0050 driver
- Fix possible crash in AES3500 and AES4000
- Fix broken enrollment in VFS101
- Better verification with small sensor scanners
- Plenty of fixes in VFS5011
- Fix memory corruption in AES1610
- Improve calibration settings for AES1610
- Improve image assembling in upeksonly driver
- Autodetect whether image is encrypted in uru4k
* Library:
- NBIS: Remove false minutia at the edge of partial image
- Introduce routines to assemble image from lines (used in VFS5011 and upeksonly)
- Fix a bug that can cause driver state machine to enter into endless loop.
* Udev rules:
- Add driver name to the USB properties
* Plenty of build fixes
2015-02-03: v0.6.0 release
* Drivers:

View File

@@ -1,10 +1,20 @@
#!/bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
olddir="`pwd`"
cd "$srcdir"
libtoolize --copy --force || exit 1
aclocal || exit 1
autoheader || exit 1
autoconf || exit 1
automake -a -c || exit 1
cd "$olddir"
if test -z "$NOCONFIGURE"; then
exec ./configure --enable-maintainer-mode --enable-examples-build \
$srcdir/configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*
fi

View File

@@ -1,4 +1,4 @@
AC_INIT([libfprint], [0.6.0])
AC_INIT([libfprint], [0.7.0])
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news subdir-objects])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
@@ -23,7 +23,7 @@ AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 vfs5011 upektc_img etes603"
all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 vfs5011 upektc_img etes603 vfs0050"
require_imaging='no'
require_aeslib='no'
@@ -48,6 +48,7 @@ enable_vfs301='no'
enable_vfs5011='no'
enable_upektc_img='no'
enable_etes603='no'
enable_vfs0050='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
@@ -150,6 +151,10 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
AC_DEFINE([ENABLE_ETES603], [], [Build EgisTec ES603 driver])
enable_etes603="yes"
;;
vfs0050)
AC_DEFINE([ENABLE_VFS0050], [], [Build Validity VFS0050 driver])
enable_vfs0050="yes"
;;
esac
done
@@ -175,6 +180,7 @@ AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
AM_CONDITIONAL([ENABLE_VFS5011], [test "$enable_vfs5011" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKTC_IMG], [test "$enable_upektc_img" = "yes"])
AM_CONDITIONAL([ENABLE_ETES603], [test "$enable_etes603" = "yes"])
AM_CONDITIONAL([ENABLE_VFS0050], [test "$enable_vfs0050" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
@@ -309,7 +315,7 @@ fi
if test x$enable_upeke2 != xno ; then
AC_MSG_NOTICE([** upeke2 driver enabled])
else
AC_MSG_NOTICE([ upeke2 driver disabled])
AC_MSG_NOTICE([ upeke2 driver disabled (handled by upektc_img driver)])
fi
if test x$enable_upektc != xno ; then
AC_MSG_NOTICE([** upektc driver enabled])
@@ -334,7 +340,7 @@ fi
if test x$enable_fdu2000 != xno ; then
AC_MSG_NOTICE([** fdu2000 driver enabled])
else
AC_MSG_NOTICE([ fdu2000 driver disabled])
AC_MSG_NOTICE([ fdu2000 driver disabled (not ported)])
fi
if test x$enable_aes1610 != xno ; then
AC_MSG_NOTICE([** aes1610 driver enabled])
@@ -396,6 +402,11 @@ if test x$enable_etes603 != xno ; then
else
AC_MSG_NOTICE([ etes603 driver disabled])
fi
if test x$enable_vfs0050 != xno ; then
AC_MSG_NOTICE([** vfs0050 driver enabled])
else
AC_MSG_NOTICE([ vfs0050 driver disabled])
fi
if test x$require_aeslib != xno ; then
AC_MSG_NOTICE([** aeslib helper functions enabled])
else

View File

@@ -5,7 +5,7 @@ MOSTLYCLEANFILES = $(udev_rules_DATA)
UPEKE2_SRC = drivers/upeke2.c
UPEKTS_SRC = drivers/upekts.c
UPEKTC_SRC = drivers/upektc.c drivers/upektc.h
UPEKSONLY_SRC = drivers/upeksonly.c
UPEKSONLY_SRC = drivers/upeksonly.c drivers/upeksonly.h
URU4000_SRC = drivers/uru4000.c
AES1610_SRC = drivers/aes1610.c
AES1660_SRC = drivers/aes1660.c drivers/aes1660.h
@@ -21,6 +21,7 @@ VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h dri
VFS5011_SRC = drivers/vfs5011.c drivers/vfs5011_proto.h
UPEKTC_IMG_SRC = drivers/upektc_img.c drivers/upektc_img.h
ETES603_SRC = drivers/etes603.c
VFS0050_SRC = drivers/vfs0050.c drivers/vfs0050.h
EXTRA_DIST = \
$(UPEKE2_SRC) \
@@ -42,12 +43,15 @@ EXTRA_DIST = \
$(VFS5011_SRC) \
$(UPEKTC_IMG_SRC) \
$(ETES603_SRC) \
$(VFS0050_SRC) \
drivers/aesx660.c \
drivers/aesx660.h \
drivers/aes3k.c \
drivers/aes3k.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
assembling.c \
assembling.h \
pixman.c \
60-fprint-autosuspend.rules
@@ -182,6 +186,10 @@ if ENABLE_ETES603
DRIVER_SRC += $(ETES603_SRC)
endif
if ENABLE_VFS0050
DRIVER_SRC += $(VFS0050_SRC)
endif
if REQUIRE_PIXMAN
OTHER_SRC += pixman.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
@@ -210,6 +218,8 @@ libfprint_la_SOURCES = \
imgdev.c \
poll.c \
sync.c \
assembling.c \
assembling.h \
$(DRIVER_SRC) \
$(OTHER_SRC) \
$(NBIS_SRC)

View File

@@ -26,6 +26,7 @@
#include <glib.h>
#include "fp_internal.h"
#include "assembling.h"
#include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16
@@ -158,268 +159,16 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
continue_write_regv(wdata);
}
static inline unsigned char aes_get_pixel(struct aes_stripe *frame,
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y,
unsigned int frame_width,
unsigned int frame_height)
unsigned int y)
{
unsigned char ret;
ret = frame->data[x * (frame_height >> 1) + (y >> 1)];
ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}
static unsigned int calc_error(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
int dx,
int dy,
unsigned int frame_width,
unsigned int frame_height)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = frame_width - (dx > 0 ? dx : -dx);
height = frame_height - dy;
y1 = 0;
y2 = dy;
i = 0;
err = 0;
do {
x1 = dx < 0 ? 0 : dx;
x2 = dx < 0 ? -dx : 0;
j = 0;
do {
unsigned char v1, v2;
v1 = aes_get_pixel(first_frame, x1, y1, frame_width, frame_height);
v2 = aes_get_pixel(second_frame, x2, y2, frame_width, frame_height);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (frame_height * frame_width);
err /= (height * width);
if (err == 0)
return INT_MAX;
return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
static void find_overlap(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
unsigned int *min_error,
unsigned int frame_width,
unsigned int frame_height)
{
int dx, dy;
unsigned int err;
*min_error = INT_MAX;
/* Seeking in horizontal and vertical dimensions,
* for horizontal dimension we'll check only 8 pixels
* in both directions. For vertical direction diff is
* rarely less than 2, so start with it.
*/
for (dy = 2; dy < frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(first_frame, second_frame,
dx, dy, frame_width, frame_height);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
unsigned int aes_calc_delta(GSList *stripes, size_t num_stripes,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
int height = 0;
struct aes_stripe *prev_stripe = list_entry->data;
unsigned int min_error;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct aes_stripe *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(prev_stripe, cur_stripe, &min_error,
frame_width, frame_height);
prev_stripe->delta_y = -prev_stripe->delta_y;
prev_stripe->delta_x = -prev_stripe->delta_x;
}
else
find_overlap(cur_stripe, prev_stripe, &min_error,
frame_width, frame_height);
frame++;
height += prev_stripe->delta_y;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
if (height < 0)
height = -height;
height += frame_height;
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return height;
}
static inline void aes_blit_stripe(struct fp_img *img,
struct aes_stripe *stripe,
int x, int y, unsigned int frame_width,
unsigned int frame_height)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = frame_height + y;
} else {
iy = y;
fy = 0;
height = frame_height;
}
if (fx > frame_width)
return;
if (fy > frame_height)
return;
if (ix > img->width)
return;
if (iy > img->height)
return;
if ((iy + height) > img->height)
height = img->height - iy;
for (; fy < height; fy++, iy++) {
if (x < 0) {
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
}
for (; fx < width; fx++, ix++) {
img->data[ix + (iy * img->width)] = aes_get_pixel(stripe, fx, fy, frame_width, frame_height);
}
}
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height, unsigned int img_width)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct aes_stripe *aes_stripe;
BUG_ON(stripes_len == 0);
BUG_ON(img_width < frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
aes_stripe = stripe->data;
aes_stripe->delta_x = 0;
aes_stripe->delta_y = 0;
do {
aes_stripe = stripe->data;
height += aes_stripe->delta_y;
i++;
stripe = g_slist_next(stripe);
} while (i < stripes_len);
fp_dbg("height is %d", height);
if (height < 0) {
reverse = TRUE;
height = -height;
}
/* For last frame */
height += frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(img_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->width = img_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - frame_height) : 0;
x = (img_width - frame_width) / 2;
do {
aes_stripe = stripe->data;
y += aes_stripe->delta_y;
x += aes_stripe->delta_x;
aes_blit_stripe(img, aes_stripe, x, y, frame_width, frame_height);
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}

View File

@@ -27,11 +27,8 @@ struct aes_regwrite {
unsigned char value;
};
struct aes_stripe {
int delta_x;
int delta_y;
unsigned char data[0];
};
struct fpi_frame;
struct fpi_frame_asmbl_ctx;
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
@@ -39,12 +36,10 @@ typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data);
unsigned int aes_calc_delta(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse);
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height, unsigned int img_width);
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
#endif

423
libfprint/assembling.c Normal file
View File

@@ -0,0 +1,423 @@
/*
* Image assembling routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 "assembling"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "fp_internal.h"
#include "assembling.h"
static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
int dx,
int dy)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = ctx->frame_width - (dx > 0 ? dx : -dx);
height = ctx->frame_height - dy;
y1 = 0;
y2 = dy;
i = 0;
err = 0;
do {
x1 = dx < 0 ? 0 : dx;
x2 = dx < 0 ? -dx : 0;
j = 0;
do {
unsigned char v1, v2;
v1 = ctx->get_pixel(ctx, first_frame, x1, y1);
v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (ctx->frame_height * ctx->frame_width);
err /= (height * width);
if (err == 0)
return INT_MAX;
return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
static void find_overlap(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
unsigned int *min_error)
{
int dx, dy;
unsigned int err;
*min_error = 255 * ctx->frame_height * ctx->frame_width;
/* Seeking in horizontal and vertical dimensions,
* for horizontal dimension we'll check only 8 pixels
* in both directions. For vertical direction diff is
* rarely less than 2, so start with it.
*/
for (dy = 2; dy < ctx->frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(ctx, first_frame, second_frame,
dx, dy);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
struct fpi_frame *prev_stripe = list_entry->data;
unsigned int min_error;
/* Max error is width * height * 255, for AES2501 which has the largest
* sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
* we might get int overflow. Use 64bit value here to prevent integer overflow
*/
unsigned long long total_error = 0;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct fpi_frame *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
prev_stripe->delta_y = -prev_stripe->delta_y;
prev_stripe->delta_x = -prev_stripe->delta_x;
}
else
find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
total_error += min_error;
frame++;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return total_error / num_stripes;
}
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes)
{
int err, rev_err;
err = do_movement_estimation(ctx, stripes, num_stripes, FALSE);
rev_err = do_movement_estimation(ctx, stripes, num_stripes, TRUE);
fp_dbg("errors: %d rev: %d", err, rev_err);
if (err < rev_err) {
do_movement_estimation(ctx, stripes, num_stripes, FALSE);
}
}
static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
struct fp_img *img,
struct fpi_frame *stripe,
int x, int y)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = ctx->frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = ctx->frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = ctx->frame_height + y;
} else {
iy = y;
fy = 0;
height = ctx->frame_height;
}
if (fx > ctx->frame_width)
return;
if (fy > ctx->frame_height)
return;
if (ix > img->width)
return;
if (iy > img->height)
return;
if ((iy + height) > img->height)
height = img->height - iy;
for (; fy < height; fy++, iy++) {
if (x < 0) {
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
}
for (; fx < width; fx++, ix++) {
img->data[ix + (iy * img->width)] = ctx->get_pixel(ctx, stripe, fx, fy);
}
}
}
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct fpi_frame *fpi_frame;
BUG_ON(stripes_len == 0);
BUG_ON(ctx->image_width < ctx->frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
fpi_frame = stripe->data;
fpi_frame->delta_x = 0;
fpi_frame->delta_y = 0;
do {
fpi_frame = stripe->data;
height += fpi_frame->delta_y;
i++;
stripe = g_slist_next(stripe);
} while (i < stripes_len);
fp_dbg("height is %d", height);
if (height < 0) {
reverse = TRUE;
height = -height;
}
/* For last frame */
height += ctx->frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(ctx->image_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->flags |= reverse ? 0 : FP_IMG_H_FLIPPED | FP_IMG_V_FLIPPED;
img->width = ctx->image_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - ctx->frame_height) : 0;
x = (ctx->image_width - ctx->frame_width) / 2;
do {
fpi_frame = stripe->data;
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
aes_blit_stripe(ctx, img, fpi_frame, x, y);
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}
static int cmpint(const void *p1, const void *p2, gpointer data)
{
int a = *((int *)p1);
int b = *((int *)p2);
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
static void median_filter(int *data, int size, int filtersize)
{
int i;
int *result = (int *)g_malloc0(size*sizeof(int));
int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
for (i = 0; i < size; i++) {
int i1 = i - (filtersize-1)/2;
int i2 = i + (filtersize-1)/2;
if (i1 < 0)
i1 = 0;
if (i2 >= size)
i2 = size-1;
g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
result[i] = sortbuf[(i2-i1+1)/2];
}
memmove(data, result, size*sizeof(int));
g_free(result);
g_free(sortbuf);
}
static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, float y1, GSList *line2,
float y2, unsigned char *output, float yi, int size)
{
int i;
unsigned char p1, p2;
if (!line1 || !line2)
return;
for (i = 0; i < size; i++) {
p1 = ctx->get_pixel(ctx, line1, i);
p2 = ctx->get_pixel(ctx, line2, i);
output[i] = (float)p1
+ (yi - y1)/(y2 - y1)*(p2 - p1);
}
}
static int min(int a, int b) {return (a < b) ? a : b; }
/* Rescale image to account for variable swiping speed */
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t lines_len)
{
/* Number of output lines per distance between two scanners */
int i;
GSList *row1, *row2;
float y = 0.0;
int line_ind = 0;
int *offsets = (int *)g_malloc0((lines_len / 2) * sizeof(int));
unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
struct fp_img *img;
fp_dbg("%llu", g_get_real_time());
row1 = lines;
for (i = 0; (i < lines_len - 1) && row1; i += 2) {
int bestmatch = i;
int bestdiff = 0;
int j, firstrow, lastrow;
firstrow = i + 1;
lastrow = min(i + ctx->max_search_offset, lines_len - 1);
row2 = g_slist_next(row1);
for (j = firstrow; j <= lastrow; j++) {
int diff = ctx->get_deviation(ctx,
row1,
row2);
if ((j == firstrow) || (diff < bestdiff)) {
bestdiff = diff;
bestmatch = j;
}
row2 = g_slist_next(row2);
}
offsets[i / 2] = bestmatch - i;
fp_dbg("%d", offsets[i / 2]);
row1 = g_slist_next(row1);
if (row1)
row1 = g_slist_next(row1);
}
median_filter(offsets, (lines_len / 2) - 1, ctx->median_filter_size);
fp_dbg("offsets_filtered: %llu", g_get_real_time());
for (i = 0; i <= (lines_len / 2) - 1; i++)
fp_dbg("%d", offsets[i]);
row1 = lines;
for (i = 0; i < lines_len - 1; i++, row1 = g_slist_next(row1)) {
int offset = offsets[i/2];
if (offset > 0) {
float ynext = y + (float)ctx->resolution / offset;
while (line_ind < ynext) {
if (line_ind > ctx->max_height - 1)
goto out;
interpolate_lines(ctx,
row1, y,
g_slist_next(row1),
ynext,
output + line_ind * ctx->line_width,
line_ind,
ctx->line_width);
line_ind++;
}
y = ynext;
}
}
out:
img = fpi_img_new(ctx->line_width * line_ind);
img->height = line_ind;
img->width = ctx->line_width;
img->flags = FP_IMG_V_FLIPPED;
g_memmove(img->data, output, ctx->line_width * line_ind);
g_free(offsets);
g_free(output);
return img;
}

65
libfprint/assembling.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* Image assembling routines
* Shared functions between libfprint Authentec drivers
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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
*/
#ifndef __ASSEMBLING_H__
#define __ASSEMBLING_H__
#include <fp_internal.h>
struct fpi_frame {
int delta_x;
int delta_y;
unsigned char data[0];
};
struct fpi_frame_asmbl_ctx {
unsigned frame_width;
unsigned frame_height;
unsigned image_width;
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned x,
unsigned y);
};
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len);
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len);
struct fpi_line_asmbl_ctx {
unsigned line_width;
unsigned max_height;
unsigned resolution;
unsigned median_filter_size;
unsigned max_search_offset;
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2);
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
GSList *line,
unsigned x);
};
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t lines_len);
#endif

View File

@@ -398,6 +398,9 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_ETES603
&etes603_driver,
#endif
#ifdef ENABLE_VFS0050
&vfs0050_driver,
#endif
/*#ifdef ENABLE_FDU2000
&fdu2000_driver,
#endif

View File

@@ -30,6 +30,7 @@
#include <libusb.h>
#include <assembling.h>
#include <aeslib.h>
#include <fp_internal.h>
@@ -66,6 +67,7 @@ static int adjust_gain(unsigned char *buffer, int status);
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 350
@@ -80,6 +82,13 @@ struct aes1610_dev {
uint8_t blanks_count;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
@@ -404,7 +413,7 @@ static unsigned char list_BE_values[10] = {
/*
* The different possible values for 0xBD register */
static unsigned char list_BD_values[10] = {
0x48, 0x4B, 0x4F, 0x52, 0x57, 0x59, 0x5B
0x28, 0x2b, 0x30, 0x3b, 0x45, 0x49, 0x4B
};
/*
@@ -425,25 +434,25 @@ static int adjust_gain(unsigned char *buffer, int status)
strip_scan_reqs[0].value = 0x6B;
strip_scan_reqs[1].value = 0x06;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x5B;
strip_scan_reqs[3].value = 0x4B;
}
else if (buffer[1] > 0x55) {
strip_scan_reqs[0].value = 0x63;
strip_scan_reqs[1].value = 0x15;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4F;
strip_scan_reqs[3].value = 0x3b;
}
else if (buffer[1] > 0x40 || buffer[16] > 0x19) {
strip_scan_reqs[0].value = 0x43;
strip_scan_reqs[1].value = 0x13;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4B;
strip_scan_reqs[3].value = 0x30;
}
else { // minimum gain needed
strip_scan_reqs[0].value = 0x23;
strip_scan_reqs[1].value = 0x07;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x48;
strip_scan_reqs[3].value = 0x28;
}
// Now copy this values in capture_reqs
@@ -579,11 +588,14 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
}
if (sum > 0) {
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(128 * 4);
memcpy(stripdata, data + 1, 128 * 4);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, FRAME_WIDTH * (FRAME_HEIGHT / 2));
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
aesdev->blanks_count = 0;
}
@@ -609,24 +621,14 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* stop capturing if MAX_FRAMES is reached */
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
struct fp_img *img;
unsigned int height, rev_height;
fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
/* send stop capture bits */
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -809,7 +811,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
@@ -840,9 +842,9 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 50,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,

View File

@@ -28,12 +28,22 @@
#include <fp_internal.h>
#include <assembling.h>
#include <aeslib.h>
#include "aesx660.h"
#include "aes1660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 128
#define SCALE_FACTOR 2
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
@@ -43,7 +53,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
@@ -55,7 +65,8 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
aesdev->init_seqs_len[1] = array_n_elements(aes1660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes1660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes1660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
@@ -102,7 +113,7 @@ struct fp_img_driver aes1660_driver = {
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 70,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,

View File

@@ -28,6 +28,7 @@
#include <libusb.h>
#include <assembling.h>
#include <aeslib.h>
#include <fp_internal.h>
@@ -58,6 +59,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 16
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 150
@@ -72,6 +74,13 @@ struct aes2501_dev {
int no_finger_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
@@ -481,21 +490,13 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
unsigned int height, rev_height;
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
fpi_do_movement_estimation(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -509,7 +510,7 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct aes_stripe *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe));
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
@@ -849,7 +850,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
@@ -881,7 +882,7 @@ struct fp_img_driver aes2501_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.img_width = IMAGE_WIDTH,
.open = dev_init,
.close = dev_deinit,

View File

@@ -28,6 +28,7 @@
#include <libusb.h>
#include <assembling.h>
#include <aeslib.h>
#include <fp_internal.h>
@@ -56,6 +57,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
struct aes2550_dev {
GSList *strips;
@@ -64,6 +66,13 @@ struct aes2550_dev {
int heartbeat_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
/****** FINGER PRESENCE DETECTION ******/
static unsigned char finger_det_reqs[] = {
@@ -204,7 +213,7 @@ static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
struct aes_stripe *stripe;
struct fpi_frame *stripe;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
@@ -215,7 +224,7 @@ static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
if (len != (AES2550_STRIP_SIZE - 3)) {
fp_dbg("Bogus frame len: %.4x\n", len);
}
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bits per pixel */
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 4 bits per pixel */
stripe->delta_x = (int8_t)data[6];
stripe->delta_y = -(int8_t)data[7];
stripdata = stripe->data;
@@ -253,8 +262,9 @@ static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -612,7 +622,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}

View File

@@ -28,11 +28,22 @@
#include <fp_internal.h>
#include <assembling.h>
#include <aeslib.h>
#include "aesx660.h"
#include "aes2660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 192
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
@@ -42,7 +53,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
@@ -55,7 +66,8 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
aesdev->init_seqs_len[1] = array_n_elements(aes2660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes2660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes2660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;

View File

@@ -130,23 +130,24 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
if (!aesdev)
return -ENOMEM;
if (r == 0)
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}

View File

@@ -127,23 +127,24 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
if (!aesdev)
return -ENOMEM;
if (r == 0)
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}

View File

@@ -30,6 +30,7 @@
#include <libusb.h>
#include <assembling.h>
#include <aeslib.h>
#include <fp_internal.h>
@@ -41,7 +42,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FRAME_HEIGHT 8
#define FRAME_HEIGHT AESX660_FRAME_HEIGHT
#define min(a, b) (((a) < (b)) ? (a) : (b))
@@ -273,12 +274,12 @@ enum capture_states {
/* Returns number of processed bytes */
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
{
struct aes_stripe *stripe;
struct fpi_frame *stripe;
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
stripe = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bpp */
stripe = g_malloc(aesdev->assembling_ctx->frame_width * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 4 bpp */
stripdata = stripe->data;
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
@@ -289,7 +290,7 @@ static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
fp_dbg("Offset to previous frame: %d %d", stripe->delta_x, stripe->delta_y);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->frame_width * FRAME_HEIGHT / 2);
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->assembling_ctx->frame_width * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
@@ -311,8 +312,8 @@ static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT, aesdev->frame_width + aesdev->frame_width / 2);
img = fpi_assemble_frames(aesdev->assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= aesdev->extra_img_flags;
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;

View File

@@ -41,6 +41,8 @@
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096
#define AESX660_FRAME_HEIGHT 8
struct aesX660_dev {
GSList *strips;
size_t strips_len;
@@ -55,12 +57,12 @@ struct aesX660_dev {
size_t buffer_max;
/* Device-specific stuff */
int h_scale_factor;
struct aesX660_cmd *init_seqs[2];
size_t init_seqs_len[2];
unsigned char *start_imaging_cmd;
size_t start_imaging_cmd_len;
unsigned int frame_width;
struct fpi_frame_asmbl_ctx *assembling_ctx;
uint16_t extra_img_flags;
};
struct aesX660_cmd {

View File

@@ -40,6 +40,7 @@ enum {
UPEKTC_IMG_ID = 17,
ETES603_ID = 18,
VFS5011_ID = 19,
VFS0050_ID = 20,
};
#endif

View File

@@ -1465,8 +1465,7 @@ static int dev_open(struct fp_img_dev *idev, unsigned long driver_data)
ret = libusb_claim_interface(idev->udev, 0);
if (ret != LIBUSB_SUCCESS) {
fp_err("libusb_claim_interface failed on interface 0 "
"(err=%d)", ret);
fp_err("libusb_claim_interface failed on interface 0: %s", libusb_error_name(ret));
return ret;
}

View File

@@ -264,8 +264,10 @@ gint dev_init(struct fp_img_dev *dev, unsigned long driver_data)
//if ( (r = usb_set_configuration(dev->udev, 1)) < 0 )
// goto out;
if ( (r = libusb_claim_interface(dev->udev, 0)) < 0 )
goto out;
if ( (r = libusb_claim_interface(dev->udev, 0)) < 0 ) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
//if ( (r = usb_set_altinterface(dev->udev, 1)) < 0 )
// goto out;

View File

@@ -864,8 +864,10 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc(sizeof(*upekdev));
upekdev->seq = 0xf0; /* incremented to 0x00 before first cmd */

View File

@@ -5,6 +5,9 @@
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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
@@ -30,17 +33,25 @@
#include <fp_internal.h>
#include <assembling.h>
#include "upeksonly.h"
#include "driver_ids.h"
#define CTRL_TIMEOUT 1000
#define IMG_WIDTH 288
#define NUM_BULK_TRANSFERS 24
#define MAX_ROWS 700
#define MAX_ROWS 2048
#define MIN_ROWS 64
#define BLANK_THRESHOLD 250
#define FINGER_PRESENT_THRESHOLD 32
#define FINGER_REMOVED_THRESHOLD 100
#define DIFF_THRESHOLD 13
enum {
UPEKSONLY_2016,
UPEKSONLY_1000,
UPEKSONLY_2016,
UPEKSONLY_1000,
UPEKSONLY_1001,
};
struct img_transfer_data {
@@ -66,12 +77,19 @@ enum sonly_kill_transfers_action {
EXEC_CALLBACK,
};
enum sonly_fs {
AWAIT_FINGER,
FINGER_DETECTED,
FINGER_REMOVED,
};
struct sonly_dev {
gboolean capturing;
gboolean deactivating;
uint8_t read_reg_result;
int dev_model;
int img_width;
struct fpi_ssm *loopsm;
struct libusb_transfer *img_transfer[NUM_BULK_TRANSFERS];
@@ -85,7 +103,8 @@ struct sonly_dev {
int wraparounds;
int num_blank;
int finger_removed;
int num_nonblank;
enum sonly_fs finger_state;
int last_seqnum;
enum sonly_kill_transfers_action killing_transfers;
@@ -96,9 +115,57 @@ struct sonly_dev {
};
};
struct sonly_regwrite {
uint8_t reg;
uint8_t value;
/* Calculade squared standand deviation of sum of two lines */
static int upeksonly_get_deviation2(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2)
{
unsigned char *buf1 = line1->data, *buf2 = line2->data;
int res = 0, mean = 0, i;
for (i = 0; i < ctx->line_width; i+= 2)
mean += (int)buf1[i + 1] + (int)buf2[i];
mean /= (ctx->line_width / 2);
for (i = 0; i < ctx->line_width; i+= 2) {
int dev = (int)buf1[i + 1] + (int)buf2[i] - mean;
res += dev*dev;
}
return res / (ctx->line_width / 2);
}
static unsigned char upeksonly_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
unsigned char *buf;
unsigned offset;
/* The scans from this device are rolled right by two colums */
if (x < ctx->line_width - 2)
offset = x + 2;
else if ((x > ctx->line_width - 2) && (x < ctx->line_width))
offset = x - (ctx->line_width - 2);
else
return 0;
/* Each 2nd pixel is shifted 2 pixels down */
if ((!(x & 1)) && g_slist_next(row) && g_slist_next(g_slist_next(row)))
buf = g_slist_next(g_slist_next(row))->data;
else
buf = row->data;
return buf[offset];
}
static struct fpi_line_asmbl_ctx assembling_ctx = {
.max_height = 1024,
.resolution = 8,
.median_filter_size = 25,
.max_search_offset = 30,
.get_deviation = upeksonly_get_deviation2,
.get_pixel = upeksonly_get_pixel,
};
/***** IMAGE PROCESSING *****/
@@ -162,36 +229,27 @@ static void cancel_img_transfers(struct fp_img_dev *dev)
static gboolean is_capturing(struct sonly_dev *sdev)
{
return sdev->num_rows < MAX_ROWS && !sdev->finger_removed;
return sdev->num_rows < MAX_ROWS && (sdev->finger_state != FINGER_REMOVED);
}
static void handoff_img(struct fp_img_dev *dev)
{
struct sonly_dev *sdev = dev->priv;
size_t size = IMG_WIDTH * sdev->num_rows;
struct fp_img *img = fpi_img_new(size);
struct fp_img *img;
GSList *elem = sdev->rows;
size_t offset = 0;
if (!elem) {
fp_err("no rows?");
return;
}
sdev->rows = g_slist_reverse(sdev->rows);
fp_dbg("%d rows", sdev->num_rows);
img->height = sdev->num_rows;
img = fpi_assemble_lines(&assembling_ctx, sdev->rows, sdev->num_rows);
/* The scans from this device are rolled right by two colums
* It feels a lot smarter to correct here than mess with it at
* read time*/
do {
memcpy(img->data + offset, elem->data + 2, IMG_WIDTH - 2);
memcpy(img->data + offset + IMG_WIDTH - 2, elem->data, 2);
g_free(elem->data);
offset += IMG_WIDTH;
} while ((elem = g_slist_next(elem)) != NULL);
g_slist_free(sdev->rows);
g_slist_free_full(sdev->rows, g_free);
sdev->rows = NULL;
fpi_imgdev_image_captured(dev, img);
@@ -202,24 +260,6 @@ static void handoff_img(struct fp_img_dev *dev)
cancel_img_transfers(dev);
}
static void compute_rows(unsigned char *a, unsigned char *b, int *diff,
int *total)
{
int i;
int _total = 0;
int _diff = 0;
for (i = 0; i < IMG_WIDTH; i++) {
if (a[i] > b[i])
_diff += a[i] - b[i];
else
_diff += b[i] - a[i];
_total += b[i];
}
*diff = _diff;
*total = _total;
}
static void row_complete(struct fp_img_dev *dev)
{
struct sonly_dev *sdev = dev->priv;
@@ -227,12 +267,39 @@ static void row_complete(struct fp_img_dev *dev)
if (sdev->num_rows > 0) {
unsigned char *lastrow = sdev->rows->data;
int diff;
int total;
int std_sq_dev, mean_sq_diff;
compute_rows(lastrow, sdev->rowbuf, &diff, &total);
std_sq_dev = fpi_std_sq_dev(sdev->rowbuf, sdev->img_width);
mean_sq_diff = fpi_mean_sq_diff_norm(lastrow, sdev->rowbuf, sdev->img_width);
if (total < 52000) {
switch (sdev->finger_state) {
case AWAIT_FINGER:
if (sdev->deactivating) {
sdev->killing_transfers = ITERATE_SSM;
sdev->kill_ssm = sdev->loopsm;
cancel_img_transfers(dev);
}
fp_dbg("std_sq_dev: %d", std_sq_dev);
if (std_sq_dev > BLANK_THRESHOLD) {
sdev->num_nonblank++;
} else {
sdev->num_nonblank = 0;
}
if (sdev->num_nonblank > FINGER_PRESENT_THRESHOLD) {
sdev->finger_state = FINGER_DETECTED;
fpi_imgdev_report_finger_status(dev, TRUE);
} else {
return;
}
break;
case FINGER_DETECTED:
case FINGER_REMOVED:
default:
break;
}
if (std_sq_dev > BLANK_THRESHOLD) {
sdev->num_blank = 0;
} else {
sdev->num_blank++;
@@ -243,20 +310,35 @@ static void row_complete(struct fp_img_dev *dev)
* actual scan. Happens most commonly if scan is started
* from before the first joint resulting in a gap after the inital touch.
*/
if ((sdev->num_blank > 500)
&& ((sdev->num_rows > MIN_ROWS) || (sdev->num_blank > 5000))) {
sdev->finger_removed = 1;
if (sdev->num_blank > FINGER_REMOVED_THRESHOLD) {
sdev->finger_state = FINGER_REMOVED;
fp_dbg("detected finger removal. Blank rows: %d, Full rows: %d", sdev->num_blank, sdev->num_rows);
handoff_img(dev);
return;
}
}
if (diff < 3000)
fp_dbg("mean_sq_diff: %d, std_sq_dev: %d", mean_sq_diff, std_sq_dev);
fp_dbg("num_blank: %d", sdev->num_blank);
if (mean_sq_diff < DIFF_THRESHOLD) {
return;
}
}
sdev->rows = g_slist_prepend(sdev->rows, sdev->rowbuf);
sdev->num_rows++;
switch (sdev->finger_state) {
case AWAIT_FINGER:
if (!sdev->num_rows) {
sdev->rows = g_slist_prepend(sdev->rows, sdev->rowbuf);
sdev->num_rows++;
} else {
return;
}
break;
case FINGER_DETECTED:
case FINGER_REMOVED:
sdev->rows = g_slist_prepend(sdev->rows, sdev->rowbuf);
sdev->num_rows++;
break;
}
sdev->rowbuf = NULL;
if (sdev->num_rows >= MAX_ROWS) {
@@ -272,7 +354,7 @@ static void add_to_rowbuf(struct fp_img_dev *dev, unsigned char *data, int size)
memcpy(sdev->rowbuf + sdev->rowbuf_offset, data, size);
sdev->rowbuf_offset += size;
if (sdev->rowbuf_offset >= IMG_WIDTH)
if (sdev->rowbuf_offset >= sdev->img_width)
row_complete(dev);
}
@@ -280,7 +362,7 @@ static void add_to_rowbuf(struct fp_img_dev *dev, unsigned char *data, int size)
static void start_new_row(struct sonly_dev *sdev, unsigned char *data, int size)
{
if (!sdev->rowbuf)
sdev->rowbuf = g_malloc(IMG_WIDTH);
sdev->rowbuf = g_malloc(sdev->img_width);
memcpy(sdev->rowbuf, data, size);
sdev->rowbuf_offset = size;
}
@@ -294,7 +376,7 @@ static int rowbuf_remaining(struct sonly_dev *sdev)
if (sdev->rowbuf_offset == -1)
return -1;
r = IMG_WIDTH - sdev->rowbuf_offset;
r = sdev->img_width - sdev->rowbuf_offset;
if (r > 62)
r = 62;
return r;
@@ -326,7 +408,7 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data)
/* If possible take the replacement data from last row */
if (sdev->num_rows > 1) {
int row_left = IMG_WIDTH - sdev->rowbuf_offset;
int row_left = sdev->img_width - sdev->rowbuf_offset;
unsigned char *last_row = g_slist_nth_data (sdev->rows, 0);
if (row_left >= 62) {
@@ -345,12 +427,12 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data)
if (for_rowbuf < 62) {
start_new_row(sdev, dummy_data + for_rowbuf, 62 - for_rowbuf);
}
} else if (abs_base_addr % IMG_WIDTH == 0) {
} else if (abs_base_addr % sdev->img_width == 0) {
start_new_row(sdev, dummy_data, 62);
} else {
/* does the data in the packet reside on a row boundary?
* if so capture it */
next_row_addr = ((abs_base_addr / IMG_WIDTH) + 1) * IMG_WIDTH;
next_row_addr = ((abs_base_addr / sdev->img_width) + 1) * sdev->img_width;
diff = next_row_addr - abs_base_addr;
if (diff < 62)
start_new_row(sdev, dummy_data + diff, 62 - diff);
@@ -381,14 +463,14 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data)
}
/* does the packet START on a boundary? if so we want it in full */
if (abs_base_addr % IMG_WIDTH == 0) {
if (abs_base_addr % sdev->img_width == 0) {
start_new_row(sdev, data, 62);
return;
}
/* does the data in the packet reside on a row boundary?
* if so capture it */
next_row_addr = ((abs_base_addr / IMG_WIDTH) + 1) * IMG_WIDTH;
next_row_addr = ((abs_base_addr / sdev->img_width) + 1) * sdev->img_width;
diff = next_row_addr - abs_base_addr;
if (diff < 62)
start_new_row(sdev, data + diff, 62 - diff);
@@ -618,6 +700,7 @@ static void sm_await_intr_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
g_free(transfer->buffer);
@@ -630,6 +713,7 @@ static void sm_await_intr_cb(struct libusb_transfer *transfer)
transfer->buffer[2], transfer->buffer[3]);
g_free(transfer->buffer);
sdev->finger_state = FINGER_DETECTED;
fpi_imgdev_report_finger_status(dev, TRUE);
fpi_ssm_next_state(ssm);
}
@@ -663,51 +747,6 @@ static void sm_await_intr(struct fpi_ssm *ssm)
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
{ 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b },
{ 0x00, 0x67 }, { 0x00, 0x67 },
};
static const struct sonly_regwrite awfsm_1000_writev_1[] = {
/* Initialize sensor settings */
{ 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f },
{ 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d },
{ 0x0e, 0x0e }, { 0x0f, 0x0d },
{ 0x13, 0x05 }, { 0x13, 0x45 },
/* Initialize finger detection registers (not enabling yet) */
{ 0x30, 0xe0 }, { 0x15, 0x26 },
{ 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 },
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite awfsm_1000_writev_2[] = {
/* Enable finger detection */
{ 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 },
};
static const struct sonly_regwrite awfsm_2016_writev_3[] = {
{ 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 },
{ 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 },
};
static const struct sonly_regwrite awfsm_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
/* enter finger detection mode */
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
{ 0x15, 0x84 },
};
enum awfsm_2016_states {
AWFSM_2016_WRITEV_1,
AWFSM_2016_READ_01,
@@ -789,16 +828,6 @@ static void awfsm_1000_run_state(struct fpi_ssm *ssm)
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
enum capsm_2016_states {
CAPSM_2016_INIT,
CAPSM_2016_WRITE_15,
@@ -815,6 +844,17 @@ enum capsm_1000_states {
CAPSM_1000_NUM_STATES,
};
enum capsm_1001_states {
CAPSM_1001_INIT,
CAPSM_1001_FIRE_BULK,
CAPSM_1001_WRITEV_1,
CAPSM_1001_WRITEV_2,
CAPSM_1001_WRITEV_3,
CAPSM_1001_WRITEV_4,
CAPSM_1001_WRITEV_5,
CAPSM_1001_NUM_STATES,
};
static void capsm_fire_bulk(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
@@ -856,7 +896,8 @@ static void capsm_2016_run_state(struct fpi_ssm *ssm)
sdev->num_rows = 0;
sdev->wraparounds = -1;
sdev->num_blank = 0;
sdev->finger_removed = 0;
sdev->num_nonblank = 0;
sdev->finger_state = FINGER_DETECTED;
sdev->last_seqnum = 16383;
sdev->killing_transfers = 0;
fpi_ssm_next_state(ssm);
@@ -887,7 +928,8 @@ static void capsm_1000_run_state(struct fpi_ssm *ssm)
sdev->num_rows = 0;
sdev->wraparounds = -1;
sdev->num_blank = 0;
sdev->finger_removed = 0;
sdev->num_nonblank = 0;
sdev->finger_state = FINGER_DETECTED;
sdev->last_seqnum = 16383;
sdev->killing_transfers = 0;
fpi_ssm_next_state(ssm);
@@ -901,20 +943,46 @@ static void capsm_1000_run_state(struct fpi_ssm *ssm)
}
}
static void capsm_1001_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case CAPSM_1001_INIT:
sdev->rowbuf_offset = -1;
sdev->num_rows = 0;
sdev->wraparounds = -1;
sdev->num_blank = 0;
sdev->num_nonblank = 0;
sdev->finger_state = AWAIT_FINGER;
sdev->last_seqnum = 16383;
sdev->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_1001_FIRE_BULK: ;
capsm_fire_bulk (ssm);
break;
case CAPSM_1001_WRITEV_1:
sm_write_regs(ssm, capsm_1001_writev_1, G_N_ELEMENTS(capsm_1001_writev_1));
break;
case CAPSM_1001_WRITEV_2:
sm_write_regs(ssm, capsm_1001_writev_2, G_N_ELEMENTS(capsm_1001_writev_2));
break;
case CAPSM_1001_WRITEV_3:
sm_write_regs(ssm, capsm_1001_writev_3, G_N_ELEMENTS(capsm_1001_writev_3));
break;
case CAPSM_1001_WRITEV_4:
sm_write_regs(ssm, capsm_1001_writev_4, G_N_ELEMENTS(capsm_1001_writev_4));
break;
case CAPSM_1001_WRITEV_5:
sm_write_regs(ssm, capsm_1001_writev_5, G_N_ELEMENTS(capsm_1001_writev_5));
break;
}
}
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
static const struct sonly_regwrite deinitsm_1000_writev[] = {
{ 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */
{ 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */
};
enum deinitsm_2016_states {
DEINITSM_2016_WRITEV,
DEINITSM_2016_NUM_STATES,
@@ -925,6 +993,11 @@ enum deinitsm_1000_states {
DEINITSM_1000_NUM_STATES,
};
enum deinitsm_1001_states {
DEINITSM_1001_WRITEV,
DEINITSM_1001_NUM_STATES,
};
static void deinitsm_2016_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
@@ -943,34 +1016,17 @@ static void deinitsm_1000_run_state(struct fpi_ssm *ssm)
}
}
static void deinitsm_1001_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case DEINITSM_1001_WRITEV:
sm_write_regs(ssm, deinitsm_1001_writev, G_N_ELEMENTS(deinitsm_1001_writev));
break;
}
}
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
* thought this was some kind of clever authentication, but just blasting
* these sniffed values each time seems to work. */
{ 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf },
{ 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae },
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x49, 0x00 }, /* Encryption disabled */
/* Setting encryption key. Doesn't need to be random since we don't use any
* encryption. */
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x04, 0x00 }, { 0x05, 0x00 },
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
enum initsm_2016_states {
INITSM_2016_WRITEV_1,
INITSM_2016_READ_09,
@@ -987,6 +1043,15 @@ enum initsm_1000_states {
INITSM_1000_NUM_STATES,
};
enum initsm_1001_states {
INITSM_1001_WRITEV_1,
INITSM_1001_WRITEV_2,
INITSM_1001_WRITEV_3,
INITSM_1001_WRITEV_4,
INITSM_1001_WRITEV_5,
INITSM_1001_NUM_STATES,
};
static void initsm_2016_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
@@ -1026,6 +1091,27 @@ static void initsm_1000_run_state(struct fpi_ssm *ssm)
}
}
static void initsm_1001_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case INITSM_1001_WRITEV_1:
sm_write_regs(ssm, initsm_1001_writev_1, G_N_ELEMENTS(initsm_1001_writev_1));
break;
case INITSM_1001_WRITEV_2:
sm_write_regs(ssm, initsm_1001_writev_2, G_N_ELEMENTS(initsm_1001_writev_2));
break;
case INITSM_1001_WRITEV_3:
sm_write_regs(ssm, initsm_1001_writev_3, G_N_ELEMENTS(initsm_1001_writev_3));
break;
case INITSM_1001_WRITEV_4:
sm_write_regs(ssm, initsm_1001_writev_4, G_N_ELEMENTS(initsm_1001_writev_4));
break;
case INITSM_1001_WRITEV_5:
sm_write_regs(ssm, initsm_1001_writev_5, G_N_ELEMENTS(initsm_1001_writev_5));
break;
}
}
/***** CAPTURE LOOP *****/
enum loopsm_states {
@@ -1045,26 +1131,44 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
switch (ssm->cur_state) {
case LOOPSM_RUN_AWFSM: ;
if (sdev->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
struct fpi_ssm *awfsm = NULL;
switch (sdev->dev_model) {
case UPEKSONLY_2016:
awfsm = fpi_ssm_new(dev->dev, awfsm_2016_run_state,
AWFSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
awfsm = fpi_ssm_new(dev->dev, awfsm_1000_run_state,
AWFSM_1000_NUM_STATES);
break;
switch (sdev->dev_model) {
case UPEKSONLY_1001:
if (sdev->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_next_state(ssm);
}
awfsm->priv = dev;
fpi_ssm_start_subsm(ssm, awfsm);
break;
default:
if (sdev->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
struct fpi_ssm *awfsm = NULL;
switch (sdev->dev_model) {
case UPEKSONLY_2016:
awfsm = fpi_ssm_new(dev->dev, awfsm_2016_run_state,
AWFSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
awfsm = fpi_ssm_new(dev->dev, awfsm_1000_run_state,
AWFSM_1000_NUM_STATES);
break;
}
awfsm->priv = dev;
fpi_ssm_start_subsm(ssm, awfsm);
}
break;
}
break;
case LOOPSM_AWAIT_FINGER:
sm_await_intr(ssm);
switch (sdev->dev_model) {
case UPEKSONLY_1001:
fpi_ssm_next_state(ssm);
break;
default:
sm_await_intr(ssm);
break;
}
break;
case LOOPSM_RUN_CAPSM: ;
struct fpi_ssm *capsm = NULL;
@@ -1077,13 +1181,15 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
capsm = fpi_ssm_new(dev->dev, capsm_1000_run_state,
CAPSM_1000_NUM_STATES);
break;
case UPEKSONLY_1001:
capsm = fpi_ssm_new(dev->dev, capsm_1001_run_state,
CAPSM_1001_NUM_STATES);
break;
}
capsm->priv = dev;
fpi_ssm_start_subsm(ssm, capsm);
break;
case LOOPSM_CAPTURE:
/* bulk URBs already flying, so just wait for image completion
* to push us into next state */
break;
case LOOPSM_RUN_DEINITSM: ;
struct fpi_ssm *deinitsm = NULL;
@@ -1096,6 +1202,10 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
deinitsm = fpi_ssm_new(dev->dev, deinitsm_1000_run_state,
DEINITSM_1000_NUM_STATES);
break;
case UPEKSONLY_1001:
deinitsm = fpi_ssm_new(dev->dev, deinitsm_1001_run_state,
DEINITSM_1001_NUM_STATES);
break;
}
sdev->capturing = FALSE;
deinitsm->priv = dev;
@@ -1212,6 +1322,9 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
case UPEKSONLY_1000:
ssm = fpi_ssm_new(dev->dev, initsm_1000_run_state, INITSM_1000_NUM_STATES);
break;
case UPEKSONLY_1001:
ssm = fpi_ssm_new(dev->dev, initsm_1001_run_state, INITSM_1001_NUM_STATES);
break;
}
ssm->priv = dev;
fpi_ssm_start(ssm, initsm_complete);
@@ -1221,6 +1334,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct sonly_dev *sdev;
r = libusb_set_configuration(dev->udev, 1);
if (r < 0) {
@@ -1230,12 +1344,30 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct sonly_dev));
((struct sonly_dev*)dev->priv)->dev_model = (int)driver_data;
sdev = dev->priv = g_malloc0(sizeof(struct sonly_dev));
sdev->dev_model = (int)driver_data;
switch (driver_data) {
case UPEKSONLY_1000:
sdev->img_width = IMG_WIDTH_1000;
upeksonly_driver.img_width = IMG_WIDTH_1000;
assembling_ctx.line_width = IMG_WIDTH_1000;
break;
case UPEKSONLY_1001:
sdev->img_width = IMG_WIDTH_1001;
upeksonly_driver.img_width = IMG_WIDTH_1001;
upeksonly_driver.bz3_threshold = 25;
assembling_ctx.line_width = IMG_WIDTH_1001;
break;
case UPEKSONLY_2016:
sdev->img_width = IMG_WIDTH_2016;
upeksonly_driver.img_width = IMG_WIDTH_2016;
assembling_ctx.line_width = IMG_WIDTH_2016;
break;
}
fpi_imgdev_open_complete(dev, 0);
return 0;
}
@@ -1258,12 +1390,16 @@ static int dev_discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
return 1;
}
if (dsc->idProduct == 0x1001)
return 1;
return 0;
}
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKSONLY_2016 },
{ .vendor = 0x147e, .product = 0x1000, .driver_data = UPEKSONLY_1000 },
{ .vendor = 0x147e, .product = 0x1001, .driver_data = UPEKSONLY_1001 },
{ 0, 0, 0, },
};
@@ -1277,7 +1413,7 @@ struct fp_img_driver upeksonly_driver = {
.discover = dev_discover,
},
.flags = 0,
.img_width = IMG_WIDTH,
.img_width = -1,
.img_height = -1,
.open = dev_init,

View File

@@ -0,0 +1,319 @@
/*
* UPEK TouchStrip Sensor-Only driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 IMG_WIDTH_2016 288
#define IMG_WIDTH_1000 288
#define IMG_WIDTH_1001 216
struct sonly_regwrite {
uint8_t reg;
uint8_t value;
};
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
{ 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b },
{ 0x00, 0x67 }, { 0x00, 0x67 },
};
static const struct sonly_regwrite awfsm_1000_writev_1[] = {
/* Initialize sensor settings */
{ 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f },
{ 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d },
{ 0x0e, 0x0e }, { 0x0f, 0x0d },
{ 0x13, 0x05 }, { 0x13, 0x45 },
/* Initialize finger detection registers (not enabling yet) */
{ 0x30, 0xe0 }, { 0x15, 0x26 },
{ 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 },
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite awfsm_1000_writev_2[] = {
/* Enable finger detection */
{ 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 },
};
static const struct sonly_regwrite awfsm_2016_writev_3[] = {
{ 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 },
{ 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 },
};
static const struct sonly_regwrite awfsm_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
/* enter finger detection mode */
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
{ 0x15, 0x84 },
};
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
static const struct sonly_regwrite capsm_1001_writev_1[] = {
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x05 },
};
static const struct sonly_regwrite capsm_1001_writev_2[] = {
{ 0x4d, 0xc0 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x0b, 0x00 },
{ 0x04, 0x00 },
{ 0x05, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x08 },
};
static const struct sonly_regwrite capsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x00, 0x5f }, { 0x01, 0xee },
{ 0x03, 0x2c },
{ 0x07, 0x00 }, { 0x08, 0x00 }, { 0x09, 0x29 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x10, 0x00 }, { 0x11, 0x8f }, { 0x12, 0x01 }, { 0x13, 0x45 },
{ 0x15, 0x26 },
{ 0x1e, 0x02 },
{ 0x20, 0x01 },
{ 0x25, 0x8f },
{ 0x27, 0x23 },
{ 0x30, 0xe0 },
{ 0x07, 0x10 },
{ 0x09, 0x21 },
{ 0x13, 0x75 },
{ 0x0b, 0x80 },
};
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
static const struct sonly_regwrite deinitsm_1000_writev[] = {
{ 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */
{ 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */
};
static const struct sonly_regwrite deinitsm_1001_writev[] = {
{ 0x0b, 0x00 },
{ 0x13, 0x45 },
{ 0x09, 0x29 },
{ 0x1a, 0x00 },
};
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
* thought this was some kind of clever authentication, but just blasting
* these sniffed values each time seems to work. */
{ 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf },
{ 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae },
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x49, 0x00 }, /* Encryption disabled */
/* Setting encryption key. Doesn't need to be random since we don't use any
* encryption. */
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x04, 0x00 }, { 0x05, 0x00 },
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
static const struct sonly_regwrite initsm_1001_writev_1[] = {
{ 0x4a, 0x9d },
{ 0x4f, 0x06 },
{ 0x4f, 0x05 },
{ 0x4f, 0x04 },
{ 0x4a, 0x9c },
{ 0x3e, 0xa6 },
{ 0x3e, 0x01 },
{ 0x3e, 0x68 },
{ 0x3e, 0xfd },
{ 0x3e, 0x72 },
{ 0x3e, 0xef },
{ 0x3e, 0x5d },
{ 0x3e, 0xc5 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0x1f }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_2[] = {
{ 0x4c, 0x03 }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0xff }, { 0x4d, 0xc0 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x09, 0x27 },
{ 0x1a, 0x02 },
{ 0x49, 0x01 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x49, 0x00 },
{ 0x3e, 0x90 },
{ 0x3e, 0xbd },
{ 0x3e, 0xbf },
{ 0x3e, 0x48 },
{ 0x3e, 0x2a },
{ 0x3e, 0xe3 },
{ 0x3e, 0xd2 },
{ 0x3e, 0x58 },
{ 0x09, 0x2f },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x03 },
};
static const struct sonly_regwrite initsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
};

View File

@@ -441,7 +441,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}

View File

@@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upekts_img"
#define FP_COMPONENT "upektc_img"
#include <errno.h>
#include <string.h>
@@ -46,7 +46,7 @@ static void start_deactivation(struct fp_img_dev *dev);
#define MAX_RESPONSE_SIZE 2052
#define SHORT_RESPONSE_SIZE 64
struct upekts_img_dev {
struct upektc_img_dev {
unsigned char cmd[MAX_CMD_SIZE];
unsigned char response[MAX_RESPONSE_SIZE];
unsigned char image_bits[IMAGE_SIZE * 2];
@@ -126,7 +126,7 @@ static void upektc_img_submit_req(struct fpi_ssm *ssm,
libusb_transfer_cb_fn cb)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
@@ -157,7 +157,7 @@ static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t bu
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
int r;
if (!transfer) {
@@ -184,9 +184,11 @@ static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t bu
enum capture_states {
CAPTURE_INIT_CAPTURE,
CAPTURE_READ_DATA,
CAPTURE_READ_DATA_TERM,
CAPTURE_ACK_00_28,
CAPTURE_ACK_08,
CAPTURE_ACK_FRAME,
CAPTURE_ACK_00_28_TERM,
CAPTURE_NUM_STATES,
};
@@ -194,11 +196,18 @@ static void capture_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
} else {
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
switch (ssm->cur_state) {
case CAPTURE_ACK_00_28_TERM:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA_TERM);
break;
default:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
}
@@ -224,7 +233,7 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
unsigned char *data = upekdev->response;
struct fp_img *img;
size_t response_size;
@@ -243,7 +252,13 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length == 0) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
fpi_ssm_jump_to_state(ssm, ssm->cur_state);
return;
}
if (ssm->cur_state == CAPTURE_READ_DATA_TERM) {
fp_dbg("Terminating SSM\n");
fpi_ssm_mark_completed(ssm);
return;
}
@@ -277,10 +292,26 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
/* finger is present! */
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
break;
case 0x1e:
/* short scan */
fp_err("short scan, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
case 0x1d:
/* too much horisontal movement */
fp_err("too much horisontal movement, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_CENTER_FINGER);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
default:
/* some error happened, cancel scan */
fp_err("something bad happened, aborting scan :(\n");
fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY_REMOVE_FINGER);
fp_err("something bad happened, stop scan\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
}
break;
@@ -302,6 +333,7 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
BUG_ON(upekdev->image_size != IMAGE_SIZE);
fp_dbg("Image size is %d\n", upekdev->image_size);
img = fpi_img_new(IMAGE_SIZE);
img->flags = FP_IMG_PARTIAL;
memcpy(img->data, upekdev->image_bits, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
@@ -325,7 +357,7 @@ static void capture_read_data_cb(struct libusb_transfer *transfer)
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
switch (ssm->cur_state) {
case CAPTURE_INIT_CAPTURE:
@@ -334,6 +366,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
upekdev->seq++;
break;
case CAPTURE_READ_DATA:
case CAPTURE_READ_DATA_TERM:
if (!upekdev->response_rest)
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
else
@@ -341,6 +374,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
SHORT_RESPONSE_SIZE, capture_read_data_cb);
break;
case CAPTURE_ACK_00_28:
case CAPTURE_ACK_00_28_TERM:
upektc_img_submit_req(ssm, upek2020_ack_00_28, sizeof(upek2020_ack_00_28),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
@@ -360,7 +394,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
int err = ssm->error;
fp_dbg("Capture completed, %d", err);
@@ -376,7 +410,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
upekdev->image_size = 0;
@@ -421,7 +455,7 @@ static void deactivate_read_data_cb(struct libusb_transfer *transfer)
static void deactivate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
switch (ssm->cur_state) {
case DEACTIVATE_DEINIT:
@@ -438,7 +472,7 @@ static void deactivate_run_state(struct fpi_ssm *ssm)
static void deactivate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
int err = ssm->error;
fp_dbg("Deactivate completed");
@@ -455,7 +489,7 @@ static void deactivate_sm_complete(struct fpi_ssm *ssm)
static void start_deactivation(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
upekdev->image_size = 0;
@@ -520,7 +554,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
{
struct libusb_transfer *transfer;
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
@@ -594,7 +628,7 @@ static void activate_sm_complete(struct fpi_ssm *ssm)
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
@@ -605,7 +639,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
struct upektc_img_dev *upekdev = dev->priv;
upekdev->deactivating = TRUE;
}
@@ -617,11 +651,11 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct upekts_img_dev));
dev->priv = g_malloc0(sizeof(struct upektc_img_dev));
fpi_imgdev_open_complete(dev, 0);
return 0;
}
@@ -665,7 +699,7 @@ struct fp_img_driver upektc_img_driver = {
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 70,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,

View File

@@ -875,8 +875,10 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc(sizeof(*upekdev));
upekdev->seq = 0xf0; /* incremented to 0x00 before first cmd */

View File

@@ -45,6 +45,8 @@
#define IMAGE_HEIGHT 290
#define IMAGE_WIDTH 384
#define ENC_THRESHOLD 1000
enum {
IRQDATA_SCANPWR_ON = 0x56aa,
IRQDATA_FINGER_ON = 0x0101,
@@ -664,6 +666,34 @@ static uint32_t do_decode(uint8_t *data, int num_bytes, uint32_t key)
return update_key(key);
}
static int calc_dev2(struct uru4k_image *img)
{
uint8_t *b[2] = { NULL, NULL };
int res = 0, mean = 0, i, r, j, idx;
for (i = r = idx = 0; i < array_n_elements(img->block_info) && idx < 2; i++) {
if (img->block_info[i].flags & BLOCKF_NOT_PRESENT)
continue;
for (j = 0; j < img->block_info[i].num_lines && idx < 2; j++)
b[idx++] = img->data[r++];
}
if (!b[0] || !b[1]) {
fp_dbg("NULL! %p %p", b[0], b[1]);
return 0;
}
for (i = 0; i < IMAGE_WIDTH; i++)
mean += (int)b[0][i] + (int)b[1][i];
mean /= IMAGE_WIDTH;
for (i = 0; i < IMAGE_WIDTH; i++) {
int dev = (int)b[0][i] + (int)b[1][i] - mean;
res += dev * dev;
}
return res / IMAGE_WIDTH;
}
static void imaging_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
@@ -672,7 +702,7 @@ static void imaging_run_state(struct fpi_ssm *ssm)
struct fp_img *fpimg;
uint32_t key;
uint8_t flags, num_lines;
int i, r, to;
int i, r, to, dev2;
char buf[5];
switch (ssm->cur_state) {
@@ -698,8 +728,13 @@ static void imaging_run_state(struct fpi_ssm *ssm)
return;
}
if (!urudev->profile->encryption) {
fpi_ssm_jump_to_state(ssm, IMAGING_REPORT_IMAGE);
return;
dev2 = calc_dev2(img);
fp_dbg("dev2: %d", dev2);
if (dev2 < ENC_THRESHOLD) {
fpi_ssm_jump_to_state(ssm, IMAGING_REPORT_IMAGE);
return;
}
fp_info("image seems to be encrypted");
}
buf[0] = img->key_number;
buf[1] = urudev->img_enc_seed;
@@ -1285,7 +1320,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, iface_desc->bInterfaceNumber);
if (r < 0) {
fp_err("interface claim failed");
fp_err("interface claim failed: %s", libusb_error_name(r));
goto out;
}

View File

@@ -348,7 +348,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
if (r == 0)
fpi_imgdev_open_complete(dev, 0);

791
libfprint/drivers/vfs0050.c Normal file
View File

@@ -0,0 +1,791 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@gmail.com>
*
* 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 "vfs0050"
#include <errno.h>
#include <string.h>
#include <fp_internal.h>
#include <assembling.h>
#include "driver_ids.h"
#include "vfs0050.h"
/* USB functions */
/* Callback for async_write */
static void async_write_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = ssm->priv;
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Written only %d of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Send data to EP1, the only out endpoint */
static void async_write(struct fpi_ssm *ssm, void *data, int len)
{
struct fp_img_dev *idev = ssm->priv;
struct libusb_device_handle *udev = idev->udev;
struct vfs_dev_t *vdev = idev->priv;
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, udev, 0x01, data, len,
async_write_callback, ssm, VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_read_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = ssm->priv;
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
int ep = transfer->endpoint;
if (error != 0) {
fp_err("USB read transfer on endpoint %d: %s", ep - 0x80,
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Received %d instead of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Receive data from the given ep and compare with expected */
static void async_read(struct fpi_ssm *ssm, int ep, void *data, int len)
{
struct fp_img_dev *idev = ssm->priv;
struct libusb_device_handle *udev = idev->udev;
struct vfs_dev_t *vdev = idev->priv;
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, udev, ep, data,
len, async_read_callback, ssm,
VFS_USB_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, udev, ep, data, len,
async_read_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_abort_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = ssm->priv;
int transferred = transfer->actual_length, error = transfer->status;
int ep = transfer->endpoint;
/* In normal case endpoint is empty */
if (error == LIBUSB_TRANSFER_TIMED_OUT) {
fpi_ssm_next_state(ssm);
return;
}
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Don't stop process, only print warning */
if (transferred > 0)
fp_warn("Endpoint %d had extra %d bytes", ep - 0x80,
transferred);
fpi_ssm_jump_to_state(ssm, ssm->cur_state);
}
/* Receive data from the given ep and compare with expected */
static void async_abort(struct fpi_ssm *ssm, int ep)
{
struct fp_img_dev *idev = ssm->priv;
struct libusb_device_handle *udev = idev->udev;
struct vfs_dev_t *vdev = idev->priv;
int len = VFS_USB_BUFFER_SIZE;
unsigned char *data = g_malloc(VFS_USB_BUFFER_SIZE);
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |=
LIBUSB_TRANSFER_FREE_TRANSFER | LIBUSB_TRANSFER_FREE_BUFFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, udev, ep, data,
len, async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, udev, ep, data, len,
async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Image processing functions */
/* Pixel getter for fpi_assemble_lines */
static unsigned char vfs0050_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList * line, unsigned int x)
{
return ((struct vfs_line *)line->data)->data[x];
}
/* Deviation getter for fpi_assemble_lines */
static int vfs0050_get_difference(struct fpi_line_asmbl_ctx *ctx,
GSList * line_list_1, GSList * line_list_2)
{
struct vfs_line *line1 = line_list_1->data;
struct vfs_line *line2 = line_list_2->data;
const int shift = (VFS_IMAGE_WIDTH - VFS_NEXT_LINE_WIDTH) / 2 - 1;
int res = 0;
for (int i = 0; i < VFS_NEXT_LINE_WIDTH; ++i) {
int x =
(int)line1->next_line_part[i] - (int)line2->data[shift + i];
res += x * x;
}
return res;
}
#define VFS_NOISE_THRESHOLD 40
/* Checks whether line is noise or not using hardware parameters */
static char is_noise(struct vfs_line *line)
{
int val1 = line->noise_hash_1;
int val2 = line->noise_hash_2;
if (val1 > VFS_NOISE_THRESHOLD
&& val1 < 256 - VFS_NOISE_THRESHOLD
&& val2 > VFS_NOISE_THRESHOLD && val2 < 256 - VFS_NOISE_THRESHOLD)
return 1;
return 0;
}
/* Parameters for fpi_assemble_lines */
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS_IMAGE_WIDTH,
.max_height = VFS_MAX_HEIGHT,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 100,
.get_deviation = vfs0050_get_difference,
.get_pixel = vfs0050_get_pixel,
};
/* Processes image before submitting */
static struct fp_img *prepare_image(struct vfs_dev_t *vdev)
{
int height = vdev->bytes / VFS_LINE_SIZE;
/* Noise cleaning. IMHO, it works pretty well
I've not detected cases when it doesn't work or cuts a part of the finger
Noise arises at the end of scan when some water remains on the scanner */
while (height > 0) {
if (!is_noise(vdev->lines_buffer + height - 1))
break;
--height;
}
if (height > VFS_MAX_HEIGHT)
height = VFS_MAX_HEIGHT;
/* If image is not good enough */
if (height < VFS_IMAGE_WIDTH)
return NULL;
/* Building GSList */
GSList *lines = NULL;
for (int i = height - 1; i >= 0; --i)
lines = g_slist_prepend(lines, vdev->lines_buffer + i);
/* Perform line assembling */
struct fp_img *img = fpi_assemble_lines(&assembling_ctx, lines, height);
g_slist_free(lines);
return img;
}
/* Processes and submits image after fingerprint received */
static void submit_image(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = idev->priv;
/* We were not asked to submit image actually */
if (!vdev->active)
return;
struct fp_img *img = prepare_image(vdev);
if (!img)
fpi_imgdev_abort_scan(idev, FP_VERIFY_RETRY_TOO_SHORT);
else
fpi_imgdev_image_captured(idev, img);
/* Finger not on the scanner */
fpi_imgdev_report_finger_status(idev, 0);
}
/* Proto functions */
/* SSM loop for clear_ep2 */
static void clear_ep2_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
short result;
char command04 = 0x04;
switch (ssm->cur_state) {
case SUBSM1_COMMAND_04:
async_write(ssm, &command04, sizeof(command04));
break;
case SUBSM1_RETURN_CODE:
async_read(ssm, 1, &result, sizeof(result));
break;
case SUBSM1_ABORT_2:
async_abort(ssm, 2);
break;
default:
fp_err("Unknown SUBSM1 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Send command to clear EP2 */
static void clear_ep2(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
struct fpi_ssm *subsm =
fpi_ssm_new(idev->dev, clear_ep2_ssm, SUBSM1_STATES);
subsm->priv = idev;
fpi_ssm_start_subsm(ssm, subsm);
}
static void send_control_packet_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
struct vfs_dev_t *vdev = idev->priv;
short result;
unsigned char *commit_result = NULL;
switch (ssm->cur_state) {
case SUBSM2_SEND_CONTROL:
async_write(ssm, vdev->control_packet, VFS_CONTROL_PACKET_SIZE);
break;
case SUBSM2_RETURN_CODE:
async_read(ssm, 1, &result, sizeof(result));
break;
case SUBSM2_SEND_COMMIT:
/* next_receive_* packets could be sent only in pair */
if (vdev->control_packet == next_receive_1) {
vdev->control_packet = next_receive_2;
fpi_ssm_jump_to_state(ssm, SUBSM2_SEND_CONTROL);
break;
}
/* commit_out in Windows differs in each commit, but I send the same each time */
async_write(ssm, commit_out, sizeof(commit_out));
break;
case SUBSM2_COMMIT_RESPONSE:
commit_result = g_malloc(VFS_COMMIT_RESPONSE_SIZE);
async_read(ssm, 1, commit_result, VFS_COMMIT_RESPONSE_SIZE);
break;
case SUBSM2_READ_EMPTY_INTERRUPT:
/* I don't know how to check result, it could be different */
g_free(commit_result);
async_read(ssm, 3, vdev->interrupt, VFS_INTERRUPT_SIZE);
break;
case SUBSM2_ABORT_3:
/* Check that interrupt is empty */
if (memcmp
(vdev->interrupt, empty_interrupt, VFS_INTERRUPT_SIZE)) {
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
break;
}
async_abort(ssm, 3);
break;
case SUBSM2_CLEAR_EP2:
/* After turn_on Windows doesn't clear EP2 */
if (vdev->control_packet != turn_on)
clear_ep2(ssm);
else
fpi_ssm_next_state(ssm);
break;
default:
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Send device state control packet */
static void send_control_packet(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
struct fpi_ssm *subsm =
fpi_ssm_new(idev->dev, send_control_packet_ssm, SUBSM2_STATES);
subsm->priv = idev;
fpi_ssm_start_subsm(ssm, subsm);
}
/* Clears all fprint data */
static void clear_data(struct vfs_dev_t *vdev)
{
g_free(vdev->lines_buffer);
vdev->lines_buffer = NULL;
vdev->memory = vdev->bytes = 0;
}
/* After receiving interrupt from EP3 */
static void interrupt_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = ssm->priv;
struct vfs_dev_t *vdev = idev->priv;
char *interrupt = vdev->interrupt;
int error = transfer->status, transferred = transfer->actual_length;
vdev->wait_interrupt = 0;
/* When we have cancelled transfer, error is ok actually */
if (!vdev->active && error == LIBUSB_TRANSFER_CANCELLED)
return;
if (error != 0) {
fp_err("USB read interrupt transfer: %s",
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Interrupt size is VFS_INTERRUPT_SIZE bytes in all known cases */
if (transferred != VFS_INTERRUPT_SIZE) {
fp_err("Unknown interrupt size %d", transferred);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Standard interrupts */
if (memcmp(interrupt, interrupt1, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt2, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt3, VFS_INTERRUPT_SIZE) == 0) {
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* When finger is on the scanner before turn_on */
if (interrupt[0] == 0x01) {
fp_warn("Finger is already on the scanner");
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* Unknown interrupt; abort the session */
fp_err("Unknown interrupt '%02x:%02x:%02x:%02x:%02x'!",
interrupt[0] & 0xff, interrupt[1] & 0xff, interrupt[2] & 0xff,
interrupt[3] & 0xff, interrupt[4] & 0xff);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
static void receive_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = ssm->priv;
struct vfs_dev_t *vdev = idev->priv;
int transferred = transfer->actual_length, error = transfer->status;
if (error != 0 && error != LIBUSB_TRANSFER_TIMED_OUT) {
fp_err("USB read transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Check if fingerprint data is over */
if (transferred == 0) {
fpi_ssm_next_state(ssm);
} else {
vdev->bytes += transferred;
/* We need more data */
fpi_ssm_jump_to_state(ssm, ssm->cur_state);
}
}
/* Stub to keep SSM alive when waiting an interrupt */
static void wait_interrupt(void *data)
{
struct fpi_ssm *ssm = data;
struct fp_img_dev *idev = ssm->priv;
struct vfs_dev_t *vdev = idev->priv;
/* Keep sleeping while this flag is on */
if (vdev->wait_interrupt)
fpi_ssm_jump_to_state(ssm, ssm->cur_state);
}
/* SSM stub to prepare device to another scan after orange light was on */
static void another_scan(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_jump_to_state(ssm, SSM_TURN_ON);
}
/* Another SSM stub to continue after waiting for probable vdev->active changes */
static void scan_completed(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/* Main SSM loop */
static void activate_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
struct libusb_device_handle *udev = idev->udev;
struct vfs_dev_t *vdev = idev->priv;
switch (ssm->cur_state) {
case SSM_INITIAL_ABORT_1:
async_abort(ssm, 1);
break;
case SSM_INITIAL_ABORT_2:
async_abort(ssm, 2);
break;
case SSM_INITIAL_ABORT_3:
async_abort(ssm, 3);
break;
case SSM_CLEAR_EP2:
clear_ep2(ssm);
break;
case SSM_TURN_OFF:
/* Set control_packet argument */
vdev->control_packet = turn_off;
send_control_packet(ssm);
break;
case SSM_TURN_ON:
if (!vdev->active) {
/* The only correct exit */
fpi_ssm_mark_completed(ssm);
if (vdev->need_report) {
fpi_imgdev_deactivate_complete(idev);
vdev->need_report = 0;
}
break;
}
/* Set control_packet argument */
vdev->control_packet = turn_on;
send_control_packet(ssm);
break;
case SSM_ASK_INTERRUPT:
/* Activated, light must be blinking now */
/* If we first time here, report that activate completed */
if (vdev->need_report) {
fpi_imgdev_activate_complete(idev, 0);
vdev->need_report = 0;
}
/* Asyncronously enquire an interrupt */
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_interrupt_transfer(vdev->transfer, udev, 0x83,
vdev->interrupt,
VFS_INTERRUPT_SIZE,
interrupt_callback, ssm, 0);
libusb_submit_transfer(vdev->transfer);
/* This flag could be turned off only in callback function */
vdev->wait_interrupt = 1;
/* I've put it here to be sure that data is cleared */
clear_data(vdev);
fpi_ssm_next_state(ssm);
break;
case SSM_WAIT_INTERRUPT:
/* Check if user had interrupted the process */
if (!vdev->active) {
libusb_cancel_transfer(vdev->transfer);
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
if (vdev->wait_interrupt)
fpi_timeout_add(VFS_SSM_TIMEOUT, wait_interrupt, ssm);
break;
case SSM_RECEIVE_FINGER:
if (vdev->memory == 0) {
/* Initialize fingerprint buffer */
g_free(vdev->lines_buffer);
vdev->memory = VFS_USB_BUFFER_SIZE;
vdev->lines_buffer = g_malloc(vdev->memory);
vdev->bytes = 0;
/* Finger is on the scanner */
fpi_imgdev_report_finger_status(idev, 1);
}
/* Increase buffer size while it's insufficient */
while (vdev->bytes + VFS_USB_BUFFER_SIZE > vdev->memory) {
vdev->memory <<= 1;
vdev->lines_buffer =
(struct vfs_line *)g_realloc(vdev->lines_buffer,
vdev->memory);
}
/* Receive chunk of data */
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, udev, 0x82,
(void *)vdev->lines_buffer +
vdev->bytes, VFS_USB_BUFFER_SIZE,
receive_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
break;
case SSM_SUBMIT_IMAGE:
submit_image(idev);
clear_data(vdev);
/* Wait for probable vdev->active changing */
fpi_timeout_add(VFS_SSM_TIMEOUT, scan_completed, ssm);
break;
case SSM_NEXT_RECEIVE:
if (!vdev->active) {
/* It's the last scan */
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
/* Set control_packet argument */
vdev->control_packet = next_receive_1;
send_control_packet(ssm);
break;
case SSM_WAIT_ANOTHER_SCAN:
/* Orange light is on now */
fpi_timeout_add(VFS_SSM_ORANGE_TIMEOUT, another_scan, ssm);
break;
default:
fp_err("Unknown state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Driver functions */
/* Callback for dev_activate ssm */
static void dev_activate_callback(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = ssm->priv;
struct vfs_dev_t *vdev = idev->priv;
vdev->ssm_active = 0;
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *idev, enum fp_imgdev_state state)
{
struct vfs_dev_t *vdev = idev->priv;
/* Initialize flags */
vdev->active = 1;
vdev->need_report = 1;
vdev->ssm_active = 1;
struct fpi_ssm *ssm = fpi_ssm_new(idev->dev, activate_ssm, SSM_STATES);
ssm->priv = idev;
fpi_ssm_start(ssm, dev_activate_callback);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = idev->priv;
if (!vdev->ssm_active) {
fpi_imgdev_deactivate_complete(idev);
return;
}
/* Initialize flags */
vdev->active = 0;
vdev->need_report = 1;
}
/* Callback for dev_open ssm */
static void dev_open_callback(struct fpi_ssm *ssm)
{
/* Notify open complete */
fpi_imgdev_open_complete((struct fp_img_dev *)ssm->priv, 0);
fpi_ssm_free(ssm);
}
/* Open device */
static int dev_open(struct fp_img_dev *idev, unsigned long driver_data)
{
/* Claim usb interface */
int error = libusb_claim_interface(idev->udev, 0);
if (error < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
return error;
}
/* Initialize private structure */
struct vfs_dev_t *vdev = g_malloc0(sizeof(struct vfs_dev_t));
idev->priv = vdev;
/* Clearing previous device state */
struct fpi_ssm *ssm = fpi_ssm_new(idev->dev, activate_ssm, SSM_STATES);
ssm->priv = idev;
fpi_ssm_start(ssm, dev_open_callback);
return 0;
}
/* Close device */
static void dev_close(struct fp_img_dev *idev)
{
/* Release private structure */
g_free(idev->priv);
/* Release usb interface */
libusb_release_interface(idev->udev, 0);
/* Notify close complete */
fpi_imgdev_close_complete(idev);
}
/* Usb id table of device */
static const struct usb_id id_table[] = {
{.vendor = 0x138a,.product = 0x0050},
{0, 0, 0,},
};
/* Device driver definition */
struct fp_img_driver vfs0050_driver = {
/* Driver specification */
.driver = {
.id = VFS0050_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS0050",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

382
libfprint/drivers/vfs0050.h Normal file
View File

@@ -0,0 +1,382 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@gmail.com>
*
* 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
*/
/* Timeout for all send/recv operations, except interrupt waiting and abort */
#define VFS_USB_TIMEOUT 100
/* Timeout for usb abort */
#define VFS_USB_ABORT_TIMEOUT 20
/* Default timeout for SSM timers */
#define VFS_SSM_TIMEOUT 100
/* Timeout for orange light */
#define VFS_SSM_ORANGE_TIMEOUT 400
/* Buffer size for abort and fprint receiving */
#define VFS_USB_BUFFER_SIZE 65536
/* Line size from scanner including metainformation: line number, narrow stripe from the center, etc */
#define VFS_LINE_SIZE 148
/* Width of narrow stripe from the center */
#define VFS_NEXT_LINE_WIDTH 32
/* Image width from scanner */
#define VFS_IMAGE_WIDTH 100
/* Maximum image height after assembling */
#define VFS_MAX_HEIGHT 3000
/* Size of control packets: turn_on, turn_off, next_receive_* */
#define VFS_CONTROL_PACKET_SIZE 125
/* Size of result of commit */
#define VFS_COMMIT_RESPONSE_SIZE 1106
/* Size of interrupt from EP3 */
#define VFS_INTERRUPT_SIZE 5
/* EP3 endpoint */
#define EP3_IN 0x83
/* Fingerprint horizontal line */
struct vfs_line {
/* It must be always 0x01 */
unsigned char _0x01;
/* It must be always 0xfe */
unsigned char _0xfe;
/* line number starting from some number in Little-Endian */
unsigned short id;
/* Some hashes which are useful to detect noise */
unsigned char noise_hash_1;
unsigned char noise_hash_2;
/* The first byte of _somedata is always 0x00, the second is strange useless cyclic line number */
unsigned short _somedata;
/* Fingerprint image */
unsigned char data[VFS_IMAGE_WIDTH];
/* Narrow fingerprint part from the center used for variable speed lines assembling */
unsigned char next_line_part[VFS_NEXT_LINE_WIDTH];
/* scan_data is 0xfb except some rare cases, it's skipped */
unsigned char scan_data[8];
} __attribute__ ((__packed__));
/* The main driver structure */
struct vfs_dev_t {
/* One if we were asked to read fingerprint, zero otherwise */
char active;
/* Control packet parameter for send_control_packet */
unsigned char *control_packet;
/* For dev_deactivate to check whether ssm still running or not */
char ssm_active;
/* Current async transfer */
struct libusb_transfer *transfer;
/* Should we call fpi_imgdev_activate_complete or fpi_imgdev_deactivate_complete */
char need_report;
/* Should we wait more for interrupt */
char wait_interrupt;
/* Received fingerprint raw lines */
struct vfs_line *lines_buffer;
/* Current number of received bytes and current memory used by data */
int bytes, memory;
/* USB buffer for fingerprint */
char *usb_buffer;
/* Received interrupt data */
unsigned char interrupt[8];
};
/* SSM states for clear_ep2 */
enum SUBSM1 {
SUBSM1_COMMAND_04,
SUBSM1_RETURN_CODE,
SUBSM1_ABORT_2,
SUBSM1_STATES,
};
/* SSM states for control */
enum SUBSM2 {
SUBSM2_SEND_CONTROL,
SUBSM2_RETURN_CODE, /* If next_receive, send another control packet */
SUBSM2_SEND_COMMIT,
SUBSM2_COMMIT_RESPONSE,
SUBSM2_READ_EMPTY_INTERRUPT,
SUBSM2_ABORT_3,
SUBSM2_CLEAR_EP2,
SUBSM2_STATES,
};
/* SSM states for activate_ssm */
enum SSM_STATE {
SSM_INITIAL_ABORT_1,
SSM_INITIAL_ABORT_2,
SSM_INITIAL_ABORT_3,
SSM_CLEAR_EP2,
SSM_TURN_OFF,
/* Here the device is turned off; if not active, complete ssm */
SSM_TURN_ON,
SSM_ASK_INTERRUPT,
SSM_WAIT_INTERRUPT,
SSM_RECEIVE_FINGER,
SSM_SUBMIT_IMAGE,
/* If not active, jump to CLEAR_EP2 */
SSM_NEXT_RECEIVE,
SSM_WAIT_ANOTHER_SCAN,
/* Jump to TURN_ON */
SSM_STATES
};
/* Blocks of data from USB sniffer */
/* Turns on the light */
static unsigned char turn_on[] = {
0x39, 0x20, 0xBF, 0x02, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0xD1, 0xD1, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Power off */
static unsigned char turn_off[] = {
0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Turns on orange light */
static unsigned char next_receive_1[] = {
0x39, 0xB8, 0x0B, 0x00, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xB8, 0x0B, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Packet directly after next_receive_1 */
static unsigned char next_receive_2[] = {
0x39, 0xE8, 0x03, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xE8, 0x03, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
};
/* Commit message */
static unsigned char commit_out[] = {
0x02, 0x94, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x2C, 0x03, 0x00,
0x30, 0x1B, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x03, 0x00, 0x30, 0x3D, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x18, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x28, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x30, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x38, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x3C, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x44, 0x03, 0x00,
0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x48, 0x03, 0x00, 0x30, 0x01, 0x04, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x4C, 0x03, 0x00, 0x30, 0x01, 0x0C, 0x02, 0x00, 0x20, 0x00, 0x08,
0x00, 0x54, 0x03, 0x00,
0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x5C, 0x03, 0x00,
0x30, 0x90, 0x01, 0x02,
0x00, 0x20, 0x00, 0x08, 0x00, 0x60, 0x03, 0x00, 0x30, 0x2C, 0x01, 0x19,
0x00, 0x20, 0x00, 0x08,
0x00, 0x64, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x6C, 0x03, 0x00,
0x30, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x03, 0x00,
0x30, 0x21, 0x80, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x78, 0x03, 0x00, 0x30, 0x09, 0x00, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x7C, 0x03, 0x00, 0x30, 0x0B, 0x00, 0x19, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x84, 0x03, 0x00,
0x30, 0x3A, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x88, 0x03, 0x00, 0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x8C, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x90, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x94, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x98, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1,
0x01, 0x20, 0x00, 0x08,
0x00, 0x9C, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1, 0x01, 0x20, 0x00, 0x08,
0x00, 0xA8, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xAC, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xB0, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0xB4, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0xB8, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xBC, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xC0, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x84, 0x03, 0x00, 0x30, 0x3B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x08, 0x07, 0x00,
0x30, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x0C, 0x07, 0x00,
0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x14, 0x07, 0x00, 0x30, 0x20, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00, 0x30, 0x1A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x70, 0x0D, 0x00,
0x30, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x2B, 0xFF, 0x2B, 0xFF, 0x2B,
0xED, 0x00, 0x00, 0x2B,
0xFB, 0x00, 0x00, 0x2B, 0xC5, 0x00, 0x00, 0x2B, 0x05, 0x80, 0x70, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0xD3, 0x2E, 0xC0, 0x2C, 0x3B, 0x08, 0xF0, 0x3B, 0x09, 0x24,
0xBB, 0x3B, 0x0B, 0x24,
0xAA, 0x3B, 0x1F, 0xF8, 0x00, 0x3B, 0x3F, 0xF0, 0x00, 0x3B, 0x35, 0xC0,
0x00, 0x38, 0x80, 0x2C,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x38, 0x80, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xC0,
0x3A, 0x80, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3B, 0x0A, 0x80,
0x2E, 0x83, 0x24, 0xDB,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x2C, 0x31, 0x83, 0x2C, 0x70,
0x00, 0x00, 0x00, 0x00,
0xCB, 0x33, 0x1B, 0x83, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x31,
0x83, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xCB, 0x00, 0x33, 0x1E, 0x83, 0x2E, 0x25, 0xFF, 0xC4,
0x00, 0x2F, 0x06, 0x84,
0x2E, 0x00, 0x00, 0x10, 0x20, 0x29, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00,
0x00, 0x23, 0x00, 0x00,
0x00, 0x21, 0x00, 0x10, 0x00, 0x48, 0x03, 0x00, 0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x4C, 0x03, 0x00,
0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x21, 0x00, 0x10,
0x00, 0x20, 0x03, 0x00,
0x30, 0x7F, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00,
0x30, 0x1A, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x20, 0x03, 0x00,
0x30, 0xC3, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x84, 0x00, 0x31, 0x65, 0x77,
0x77, 0x77, 0x78, 0x88,
0x77, 0x77, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x77, 0x67,
0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x66, 0x66, 0x66,
0x66, 0x77, 0x66, 0x66,
0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x76, 0x66, 0x56,
0x66, 0x66, 0x56, 0x55,
0x65, 0x66, 0x66, 0x66, 0x65, 0x66, 0x66, 0x55, 0x66, 0x66, 0x65, 0x66,
0x76, 0x76, 0x77, 0x77,
0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x77, 0x67, 0x66, 0x66, 0x66, 0x56,
0x65, 0x66, 0x65, 0x66,
0x66, 0x55, 0x55, 0x54, 0x55, 0x65, 0x66, 0x66, 0x66, 0x76, 0x77, 0x87,
0x88, 0x77, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x65, 0x66, 0x55, 0x55, 0x65, 0x56, 0x55,
0x55, 0x55, 0x54, 0x45,
0x54, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77,
0x66, 0x26, 0x00, 0x28,
0x00, 0xFF, 0x00, 0x0F, 0x00, 0xF0, 0xF0, 0x0F, 0x00, 0x20, 0x00, 0x00,
0x00, 0x30, 0x01, 0x02,
0x00, 0x2C, 0x01, 0x28, 0x00, 0x20, 0x80, 0x00, 0x00, 0x0A, 0x00, 0x02,
0x00, 0x0B, 0x00, 0x19,
0x00, 0x40, 0x1F, 0x10, 0x27, 0x00, 0x0F, 0x03, 0x00,
};
/* Known interrupts */
static unsigned char empty_interrupt[] = {
0x00, 0x00, 0x00, 0x00, 0x00,
};
static unsigned char interrupt1[] = {
0x02, 0x00, 0x0E, 0x00, 0xF0,
};
static unsigned char interrupt2[] = {
0x02, 0x04, 0x0A, 0x00, 0xF0,
};
static unsigned char interrupt3[] = {
0x02, 0x00, 0x0A, 0x00, 0xF0,
};

View File

@@ -186,7 +186,7 @@ static int result_codes[2][RESULT_COUNT] =
static int result_code(struct fp_img_dev *dev, int result)
{
/* Check result value */
if (result < 0 && result >= RESULT_COUNT)
if (result < 0 || result >= RESULT_COUNT)
return result;
/* Return result code */
@@ -653,7 +653,7 @@ static int action_completed(struct fp_img_dev *dev)
struct vfs101_dev *vdev = dev->priv;
if ((dev->action == IMG_ACTION_ENROLL) &&
(vdev->enroll_stage < 1))
(vdev->enroll_stage < dev->dev->nr_enroll_stages))
/* Enroll not completed, return false */
return FALSE;
@@ -1504,7 +1504,7 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
if (r < 0)
{
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}

View File

@@ -240,7 +240,7 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}

View File

@@ -23,6 +23,7 @@
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include <assembling.h>
#include "driver_ids.h"
#include "vfs5011_proto.h"
@@ -77,6 +78,8 @@ struct usbexchange_data {
int timeout;
};
static void start_scan(struct fp_img_dev *dev);
static void async_send_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
@@ -249,43 +252,16 @@ static void usb_exchange_async(struct fpi_ssm *ssm,
/* ====================== utils ======================= */
#if VFS5011_LINE_SIZE > INT_MAX/(256*256)
#error We might get integer overflow while computing standard deviation!
#endif
/* Calculade squared standand deviation */
static int get_deviation(unsigned char *buf, int size)
{
int res = 0, mean = 0, i;
for (i = 0; i < size; i++)
mean += buf[i];
mean /= size;
for (i = 0; i < size; i++) {
int dev = (int)buf[i] - mean;
res += dev*dev;
}
return res / size;
}
/* Calculate mean square difference of two lines */
static int get_diff_norm(unsigned char *buf1, unsigned char *buf2, int size)
{
int res = 0, i;
for (i = 0; i < size; i++) {
int dev = (int)buf1[i] - (int)buf2[i];
res += dev*dev;
}
return res / size;
}
/* Calculade squared standand deviation of sum of two lines */
static int get_deviation2(unsigned char *buf1, unsigned char *buf2, int size)
static int vfs5011_get_deviation2(struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *row2)
{
unsigned char *buf1, *buf2;
int res = 0, mean = 0, i;
const int size = 64;
buf1 = row1->data + 56;
buf2 = row2->data + 168;
for (i = 0; i < size; i++)
mean += (int)buf1[i] + (int)buf2[i];
@@ -299,121 +275,13 @@ static int get_deviation2(unsigned char *buf1, unsigned char *buf2, int size)
return res / size;
}
static int cmpint(const void *p1, const void *p2, gpointer data)
static unsigned char vfs5011_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
int a = *((int *)p1);
int b = *((int *)p2);
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
unsigned char *data = row->data + 8;
static void median_filter(int *data, int size, int filtersize)
{
int i;
int *result = (int *)g_malloc0(size*sizeof(int));
int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
for (i = 0; i < size; i++) {
int i1 = i - (filtersize-1)/2;
int i2 = i + (filtersize-1)/2;
if (i1 < 0)
i1 = 0;
if (i2 >= size)
i2 = size-1;
g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
result[i] = sortbuf[(i2-i1+1)/2];
}
memmove(data, result, size*sizeof(int));
g_free(result);
g_free(sortbuf);
}
void interpolate_lines(unsigned char *line1, float y1, unsigned char *line2,
float y2, unsigned char *output, float yi, int size)
{
int i;
for (i = 0; i < size; i++)
output[i] = (float)line1[i]
+ (yi-y1)/(y2-y1)*(line2[i]-line1[i]);
}
int min(int a, int b) {return (a < b) ? a : b; }
/* Rescale image to account for variable swiping speed */
int vfs5011_rescale_image(unsigned char *image, int input_lines,
unsigned char *output, int max_output_lines)
{
/* Number of output lines per distance between two scanners */
enum {
RESOLUTION = 10,
MEDIAN_FILTER_SIZE = 13,
MAX_OFFSET = 10,
GOOD_OFFSETS_CRITERION = 20,
GOOD_OFFSETS_THRESHOLD = 3
};
int i;
float y = 0.0;
int line_ind = 0;
int *offsets = (int *)g_malloc0(input_lines * sizeof(int));
#ifdef ENABLE_DEBUG_LOGGING
gint64 start_time = g_get_real_time();
#endif
for (i = 0; i < input_lines-1; i += 2) {
int bestmatch = i;
int bestdiff = 0;
int j;
int firstrow, lastrow;
firstrow = i+1;
lastrow = min(i + MAX_OFFSET, input_lines-1);
for (j = firstrow; j <= lastrow; j++) {
int diff = get_deviation2(
image + i*VFS5011_LINE_SIZE + 56,
image + j*VFS5011_LINE_SIZE + 168,
64);
if ((j == firstrow) || (diff < bestdiff)) {
bestdiff = diff;
bestmatch = j;
}
}
offsets[i/2] = bestmatch - i;
fp_dbg("offsets: %llu - %d", start_time, offsets[i/2]);
}
median_filter(offsets, input_lines-1, MEDIAN_FILTER_SIZE);
fp_dbg("offsets_filtered: %llu", g_get_real_time());
for (i = 0; i <= input_lines/2-1; i++)
fp_dbg("%d", offsets[i]);
for (i = 0; i < input_lines-1; i++) {
int offset = offsets[i/2];
if (offset > 0) {
float ynext = y + (float)RESOLUTION / offset;
while (line_ind < ynext) {
if (line_ind > max_output_lines-1) {
g_free(offsets);
return line_ind;
}
interpolate_lines(
image + i*VFS5011_LINE_SIZE + 8, y,
image + (i+1)*VFS5011_LINE_SIZE + 8,
ynext,
output + line_ind*VFS5011_IMAGE_WIDTH,
line_ind,
VFS5011_IMAGE_WIDTH);
line_ind++;
}
y = ynext;
}
}
g_free(offsets);
return line_ind;
return data[x];
}
/* ====================== main stuff ======================= */
@@ -424,18 +292,29 @@ enum {
MAX_CAPTURE_LINES = 100000,
};
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS5011_IMAGE_WIDTH,
.max_height = MAXLINES,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 30,
.get_deviation = vfs5011_get_deviation2,
.get_pixel = vfs5011_get_pixel,
};
struct vfs5011_data {
unsigned char *total_buffer;
unsigned char *capture_buffer;
unsigned char *image_buffer;
unsigned char *row_buffer;
unsigned char *lastline;
unsigned char *rescale_buffer;
GSList *rows;
int lines_captured, lines_recorded, empty_lines;
int max_lines_captured, max_lines_recorded;
int lines_total, lines_total_allocated;
gboolean loop_running;
gboolean deactivating;
struct usbexchange_data init_sequence;
struct libusb_transfer *flying_transfer;
};
enum {
@@ -483,7 +362,7 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
unsigned char *linebuf = data->capture_buffer
+ i * VFS5011_LINE_SIZE;
if (get_deviation(linebuf + 8, VFS5011_IMAGE_WIDTH)
if (fpi_std_sq_dev(linebuf + 8, VFS5011_IMAGE_WIDTH)
< DEVIATION_THRESHOLD) {
if (data->lines_captured == 0)
continue;
@@ -505,14 +384,13 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
}
if ((data->lastline == NULL)
|| (get_diff_norm(
|| (fpi_mean_sq_diff_norm(
data->lastline + 8,
linebuf + 8,
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
data->lastline = data->image_buffer
+ data->lines_recorded
* VFS5011_LINE_SIZE;
memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
data->lastline = g_malloc(VFS5011_LINE_SIZE);
data->rows = g_slist_prepend(data->rows, data->lastline);
g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
data->lines_recorded++;
if (data->lines_recorded >= data->max_lines_recorded) {
fp_dbg("process_chunk: recorded %d lines, finishing",
@@ -527,20 +405,14 @@ static int process_chunk(struct vfs5011_data *data, int transferred)
void submit_image(struct fpi_ssm *ssm, struct vfs5011_data *data)
{
struct fp_img_dev *dev = (struct fp_img_dev *)ssm->priv;
int height = vfs5011_rescale_image(data->image_buffer,
data->lines_recorded,
data->rescale_buffer, MAXLINES);
struct fp_img *img = fpi_img_new(VFS5011_IMAGE_WIDTH * height);
struct fp_img *img;
if (img == NULL) {
fp_err("Failed to create image");
fpi_ssm_mark_aborted(ssm, -1);
}
data->rows = g_slist_reverse(data->rows);
img->flags = FP_IMG_V_FLIPPED;
img->width = VFS5011_IMAGE_WIDTH;
img->height = height;
memmove(img->data, data->rescale_buffer, VFS5011_IMAGE_WIDTH * height);
img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded);
g_slist_free_full(data->rows, g_free);
data->rows = NULL;
fp_dbg("Image captured, commiting");
@@ -564,10 +436,15 @@ static void chunk_capture_callback(struct libusb_transfer *transfer)
else
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_READ_DATA);
} else {
fp_err("Failed to capture data");
fpi_ssm_mark_aborted(ssm, -1);
if (!data->deactivating) {
fp_err("Failed to capture data");
fpi_ssm_mark_aborted(ssm, -1);
} else {
fpi_ssm_mark_completed(ssm);
}
}
libusb_free_transfer(transfer);
data->flying_transfer = NULL;
}
static int capture_chunk_async(struct vfs5011_data *data,
@@ -582,12 +459,12 @@ static int capture_chunk_async(struct vfs5011_data *data,
STOP_CHECK_LINES = 50
};
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, handle, VFS5011_IN_ENDPOINT_DATA,
data->flying_transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(data->flying_transfer, handle, VFS5011_IN_ENDPOINT_DATA,
data->capture_buffer,
nline * VFS5011_LINE_SIZE,
chunk_capture_callback, ssm, timeout);
return libusb_submit_transfer(transfer);
return libusb_submit_transfer(data->flying_transfer);
}
static void async_sleep_cb(void *data)
@@ -796,6 +673,12 @@ static void activate_loop(struct fpi_ssm *ssm)
fp_dbg("main_loop: state %d", ssm->cur_state);
if (data->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
return;
}
switch (ssm->cur_state) {
case DEV_ACTIVATE_REQUEST_FPRINT:
data->init_sequence.stepcount =
@@ -819,17 +702,12 @@ static void activate_loop(struct fpi_ssm *ssm)
break;
case DEV_ACTIVATE_READ_DATA:
if (data->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
} else {
r = capture_chunk_async(data, dev->udev, CAPTURE_LINES,
READ_TIMEOUT, ssm);
if (r != 0) {
fp_err("Failed to capture data");
fpi_imgdev_session_error(dev, r);
fpi_ssm_mark_aborted(ssm, r);
}
r = capture_chunk_async(data, dev->udev, CAPTURE_LINES,
READ_TIMEOUT, ssm);
if (r != 0) {
fp_err("Failed to capture data");
fpi_imgdev_session_error(dev, r);
fpi_ssm_mark_aborted(ssm, r);
}
break;
@@ -869,19 +747,25 @@ static void activate_loop_complete(struct fpi_ssm *ssm)
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
data->loop_running = FALSE;
submit_image(ssm, data);
fpi_imgdev_report_finger_status(dev, FALSE);
/* We don't want to submit image if we're in deactivating process */
if (!data->deactivating) {
submit_image(ssm, data);
fpi_imgdev_report_finger_status(dev, FALSE);
}
fpi_ssm_free(ssm);
if (r)
fpi_imgdev_session_error(dev, r);
data->loop_running = FALSE;
if (data->deactivating)
if (data->deactivating) {
fpi_imgdev_deactivate_complete(dev);
} else if (r) {
fpi_imgdev_session_error(dev, r);
} else {
start_scan(dev);
}
}
static void open_loop(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = (struct fp_img_dev *)ssm->priv;
@@ -922,14 +806,8 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
data = (struct vfs5011_data *)g_malloc0(sizeof(*data));
data->capture_buffer =
(unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE);
data->image_buffer =
(unsigned char *)g_malloc0(MAXLINES * VFS5011_LINE_SIZE);
data->rescale_buffer =
(unsigned char *)g_malloc0(MAXLINES * VFS5011_IMAGE_WIDTH);
dev->priv = data;
dev->dev->nr_enroll_stages = 1;
r = libusb_reset_device(dev->udev);
if (r != 0) {
fp_err("Failed to reset the device");
@@ -938,7 +816,7 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
r = libusb_claim_interface(dev->udev, 0);
if (r != 0) {
fp_err("Failed to claim interface");
fp_err("Failed to claim interface: %s", libusb_error_name(r));
return r;
}
@@ -956,42 +834,55 @@ static void dev_close(struct fp_img_dev *dev)
struct vfs5011_data *data = (struct vfs5011_data *)dev->priv;
if (data != NULL) {
g_free(data->capture_buffer);
g_free(data->image_buffer);
g_free(data->rescale_buffer);
g_slist_free_full(data->rows, g_free);
g_free(data);
}
fpi_imgdev_close_complete(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void start_scan(struct fp_img_dev *dev)
{
struct vfs5011_data *data = (struct vfs5011_data *)dev->priv;
struct fpi_ssm *ssm;
fp_dbg("device initialized");
data->deactivating = FALSE;
data->loop_running = TRUE;
fp_dbg("creating ssm");
ssm = fpi_ssm_new(dev->dev, activate_loop, DEV_ACTIVATE_NUM_STATES);
ssm->priv = dev;
fp_dbg("starting ssm");
fpi_ssm_start(ssm, activate_loop_complete);
fp_dbg("ssm done, getting out");
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct vfs5011_data *data = (struct vfs5011_data *)dev->priv;
fp_dbg("device initialized");
data->deactivating = FALSE;
start_scan(dev);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
int r;
struct vfs5011_data *data = dev->priv;
if (data->loop_running)
if (data->loop_running) {
data->deactivating = TRUE;
else
if (data->flying_transfer) {
r = libusb_cancel_transfer(data->flying_transfer);
if (r < 0)
fp_dbg("cancel failed error %d", r);
}
} else
fpi_imgdev_deactivate_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x138a, .product = 0x0010 /* Validity device from some Toshiba laptops */ },
{ .vendor = 0x138a, .product = 0x0011 /* vfs5011 */ },
{ .vendor = 0x138a, .product = 0x0017 /* Validity device from Lenovo T440 laptops */ },
{ .vendor = 0x138a, .product = 0x0018 /* one more Validity device */ },

View File

@@ -305,6 +305,9 @@ extern struct fp_img_driver upektc_img_driver;
#ifdef ENABLE_ETES603
extern struct fp_img_driver etes603_driver;
#endif
#ifdef ENABLE_VFS0050
extern struct fp_img_driver vfs0050_driver;
#endif
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
@@ -376,6 +379,7 @@ struct fp_minutiae {
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
#define FP_IMG_PARTIAL (1<<4)
#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
| FP_IMG_COLORS_INVERTED)
@@ -482,7 +486,12 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
gboolean present);
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result);
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
/* utils */
int fpi_std_sq_dev(const unsigned char *buf, int size);
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size);
#endif

View File

@@ -25,6 +25,8 @@
static const struct usb_id whitelist_id_table[] = {
{ .vendor = 0x08ff, .product = 0x2810 },
/* https://bugzilla.redhat.com/show_bug.cgi?id=1173367 */
{ .vendor = 0x138a, .product = 0x0017 },
{ 0, 0, 0, },
};
@@ -75,6 +77,7 @@ static void print_driver (struct fp_driver *driver)
printf ("# %s\n", driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name);
num_printed++;
}

View File

@@ -283,6 +283,9 @@ int fpi_img_detect_minutiae(struct fp_img *img)
return -EINVAL;
}
/* Remove perimeter points from partial image */
g_lfsparms_V2.remove_perimeter_pts = img->flags & FP_IMG_PARTIAL ? TRUE : FALSE;
/* 25.4 mm per inch */
timer = g_timer_new();
r = get_minutiae(&minutiae, &quality_map, &direction_map,
@@ -508,3 +511,37 @@ API_EXPORTED struct fp_minutia **fp_img_get_minutiae(struct fp_img *img,
return img->minutiae->list;
}
/* Calculate squared standand deviation */
int fpi_std_sq_dev(const unsigned char *buf, int size)
{
int res = 0, mean = 0, i;
if (size > (INT_MAX / 65536)) {
fp_err("%s: we might get an overflow!", __func__);
return -EOVERFLOW;
}
for (i = 0; i < size; i++)
mean += buf[i];
mean /= size;
for (i = 0; i < size; i++) {
int dev = (int)buf[i] - mean;
res += dev*dev;
}
return res / size;
}
/* Calculate normalized mean square difference of two lines */
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size)
{
int res = 0, i;
for (i = 0; i < size; i++) {
int dev = (int)buf1[i] - (int)buf2[i];
res += dev * dev;
}
return res / size;
}

View File

@@ -159,20 +159,23 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
imgdev->action_result = 0;
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
dev_change_state(imgdev, IMG_ACQUIRE_STATE_AWAIT_FINGER_ON);
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
}
break;
case IMG_ACTION_VERIFY:
fpi_drvcb_report_verify_result(imgdev->dev, r, img);
imgdev->action_result = 0;
fp_print_data_free(data);
break;
case IMG_ACTION_IDENTIFY:
fpi_drvcb_report_identify_result(imgdev->dev, r,
imgdev->identify_match_offset, img);
imgdev->action_result = 0;
fp_print_data_free(data);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_report_capture_result(imgdev->dev, r, img);
imgdev->action_result = 0;
break;
default:
fp_err("unhandled action %d", imgdev->action);
@@ -217,6 +220,13 @@ static void identify_process_img(struct fp_img_dev *imgdev)
imgdev->identify_match_offset = match_offset;
}
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result)
{
imgdev->action_result = result;
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
}
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
{
struct fp_print_data *print;

View File

@@ -236,6 +236,8 @@ typedef struct lfsparms{
int pores_steps_bwd;
double pores_min_dist2;
double pores_max_ratio;
int remove_perimeter_pts;
int min_pp_distance;
/* Ridge Counting Controls */
int max_nbrs;
@@ -585,6 +587,9 @@ typedef struct lfsparms{
/* contour points to be considered a pore. */
#define PORES_MAX_RATIO 2.25
/* Points which are closer than this distance to scan perimeter will be removed */
#define PERIMETER_PTS_DISTANCE 10
/***** RIDGE COUNTING CONSTANTS *****/

View File

@@ -129,6 +129,8 @@ LFSPARMS g_lfsparms = {
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
FALSE, /* not removing perimeter points by default */
PERIMETER_PTS_DISTANCE,
/* Ridge Counting Controls */
MAX_NBRS,
@@ -213,6 +215,8 @@ LFSPARMS g_lfsparms_V2 = {
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
FALSE, /* not removing perimeter points by default */
PERIMETER_PTS_DISTANCE,
/* Ridge Counting Controls */
MAX_NBRS,

View File

@@ -1350,6 +1350,159 @@ static int remove_overlaps(MINUTIAE *minutiae,
return(0);
}
static void mark_minutiae_in_range(MINUTIAE *minutiae, int *to_remove, int x, int y,
const LFSPARMS *lfsparms)
{
int i, dist;
for (i = 0; i < minutiae->num; i++) {
if (to_remove[i])
continue;
dist = (int)sqrt((x - minutiae->list[i]->x) * (x - minutiae->list[i]->x) +
(y - minutiae->list[i]->y) * (y - minutiae->list[i]->y));
if (dist < lfsparms->min_pp_distance) {
to_remove[i] = 1;
}
}
}
/*************************************************************************
**************************************************************************
#cat: remove_perimeter_pts - Takes a list of true and false minutiae and
#cat: attempts to detect and remove those false minutiae that
#cat: belong to image edge
Input:
minutiae - list of true and false minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Output:
minutiae - list of pruned minutiae
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
static int remove_perimeter_pts(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, j, ret, *to_remove;
int *left, *left_up, *left_down;
int *right, *right_up, *right_down;
int removed = 0;
int left_min, right_max;
if (!lfsparms->remove_perimeter_pts)
return(0);
to_remove = calloc(minutiae->num, sizeof(int));
left = calloc(ih, sizeof(int));
left_up = calloc(ih, sizeof(int));
left_down = calloc(ih, sizeof(int));
right = calloc(ih, sizeof(int));
right_up = calloc(ih, sizeof(int));
right_down = calloc(ih, sizeof(int));
/* Pass downwards */
left_min = iw - 1;
right_max = 0;
for (i = 0; i < ih; i++) {
for (j = 0; j < left_min; j++) {
if ((bdata[i * iw + j] != 0)) {
left_min = j;
break;
}
}
if (left_min == (iw - 1))
left_down[i] = -1;
else
left_down[i] = left_min;
for (j = iw - 1; j >= right_max; j--) {
if ((bdata[i * iw + j] != 0)) {
right_max = j;
break;
}
}
if (right_max == 0)
right_down[i] = -1;
else
right_down[i] = right_max;
}
/* Pass upwards */
left_min = iw - 1;
right_max = 0;
for (i = ih - 1; i >= 0; i--) {
for (j = 0; j < left_min; j++) {
if ((bdata[i * iw + j] != 0)) {
left_min = j;
break;
}
}
if (left_min == (iw - 1))
left_up[i] = -1;
else
left_up[i] = left_min;
for (j = iw - 1; j >= right_max; j--) {
if ((bdata[i * iw + j] != 0)) {
right_max = j;
break;
}
}
if (right_max == 0)
right_up[i] = -1;
else
right_up[i] = right_max;
}
/* Merge */
left_min = left_down[ih - 1];
right_max = right_down[ih - 1];
for (i = 0; i < ih; i++) {
if (left_down[i] != left_min)
left[i] = left_down[i];
else
left[i] = left_up[i];
if (right_down[i] != right_max)
right[i] = right_down[i];
else
right[i] = right_up[i];
}
free(left_up);
free(left_down);
free(right_up);
free(right_down);
/* Mark minitiae close to the edge */
for (i = 0; i < ih; i++) {
if (left[i] != -1)
mark_minutiae_in_range(minutiae, to_remove, left[i], i, lfsparms);
if (right[i] != -1)
mark_minutiae_in_range(minutiae, to_remove, right[i], i, lfsparms);
}
free(left);
free(right);
for (i = minutiae->num - 1; i >= 0; i--) {
/* If the current minutia index is flagged for removal ... */
if (to_remove[i]){
removed ++;
/* Remove the minutia from the minutiae list. */
if((ret = remove_minutia(i, minutiae))){
free(to_remove);
return(ret);
}
}
}
free(to_remove);
return (0);
}
/*************************************************************************
**************************************************************************
#cat: remove_pores_V2 - Attempts to detect and remove minutia points located on
@@ -2104,6 +2257,11 @@ int remove_false_minutia_V2(MINUTIAE *minutiae,
return(ret);
}
/* 11. Remove minutiae on image edge */
if((ret = remove_perimeter_pts(minutiae, bdata, iw, ih, lfsparms))) {
return (ret);
}
return(0);
}