mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2025-11-15 07:38:12 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d82847a6b4 | ||
|
|
5a7e6e07ff | ||
|
|
2162aa9f48 | ||
|
|
a1f36c71c9 | ||
|
|
e4eedef27e | ||
|
|
5e29695969 | ||
|
|
49a46668ad | ||
|
|
76269decdd | ||
|
|
8454a25ecf | ||
|
|
a0bbbd7d32 | ||
|
|
12f6dae8cd | ||
|
|
9570c36fd4 | ||
|
|
487dae0d2f | ||
|
|
d71018bd8f | ||
|
|
61fa57b05d | ||
|
|
fc1781e317 | ||
|
|
ffef6c2bcc | ||
|
|
67d29f7936 | ||
|
|
9437c98d54 | ||
|
|
f7d00a828d | ||
|
|
0f0a4b2da6 | ||
|
|
c9cdbaf880 | ||
|
|
83f29dad9f | ||
|
|
c2a11c5918 | ||
|
|
3746b2ad5c | ||
|
|
b51fa446e3 | ||
|
|
6fc5293e83 | ||
|
|
aab3daa28b | ||
|
|
e40f7bd1f7 | ||
|
|
6664f87d8f | ||
|
|
6e8d5cd6a1 | ||
|
|
9f7e1ecf40 | ||
|
|
d9567002e4 | ||
|
|
a656a4a9f3 | ||
|
|
2944a35e74 | ||
|
|
391373fb0c | ||
|
|
985e8c4577 | ||
|
|
bd0d4258e4 | ||
|
|
9bbd9b208a | ||
|
|
59fe0fb699 | ||
|
|
f1fdd71613 | ||
|
|
bb66780cb5 | ||
|
|
45fb6d7908 | ||
|
|
9f408bf51b |
22
NEWS
22
NEWS
@@ -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:
|
||||
|
||||
12
autogen.sh
12
autogen.sh
@@ -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
|
||||
|
||||
19
configure.ac
19
configure.ac
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
423
libfprint/assembling.c
Normal 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
65
libfprint/assembling.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -40,6 +40,7 @@ enum {
|
||||
UPEKTC_IMG_ID = 17,
|
||||
ETES603_ID = 18,
|
||||
VFS5011_ID = 19,
|
||||
VFS0050_ID = 20,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
319
libfprint/drivers/upeksonly.h
Normal file
319
libfprint/drivers/upeksonly.h
Normal 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 },
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
791
libfprint/drivers/vfs0050.c
Normal 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
382
libfprint/drivers/vfs0050.h
Normal 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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */ },
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 *****/
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user