mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2025-11-15 07:38:12 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee32166267 | ||
|
|
a3c90f2b24 | ||
|
|
dc537ef2c9 | ||
|
|
061a457658 | ||
|
|
82ae7c1c09 | ||
|
|
79d79c3c87 | ||
|
|
35e356f625 | ||
|
|
948ab02d1a | ||
|
|
a6101026d2 | ||
|
|
e0966cb20f | ||
|
|
e278e8321c | ||
|
|
e1728e7c25 | ||
|
|
e215b05094 | ||
|
|
fe3fdd1f50 | ||
|
|
5ff45658c0 | ||
|
|
d12b294783 | ||
|
|
2bba4fb073 | ||
|
|
aeca32fc12 | ||
|
|
12c1088777 | ||
|
|
9e2f8b5e75 | ||
|
|
c14ebd8b63 | ||
|
|
a6339a30ef | ||
|
|
f3dd55815e | ||
|
|
6d65bfcf80 | ||
|
|
1acd647b29 | ||
|
|
0e843ad6b3 | ||
|
|
7eafca7bab | ||
|
|
3b3679c900 | ||
|
|
43eca622cd | ||
|
|
02509e1073 | ||
|
|
0b2d33c712 | ||
|
|
7751fcb375 | ||
|
|
8a87ba448c | ||
|
|
7e1646c382 |
1
AUTHORS
1
AUTHORS
@@ -8,3 +8,4 @@ Copyright (C) 2007-2008,2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
|
||||
Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
|
||||
Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
|
||||
Copyright (C) 2012 Timo Teräs <timo.teras@iki.fi>
|
||||
|
||||
30
NEWS
30
NEWS
@@ -1,6 +1,36 @@
|
||||
This file lists notable changes in each release. For the full history of all
|
||||
changes, see ChangeLog.
|
||||
|
||||
2015-02-03: v0.6.0 release
|
||||
|
||||
* Drivers:
|
||||
- Reduce duplication between AES3500 and AES4000 drivers and
|
||||
add support for AES3500 device
|
||||
- Add support for UPEK 147e:2020 and Upek Eikon 2 devices
|
||||
- Add EgisTec ES603 driver
|
||||
- Add VFS5011 driver
|
||||
- Always perform 5 scans for image enrollment
|
||||
- Better verification with AES1660 driver
|
||||
- Better verification for a number of AES drivers
|
||||
|
||||
* Library:
|
||||
- Always use Pixman for image manipulation, gdk-pixbuf and ImageMagick
|
||||
are not supported any more.
|
||||
|
||||
* Udev rules:
|
||||
- Fix warning when USB hub or system does not support power management
|
||||
|
||||
2013-08-11: v0.5.1 release
|
||||
|
||||
* Drivers
|
||||
- Add support for 147e:2020 to upeke2 driver
|
||||
- Fix possible race condition, and cancellation in uru4000 driver
|
||||
|
||||
* Udev rules:
|
||||
- Add Microsoft keyboard to the suspend blacklist
|
||||
|
||||
* Plenty of build fixes
|
||||
|
||||
2012-12-03: v0.5.0 release
|
||||
|
||||
* Drivers:
|
||||
|
||||
95
configure.ac
95
configure.ac
@@ -1,8 +1,8 @@
|
||||
AC_INIT([libfprint], [0.5.0])
|
||||
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news])
|
||||
AC_INIT([libfprint], [0.6.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])
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Enable silent build when available (Automake 1.11)
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
@@ -23,11 +23,12 @@ AC_SUBST(lt_major)
|
||||
AC_SUBST(lt_revision)
|
||||
AC_SUBST(lt_age)
|
||||
|
||||
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes4000 vfs101 vfs301"
|
||||
all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 vfs5011 upektc_img etes603"
|
||||
|
||||
require_imaging='no'
|
||||
require_aeslib='no'
|
||||
require_aesX660='no'
|
||||
require_aes3k='no'
|
||||
enable_upeke2='no'
|
||||
enable_upekts='no'
|
||||
enable_upektc='no'
|
||||
@@ -40,9 +41,13 @@ enable_aes1660='no'
|
||||
enable_aes2501='no'
|
||||
enable_aes2550='no'
|
||||
enable_aes2660='no'
|
||||
enable_aes3500='no'
|
||||
enable_aes4000='no'
|
||||
enable_vfs101='no'
|
||||
enable_vfs301='no'
|
||||
enable_vfs5011='no'
|
||||
enable_upektc_img='no'
|
||||
enable_etes603='no'
|
||||
|
||||
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
|
||||
[List of drivers to enable])],
|
||||
@@ -111,10 +116,18 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
|
||||
require_aesX660="yes"
|
||||
enable_aes2660="yes"
|
||||
;;
|
||||
aes3500)
|
||||
AC_DEFINE([ENABLE_AES3500], [], [Build AuthenTec AES3500 driver])
|
||||
require_aeslib="yes"
|
||||
require_imaging="yes"
|
||||
require_aes3k="yes"
|
||||
enable_aes3500="yes"
|
||||
;;
|
||||
aes4000)
|
||||
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
|
||||
require_aeslib="yes"
|
||||
require_imaging="yes"
|
||||
require_aes3k="yes"
|
||||
enable_aes4000="yes"
|
||||
;;
|
||||
vfs101)
|
||||
@@ -125,6 +138,18 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
|
||||
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
|
||||
enable_vfs301="yes"
|
||||
;;
|
||||
vfs5011)
|
||||
AC_DEFINE([ENABLE_VFS5011], [], [Build Validity VFS5011 driver])
|
||||
enable_vfs5011="yes"
|
||||
;;
|
||||
upektc_img)
|
||||
AC_DEFINE([ENABLE_UPEKTC_IMG], [], [Build Upek TouchChip Fingerprint Coprocessor driver])
|
||||
enable_upektc_img="yes"
|
||||
;;
|
||||
etes603)
|
||||
AC_DEFINE([ENABLE_ETES603], [], [Build EgisTec ES603 driver])
|
||||
enable_etes603="yes"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -140,11 +165,16 @@ AM_CONDITIONAL([ENABLE_AES1660], [test "$enable_aes1660" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2550], [test "$enable_aes2550" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES2660], [test "$enable_aes2660" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES3500], [test "$enable_aes3500" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_AES3K], [test "$require_aes3k" = "yes"])
|
||||
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
|
||||
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"])
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
|
||||
@@ -156,12 +186,11 @@ PKG_CHECK_MODULES(CRYPTO, nss)
|
||||
AC_SUBST(CRYPTO_CFLAGS)
|
||||
AC_SUBST(CRYPTO_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(GLIB, "glib-2.0")
|
||||
PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28])
|
||||
AC_SUBST(GLIB_CFLAGS)
|
||||
AC_SUBST(GLIB_LIBS)
|
||||
|
||||
imagemagick_found=no
|
||||
gdkpixbuf_found=no
|
||||
pixman_found=no
|
||||
|
||||
AC_ARG_ENABLE(udev-rules,
|
||||
AC_HELP_STRING([--enable-udev-rules],[Update the udev rules]),
|
||||
@@ -173,6 +202,10 @@ AC_ARG_ENABLE(udev-rules,
|
||||
[ENABLE_UDEV_RULES=yes]) dnl Default value
|
||||
AM_CONDITIONAL(ENABLE_UDEV_RULES, test x$ENABLE_UDEV_RULES = "xyes")
|
||||
|
||||
if test $ENABLE_UDEV_RULES = no && test -d .git/ ; then
|
||||
AC_MSG_ERROR(--disable-udev-rules can only be used when building from tarballs)
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(udev-rules-dir,
|
||||
AS_HELP_STRING([--with-udev-rules-dir=DIR],[Installation path for udev rules @<:@auto@:>@]),
|
||||
[ac_with_udev_rules_dir=$withval],
|
||||
@@ -185,20 +218,13 @@ AC_MSG_NOTICE([installing udev rules in ${ac_with_udev_rules_dir}])
|
||||
AC_SUBST([udev_rulesdir],[${ac_with_udev_rules_dir}])
|
||||
|
||||
if test "$require_imaging" = "yes"; then
|
||||
PKG_CHECK_MODULES(IMAGING, gthread-2.0 gdk-pixbuf-2.0, [gdkpixbuf_found=yes], [gdkpixbuf_found=no])
|
||||
if test "$gdkpixbuf_found" != "yes"; then
|
||||
PKG_CHECK_MODULES(IMAGING, ImageMagick, [imagemagick_found=yes], [imagemagick_found=no])
|
||||
PKG_CHECK_MODULES(IMAGING, pixman-1, [pixman_found=yes], [pixman_found=no])
|
||||
if test "$pixman_found" != "yes"; then
|
||||
AC_MSG_ERROR([pixman is required for imaging support])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$require_imaging" = "yes"; then
|
||||
if test "$gdkpixbuf_found" != "yes" && test "$imagemagick_found" != "yes"; then
|
||||
AC_MSG_ERROR([gdk-pixbuf or ImageMagick is required for imaging support])
|
||||
fi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([REQUIRE_GDKPIXBUF], [test "$gdkpixbuf_found" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$imagemagick_found" = "yes"])
|
||||
AM_CONDITIONAL([REQUIRE_PIXMAN], [test "$pixman_found" = "yes"])
|
||||
AC_SUBST(IMAGING_CFLAGS)
|
||||
AC_SUBST(IMAGING_LIBS)
|
||||
|
||||
@@ -260,7 +286,7 @@ fi
|
||||
# Restore gnu89 inline semantics on gcc 4.3 and newer
|
||||
saved_cflags="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fgnu89-inline"
|
||||
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]), inline_cflags="-fgnu89-inline", inline_cflags="")
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[]])], inline_cflags="-fgnu89-inline", inline_cflags="")
|
||||
CFLAGS="$saved_cflags"
|
||||
|
||||
AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default visibility])
|
||||
@@ -268,10 +294,8 @@ AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes
|
||||
AC_SUBST(AM_CFLAGS)
|
||||
|
||||
if test "$require_imaging" = "yes"; then
|
||||
if test x$gdkpixbuf_found != no; then
|
||||
AC_MSG_NOTICE([** Using gdk-pixbuf for imaging])
|
||||
else
|
||||
AC_MSG_NOTICE([** Using ImageMagick for imaging])
|
||||
if test x$pixman_found != no; then
|
||||
AC_MSG_NOTICE([** Using pixman for imaging])
|
||||
fi
|
||||
else
|
||||
AC_MSG_NOTICE([ Imaging support disabled])
|
||||
@@ -337,6 +361,11 @@ if test x$enable_aes2660 != xno ; then
|
||||
else
|
||||
AC_MSG_NOTICE([ aes2660 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes3500 != xno ; then
|
||||
AC_MSG_NOTICE([** aes3500 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes3500 driver disabled])
|
||||
fi
|
||||
if test x$enable_aes4000 != xno ; then
|
||||
AC_MSG_NOTICE([** aes4000 driver enabled])
|
||||
else
|
||||
@@ -352,6 +381,21 @@ if test x$enable_vfs301 != xno ; then
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs301 driver disabled])
|
||||
fi
|
||||
if test x$enable_vfs5011 != xno ; then
|
||||
AC_MSG_NOTICE([** vfs5011 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ vfs5011 driver disabled])
|
||||
fi
|
||||
if test x$enable_upektc_img != xno ; then
|
||||
AC_MSG_NOTICE([** upektc_img driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ upektc_img driver disabled])
|
||||
fi
|
||||
if test x$enable_etes603 != xno ; then
|
||||
AC_MSG_NOTICE([** etes603 driver enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ etes603 driver disabled])
|
||||
fi
|
||||
if test x$require_aeslib != xno ; then
|
||||
AC_MSG_NOTICE([** aeslib helper functions enabled])
|
||||
else
|
||||
@@ -362,6 +406,11 @@ if test x$require_aesX660 != xno ; then
|
||||
else
|
||||
AC_MSG_NOTICE([ aesX660 common routines disabled])
|
||||
fi
|
||||
if test x$require_aes3k != xno ; then
|
||||
AC_MSG_NOTICE([** aes3k common routines enabled])
|
||||
else
|
||||
AC_MSG_NOTICE([ aes3k common routines disabled])
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
INCLUDES = -I$(top_srcdir)
|
||||
AM_CFLAGS = -I$(top_srcdir)
|
||||
noinst_PROGRAMS = verify_live enroll verify img_capture cpp-test
|
||||
|
||||
verify_live_SOURCES = verify_live.c
|
||||
|
||||
@@ -12,11 +12,15 @@ AES1660_SRC = drivers/aes1660.c drivers/aes1660.h
|
||||
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
|
||||
AES2550_SRC = drivers/aes2550.c drivers/aes2550.h
|
||||
AES2660_SRC = drivers/aes2660.c drivers/aes2660.h
|
||||
AES3500_SRC = drivers/aes3500.c
|
||||
AES4000_SRC = drivers/aes4000.c
|
||||
FDU2000_SRC = drivers/fdu2000.c
|
||||
VCOM5S_SRC = drivers/vcom5s.c
|
||||
VFS101_SRC = drivers/vfs101.c
|
||||
VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h drivers/vfs301_proto_fragments.h
|
||||
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
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(UPEKE2_SRC) \
|
||||
@@ -29,17 +33,22 @@ EXTRA_DIST = \
|
||||
$(AES2501_SRC) \
|
||||
$(AES2550_SRC) \
|
||||
$(AES2660_SRC) \
|
||||
$(AES3500_SRC) \
|
||||
$(AES4000_SRC) \
|
||||
$(FDU2000_SRC) \
|
||||
$(VCOM5S_SRC) \
|
||||
$(VFS101_SRC) \
|
||||
$(VFS301_SRC) \
|
||||
$(VFS5011_SRC) \
|
||||
$(UPEKTC_IMG_SRC) \
|
||||
$(ETES603_SRC) \
|
||||
drivers/aesx660.c \
|
||||
drivers/aesx660.h \
|
||||
drivers/aes3k.c \
|
||||
drivers/aes3k.h \
|
||||
drivers/driver_ids.h \
|
||||
aeslib.c aeslib.h \
|
||||
imagemagick.c \
|
||||
gdkpixbuf.c \
|
||||
pixman.c \
|
||||
60-fprint-autosuspend.rules
|
||||
|
||||
DRIVER_SRC =
|
||||
@@ -87,8 +96,8 @@ libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
|
||||
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
|
||||
|
||||
fprint_list_udev_rules_SOURCES = fprint-list-udev-rules.c
|
||||
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
|
||||
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la
|
||||
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
|
||||
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la $(GLIB_LIBS)
|
||||
|
||||
udev_rules_DATA = 60-fprint-autosuspend.rules
|
||||
|
||||
@@ -145,6 +154,10 @@ if ENABLE_AES2660
|
||||
DRIVER_SRC += $(AES2660_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES3500
|
||||
DRIVER_SRC += $(AES3500_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_AES4000
|
||||
DRIVER_SRC += $(AES4000_SRC)
|
||||
endif
|
||||
@@ -157,14 +170,20 @@ if ENABLE_VFS301
|
||||
DRIVER_SRC += $(VFS301_SRC)
|
||||
endif
|
||||
|
||||
if REQUIRE_IMAGEMAGICK
|
||||
OTHER_SRC += imagemagick.c
|
||||
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
|
||||
libfprint_la_LIBADD += $(IMAGING_LIBS)
|
||||
if ENABLE_VFS5011
|
||||
DRIVER_SRC += $(VFS5011_SRC)
|
||||
endif
|
||||
|
||||
if REQUIRE_GDKPIXBUF
|
||||
OTHER_SRC += gdkpixbuf.c
|
||||
if ENABLE_UPEKTC_IMG
|
||||
DRIVER_SRC += $(UPEKTC_IMG_SRC)
|
||||
endif
|
||||
|
||||
if ENABLE_ETES603
|
||||
DRIVER_SRC += $(ETES603_SRC)
|
||||
endif
|
||||
|
||||
if REQUIRE_PIXMAN
|
||||
OTHER_SRC += pixman.c
|
||||
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
|
||||
libfprint_la_LIBADD += $(IMAGING_LIBS)
|
||||
endif
|
||||
@@ -177,6 +196,10 @@ if REQUIRE_AESX660
|
||||
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
|
||||
endif
|
||||
|
||||
if REQUIRE_AES3K
|
||||
OTHER_SRC += drivers/aes3k.c drivers/aes3k.h
|
||||
endif
|
||||
|
||||
libfprint_la_SOURCES = \
|
||||
fp_internal.h \
|
||||
async.c \
|
||||
|
||||
@@ -158,127 +158,268 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
|
||||
continue_write_regv(wdata);
|
||||
}
|
||||
|
||||
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
|
||||
unsigned char *output)
|
||||
static inline unsigned char aes_get_pixel(struct aes_stripe *frame,
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int frame_width,
|
||||
unsigned int frame_height)
|
||||
{
|
||||
size_t row, column;
|
||||
unsigned char ret;
|
||||
|
||||
for (column = 0; column < width; column++) {
|
||||
for (row = 0; row < height; row += 2) {
|
||||
output[width * row + column] = (*input & 0x0f) * 17;
|
||||
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
|
||||
input++;
|
||||
ret = frame->data[x * (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* find overlapping parts of frames */
|
||||
static unsigned int find_overlap(unsigned char *first_frame,
|
||||
unsigned char *second_frame, unsigned int *min_error,
|
||||
unsigned int frame_width, unsigned int frame_height)
|
||||
{
|
||||
unsigned int dy;
|
||||
unsigned int not_overlapped_height = 0;
|
||||
/* 255 is highest brightness value for an 8bpp image */
|
||||
*min_error = 255 * frame_width * frame_height;
|
||||
for (dy = 0; dy < frame_height; dy++) {
|
||||
/* Calculating difference (error) between parts of frames */
|
||||
unsigned int i;
|
||||
unsigned int error = 0;
|
||||
for (i = 0; i < frame_width * (frame_height - dy); i++) {
|
||||
/* Using ? operator to avoid abs function */
|
||||
error += first_frame[i] > second_frame[i] ?
|
||||
(first_frame[i] - second_frame[i]) :
|
||||
(second_frame[i] - first_frame[i]);
|
||||
}
|
||||
|
||||
/* Normalize error */
|
||||
error *= 15;
|
||||
error /= i;
|
||||
if (error < *min_error) {
|
||||
*min_error = error;
|
||||
not_overlapped_height = dy;
|
||||
}
|
||||
first_frame += frame_width;
|
||||
}
|
||||
|
||||
return not_overlapped_height;
|
||||
}
|
||||
|
||||
/* assemble a series of frames into a single image */
|
||||
static unsigned int assemble(GSList *list_entry, size_t num_stripes,
|
||||
unsigned int aes_calc_delta(GSList *stripes, size_t num_stripes,
|
||||
unsigned int frame_width, unsigned int frame_height,
|
||||
unsigned char *output, gboolean reverse, unsigned int *errors_sum)
|
||||
gboolean reverse)
|
||||
{
|
||||
uint8_t *assembled = output;
|
||||
int frame;
|
||||
uint32_t image_height = frame_height;
|
||||
unsigned int min_error, frame_size = frame_width * frame_height;
|
||||
*errors_sum = 0;
|
||||
GSList *list_entry = stripes;
|
||||
GTimer *timer;
|
||||
int frame = 1;
|
||||
int height = 0;
|
||||
struct aes_stripe *prev_stripe = list_entry->data;
|
||||
unsigned int min_error;
|
||||
|
||||
if (reverse)
|
||||
output += (num_stripes - 1) * frame_size;
|
||||
for (frame = 0; frame < num_stripes; frame++) {
|
||||
aes_assemble_image(list_entry->data, frame_width, frame_height, output);
|
||||
list_entry = g_slist_next(list_entry);
|
||||
|
||||
if (reverse)
|
||||
output -= frame_size;
|
||||
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
|
||||
output += frame_size;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Detecting where frames overlaped */
|
||||
output = assembled;
|
||||
for (frame = 1; frame < num_stripes; frame++) {
|
||||
int not_overlapped;
|
||||
if (fx > frame_width)
|
||||
return;
|
||||
|
||||
output += frame_size;
|
||||
not_overlapped = find_overlap(assembled, output, &min_error,
|
||||
frame_width, frame_height);
|
||||
*errors_sum += min_error;
|
||||
image_height += not_overlapped;
|
||||
assembled += frame_width * not_overlapped;
|
||||
memcpy(assembled, output, frame_size);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return image_height;
|
||||
}
|
||||
|
||||
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
|
||||
unsigned int frame_width, unsigned int frame_height)
|
||||
unsigned int frame_width, unsigned int frame_height, unsigned int img_width)
|
||||
{
|
||||
size_t final_size;
|
||||
GSList *stripe;
|
||||
struct fp_img *img;
|
||||
unsigned int frame_size = frame_width * frame_height;
|
||||
unsigned int errors_sum, r_errors_sum;
|
||||
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);
|
||||
|
||||
/* create buffer big enough for max image */
|
||||
img = fpi_img_new(stripes_len * frame_size);
|
||||
/* Calculate height */
|
||||
i = 0;
|
||||
stripe = stripes;
|
||||
|
||||
img->flags = FP_IMG_COLORS_INVERTED;
|
||||
img->height = assemble(stripes, stripes_len,
|
||||
frame_width, frame_height,
|
||||
img->data, FALSE, &errors_sum);
|
||||
img->height = assemble(stripes, stripes_len,
|
||||
frame_width, frame_height,
|
||||
img->data, TRUE, &r_errors_sum);
|
||||
/* No offset for 1st image */
|
||||
aes_stripe = stripe->data;
|
||||
aes_stripe->delta_x = 0;
|
||||
aes_stripe->delta_y = 0;
|
||||
do {
|
||||
aes_stripe = stripe->data;
|
||||
|
||||
if (r_errors_sum > errors_sum) {
|
||||
img->height = assemble(stripes, stripes_len,
|
||||
frame_width, frame_height,
|
||||
img->data, FALSE, &errors_sum);
|
||||
img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
|
||||
fp_dbg("normal scan direction");
|
||||
} else {
|
||||
fp_dbg("reversed scan direction");
|
||||
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;
|
||||
}
|
||||
|
||||
/* now that overlap has been removed, resize output image buffer */
|
||||
final_size = img->height * frame_width;
|
||||
img = fpi_img_resize(img, final_size);
|
||||
img->width = frame_width;
|
||||
/* 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,17 +27,24 @@ struct aes_regwrite {
|
||||
unsigned char value;
|
||||
};
|
||||
|
||||
struct aes_stripe {
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
|
||||
void *user_data);
|
||||
|
||||
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);
|
||||
|
||||
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
|
||||
unsigned char *output);
|
||||
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 frame_width, unsigned int frame_height, unsigned int img_width);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -412,3 +412,101 @@ void fpi_drvcb_identify_stopped(struct fp_dev *dev)
|
||||
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
|
||||
fp_capture_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
if (!drv->capture_start)
|
||||
return -ENOTSUP;
|
||||
|
||||
dev->state = DEV_STATE_CAPTURE_STARTING;
|
||||
dev->capture_cb = callback;
|
||||
dev->capture_cb_data = user_data;
|
||||
dev->unconditional_capture = unconditional;
|
||||
|
||||
r = drv->capture_start(dev);
|
||||
if (r < 0) {
|
||||
dev->capture_cb = NULL;
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
fp_err("failed to start verification, error %d", r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when capture has started */
|
||||
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("");
|
||||
BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->capture_cb)
|
||||
dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_CAPTURING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers call this to report a capture result (which might mark completion) */
|
||||
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
|
||||
struct fp_img *img)
|
||||
{
|
||||
fp_dbg("result %d", result);
|
||||
BUG_ON(dev->state != DEV_STATE_CAPTURING);
|
||||
if (result < 0 || result == FP_CAPTURE_COMPLETE)
|
||||
dev->state = DEV_STATE_CAPTURE_DONE;
|
||||
|
||||
if (dev->capture_cb)
|
||||
dev->capture_cb(dev, result, img, dev->capture_cb_data);
|
||||
else
|
||||
fp_dbg("ignoring capture result as no callback is subscribed");
|
||||
}
|
||||
|
||||
/* Drivers call this when capture has stopped */
|
||||
void fpi_drvcb_capture_stopped(struct fp_dev *dev)
|
||||
{
|
||||
fp_dbg("");
|
||||
BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
if (dev->capture_stop_cb)
|
||||
dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
|
||||
}
|
||||
|
||||
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
|
||||
fp_capture_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv = dev->drv;
|
||||
int r;
|
||||
|
||||
fp_dbg("");
|
||||
BUG_ON(dev->state != DEV_STATE_ERROR
|
||||
&& dev->state != DEV_STATE_CAPTURING
|
||||
&& dev->state != DEV_STATE_CAPTURE_DONE);
|
||||
|
||||
dev->capture_cb = NULL;
|
||||
dev->capture_stop_cb = callback;
|
||||
dev->capture_stop_cb_data = user_data;
|
||||
dev->state = DEV_STATE_CAPTURE_STOPPING;
|
||||
|
||||
if (!drv->capture_start)
|
||||
return -ENOTSUP;
|
||||
if (!drv->capture_stop) {
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
fpi_drvcb_capture_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->capture_stop(dev);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop verification");
|
||||
dev->capture_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -349,6 +349,9 @@ static struct fp_driver * const primitive_drivers[] = {
|
||||
};
|
||||
|
||||
static struct fp_img_driver * const img_drivers[] = {
|
||||
#ifdef ENABLE_AES3500
|
||||
&aes3500_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_AES4000
|
||||
&aes4000_driver,
|
||||
#endif
|
||||
@@ -383,9 +386,18 @@ static struct fp_img_driver * const img_drivers[] = {
|
||||
#ifdef ENABLE_VFS301
|
||||
&vfs301_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_VFS5011
|
||||
&vfs5011_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKTC
|
||||
&upektc_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKTC_IMG
|
||||
&upektc_img_driver,
|
||||
#endif
|
||||
#ifdef ENABLE_ETES603
|
||||
&etes603_driver,
|
||||
#endif
|
||||
/*#ifdef ENABLE_FDU2000
|
||||
&fdu2000_driver,
|
||||
#endif
|
||||
@@ -801,7 +813,7 @@ static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
|
||||
*/
|
||||
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
|
||||
{
|
||||
return dev->drv->type == DRIVER_IMAGING;
|
||||
return dev->drv->capture_start != NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
@@ -816,38 +828,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
|
||||
return dev->drv->identify_start != NULL;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Captures an \ref img "image" from a device. The returned image is the raw
|
||||
* image provided by the device, you may wish to \ref img_std "standardize" it.
|
||||
*
|
||||
* If set, the <tt>unconditional</tt> flag indicates that the device should
|
||||
* capture an image unconditionally, regardless of whether a finger is there
|
||||
* or not. If unset, this function will block until a finger is detected on
|
||||
* the sensor.
|
||||
*
|
||||
* \param dev the device
|
||||
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
|
||||
* \param image a location to return the captured image. Must be freed with
|
||||
* fp_img_free() after use.
|
||||
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
|
||||
* unconditional flag was set but the device does not support this, or that the
|
||||
* device does not support imaging.
|
||||
* \sa fp_dev_supports_imaging()
|
||||
*/
|
||||
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
|
||||
struct fp_img **image)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
|
||||
if (!imgdev) {
|
||||
fp_dbg("image capture on non-imaging device");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
//return fpi_imgdev_capture(imgdev, unconditional, image);
|
||||
/* FIXME reimplement async */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/** \ingroup dev
|
||||
* Gets the expected width of images that will be captured from the device.
|
||||
* This function will return -1 for devices that are not
|
||||
|
||||
160
libfprint/data.c
160
libfprint/data.c
@@ -95,22 +95,33 @@ static const char *finger_num_to_str(enum fp_finger finger)
|
||||
#endif
|
||||
|
||||
static struct fp_print_data *print_data_new(uint16_t driver_id,
|
||||
uint32_t devtype, enum fp_print_data_type type, size_t length)
|
||||
uint32_t devtype, enum fp_print_data_type type)
|
||||
{
|
||||
struct fp_print_data *data = g_malloc(sizeof(*data) + length);
|
||||
fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
|
||||
memset(data, 0, sizeof(*data));
|
||||
struct fp_print_data *data = g_malloc0(sizeof(*data));
|
||||
fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
|
||||
data->driver_id = driver_id;
|
||||
data->devtype = devtype;
|
||||
data->type = type;
|
||||
data->length = length;
|
||||
return data;
|
||||
}
|
||||
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
|
||||
void fpi_print_data_item_free(struct fp_print_data_item *item)
|
||||
{
|
||||
g_free(item);
|
||||
}
|
||||
|
||||
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
|
||||
{
|
||||
struct fp_print_data_item *item = g_malloc(sizeof(*item) + length);
|
||||
item->length = length;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
|
||||
{
|
||||
return print_data_new(dev->drv->id, dev->devtype,
|
||||
fpi_driver_get_data_type(dev->drv), length);
|
||||
fpi_driver_get_data_type(dev->drv));
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
@@ -125,27 +136,115 @@ struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
|
||||
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
|
||||
unsigned char **ret)
|
||||
{
|
||||
struct fpi_print_data_fp1 *buf;
|
||||
size_t buflen;
|
||||
struct fpi_print_data_fp2 *out_data;
|
||||
struct fpi_print_data_item_fp2 *out_item;
|
||||
struct fp_print_data_item *item;
|
||||
size_t buflen = 0;
|
||||
GSList *list_item;
|
||||
unsigned char *buf;
|
||||
|
||||
fp_dbg("");
|
||||
|
||||
buflen = sizeof(*buf) + data->length;
|
||||
buf = malloc(buflen);
|
||||
if (!buf)
|
||||
return 0;
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
buflen += sizeof(*out_item);
|
||||
buflen += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
buflen += sizeof(*out_data);
|
||||
out_data = g_malloc(buflen);
|
||||
|
||||
*ret = (unsigned char *) out_data;
|
||||
buf = out_data->data;
|
||||
out_data->prefix[0] = 'F';
|
||||
out_data->prefix[1] = 'P';
|
||||
out_data->prefix[2] = '2';
|
||||
out_data->driver_id = GUINT16_TO_LE(data->driver_id);
|
||||
out_data->devtype = GUINT32_TO_LE(data->devtype);
|
||||
out_data->data_type = data->type;
|
||||
|
||||
list_item = data->prints;
|
||||
while (list_item) {
|
||||
item = list_item->data;
|
||||
out_item = (struct fpi_print_data_item_fp2 *)buf;
|
||||
out_item->length = GUINT32_TO_LE(item->length);
|
||||
/* FIXME: fp_print_data_item->data content is not endianess agnostic */
|
||||
memcpy(out_item->data, item->data, item->length);
|
||||
buf += sizeof(*out_item);
|
||||
buf += item->length;
|
||||
list_item = g_slist_next(list_item);
|
||||
}
|
||||
|
||||
*ret = (unsigned char *) buf;
|
||||
buf->prefix[0] = 'F';
|
||||
buf->prefix[1] = 'P';
|
||||
buf->prefix[2] = '1';
|
||||
buf->driver_id = GUINT16_TO_LE(data->driver_id);
|
||||
buf->devtype = GUINT32_TO_LE(data->devtype);
|
||||
buf->data_type = data->type;
|
||||
memcpy(buf->data, data->data, data->length);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t print_data_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
print_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
item = fpi_print_data_item_new(print_data_len);
|
||||
/* FIXME: fp_print_data->data content is not endianess agnostic */
|
||||
memcpy(item->data, raw->data, print_data_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
size_t total_data_len, item_len;
|
||||
struct fp_print_data *data;
|
||||
struct fp_print_data_item *item;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
unsigned char *raw_buf;
|
||||
struct fpi_print_data_item_fp2 *raw_item;
|
||||
|
||||
total_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type);
|
||||
raw_buf = raw->data;
|
||||
while (total_data_len) {
|
||||
if (total_data_len < sizeof(*raw_item))
|
||||
break;
|
||||
total_data_len -= sizeof(*raw_item);
|
||||
|
||||
raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
|
||||
item_len = GUINT32_FROM_LE(raw_item->length);
|
||||
fp_dbg("item len %d, total_data_len %d", item_len, total_data_len);
|
||||
if (total_data_len < item_len) {
|
||||
fp_err("corrupted fingerprint data");
|
||||
break;
|
||||
}
|
||||
total_data_len -= item_len;
|
||||
|
||||
item = fpi_print_data_item_new(item_len);
|
||||
/* FIXME: fp_print_data->data content is not endianess agnostic */
|
||||
memcpy(item->data, raw_item->data, item_len);
|
||||
data->prints = g_slist_prepend(data->prints, item);
|
||||
|
||||
raw_buf += sizeof(*raw_item);
|
||||
raw_buf += item_len;
|
||||
}
|
||||
|
||||
if (g_slist_length(data->prints) == 0) {
|
||||
fp_print_data_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/** \ingroup print_data
|
||||
* Load a stored print from a data buffer. The contents of said buffer must
|
||||
* be the untouched contents of a buffer previously supplied to you by the
|
||||
@@ -158,24 +257,21 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
|
||||
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf;
|
||||
size_t print_data_len;
|
||||
struct fp_print_data *data;
|
||||
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
|
||||
|
||||
fp_dbg("buffer size %zd", buflen);
|
||||
if (buflen < sizeof(*raw))
|
||||
return NULL;
|
||||
|
||||
if (strncmp(raw->prefix, "FP1", 3) != 0) {
|
||||
if (strncmp(raw->prefix, "FP1", 3) == 0) {
|
||||
return fpi_print_data_from_fp1_data(buf, buflen);
|
||||
} else if (strncmp(raw->prefix, "FP2", 3) == 0) {
|
||||
return fpi_print_data_from_fp2_data(buf, buflen);
|
||||
} else {
|
||||
fp_dbg("bad header prefix");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
print_data_len = buflen - sizeof(*raw);
|
||||
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
|
||||
GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len);
|
||||
memcpy(data->data, raw->data, print_data_len);
|
||||
return data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
|
||||
@@ -406,6 +502,8 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
|
||||
*/
|
||||
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
|
||||
{
|
||||
if (data)
|
||||
g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -609,13 +609,24 @@ 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_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
|
||||
g_slist_free_full(aesdev->strips, g_free);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
@@ -829,13 +840,9 @@ struct fp_img_driver aes1610_driver = {
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = 128,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
/* temporarily lowered until we sort out image processing code
|
||||
* binarized scan quality is good, minutiae detection is accurate,
|
||||
* it's just that we get fewer minutiae than other scanners (less scanning
|
||||
* area) */
|
||||
.bz3_threshold = 10,
|
||||
.bz3_threshold = 50,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
|
||||
@@ -48,17 +48,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
}
|
||||
|
||||
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
|
||||
if (!aesdev)
|
||||
return -ENOMEM;
|
||||
|
||||
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
|
||||
if (!aesdev->buffer) {
|
||||
g_free(aesdev);
|
||||
dev->priv = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
aesdev->h_scale_factor = SCALE_FACTOR;
|
||||
aesdev->init_seqs[0] = aes1660_init_1;
|
||||
aesdev->init_seqs_len[0] = array_n_elements(aes1660_init_1);
|
||||
aesdev->init_seqs[1] = aes1660_init_2;
|
||||
@@ -111,13 +101,8 @@ struct fp_img_driver aes1660_driver = {
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = FRAME_WIDTH * SCALE_FACTOR,
|
||||
|
||||
/* temporarily lowered until we sort out image processing code
|
||||
* binarized scan quality is good, minutiae detection is accurate,
|
||||
* it's just that we get fewer minutiae than other scanners (less scanning
|
||||
* area) */
|
||||
.bz3_threshold = 25,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
.bz3_threshold = 70,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
|
||||
@@ -481,10 +481,21 @@ 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_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
|
||||
g_slist_free_full(aesdev->strips, g_free);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
@@ -498,10 +509,13 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
|
||||
} else {
|
||||
/* obtain next strip */
|
||||
/* FIXME: would preallocating strip buffers be a decent optimization? */
|
||||
stripdata = g_malloc(192 * 8);
|
||||
struct aes_stripe *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe));
|
||||
stripe->delta_x = 0;
|
||||
stripe->delta_y = 0;
|
||||
stripdata = stripe->data;
|
||||
memcpy(stripdata, data + 1, 192*8);
|
||||
aesdev->no_finger_cnt = 0;
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
|
||||
aesdev->strips_len++;
|
||||
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
|
||||
@@ -867,7 +881,7 @@ struct fp_img_driver aes2501_driver = {
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = 192,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
|
||||
@@ -204,6 +204,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;
|
||||
int len;
|
||||
|
||||
if (data[0] != AES2550_EDATA_MAGIC) {
|
||||
@@ -214,15 +215,16 @@ 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);
|
||||
}
|
||||
stripdata = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2); /* 4 bits per pixel */
|
||||
if (!stripdata) {
|
||||
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
||||
return -ENOMEM;
|
||||
}
|
||||
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bits per pixel */
|
||||
stripe->delta_x = (int8_t)data[6];
|
||||
stripe->delta_y = -(int8_t)data[7];
|
||||
stripdata = stripe->data;
|
||||
memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2);
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
|
||||
aesdev->strips_len++;
|
||||
|
||||
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -246,12 +248,13 @@ static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
|
||||
struct aes2550_dev *aesdev = dev->priv;
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
(transfer->length == transfer->actual_length) &&
|
||||
aesdev->strips_len) {
|
||||
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_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
|
||||
g_slist_free_full(aesdev->strips, g_free);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
@@ -641,7 +644,7 @@ struct fp_img_driver aes2550_driver = {
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = 192,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
|
||||
@@ -47,18 +47,8 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
}
|
||||
|
||||
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
|
||||
if (!aesdev)
|
||||
return -ENOMEM;
|
||||
|
||||
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
|
||||
if (!aesdev->buffer) {
|
||||
g_free(aesdev);
|
||||
dev->priv = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* No scaling for AES2660 */
|
||||
aesdev->h_scale_factor = 1;
|
||||
aesdev->init_seqs[0] = aes2660_init_1;
|
||||
aesdev->init_seqs_len[0] = array_n_elements(aes2660_init_1);
|
||||
aesdev->init_seqs[1] = aes2660_init_2;
|
||||
@@ -112,7 +102,7 @@ struct fp_img_driver aes2660_driver = {
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = FRAME_WIDTH,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
|
||||
188
libfprint/drivers/aes3500.c
Normal file
188
libfprint/drivers/aes3500.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* AuthenTec AES3500 driver for libfprint
|
||||
*
|
||||
* AES3500 is a press-typed sensor, which captures image in 128x128
|
||||
* pixels.
|
||||
*
|
||||
* Thanks Rafael Toledo for the Windows driver and the help.
|
||||
*
|
||||
* This work is derived from Daniel Drake's AES4000 driver.
|
||||
*
|
||||
* Copyright (C) 2011-2013 Juvenn Woo <machese@gmail.com>
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes3500"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "aes3k.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define DATA_BUFLEN 0x2089
|
||||
|
||||
/* image size = FRAME_WIDTH x FRAME_WIDTH */
|
||||
#define FRAME_WIDTH 128
|
||||
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
|
||||
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
|
||||
#define ENLARGE_FACTOR 2
|
||||
|
||||
|
||||
static struct aes_regwrite init_reqs[] = {
|
||||
/* master reset */
|
||||
{ 0x80, 0x01 },
|
||||
{ 0, 0 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
{ 0x81, 0x00 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
/* scan reset */
|
||||
{ 0x80, 0x02 },
|
||||
{ 0, 0 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
/* disable register buffering */
|
||||
{ 0x80, 0x04 },
|
||||
{ 0, 0 },
|
||||
{ 0x80, 0x00 },
|
||||
{ 0, 0 },
|
||||
|
||||
{ 0x81, 0x00 },
|
||||
{ 0, 0 },
|
||||
/* windows driver reads registers now (81 02) */
|
||||
{ 0x80, 0x00 },
|
||||
{ 0x81, 0x00 },
|
||||
|
||||
/* set excitation bias current: 2mhz drive ring frequency,
|
||||
* 4V drive ring voltage, 16.5mA excitation bias */
|
||||
{ 0x82, 0x04 },
|
||||
|
||||
/* continuously sample drive ring for finger detection,
|
||||
* 62.50ms debounce delay */
|
||||
{ 0x83, 0x13 },
|
||||
|
||||
{ 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */
|
||||
{ 0x85, 0x3d }, /* set calibration capacitance */
|
||||
{ 0x86, 0x03 }, /* detect drive voltage */
|
||||
{ 0x87, 0x01 }, /* set detection frequency to 125khz */
|
||||
{ 0x88, 0x02 }, /* set column scan period */
|
||||
{ 0x89, 0x02 }, /* set measure drive */
|
||||
{ 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */
|
||||
{ 0x8b, 0x33 }, /* set matrix pattern */
|
||||
{ 0x8c, 0x0f }, /* set demodulation phase 1 */
|
||||
{ 0x8d, 0x04 }, /* set demodulation phase 2 */
|
||||
{ 0x8e, 0x23 }, /* set sensor gain */
|
||||
{ 0x8f, 0x07 }, /* set image parameters */
|
||||
{ 0x90, 0x00 }, /* carrier offset null */
|
||||
{ 0x91, 0x1c }, /* set A/D reference high */
|
||||
{ 0x92, 0x08 }, /* set A/D reference low */
|
||||
{ 0x93, 0x00 }, /* set start row to 0 */
|
||||
{ 0x94, 0x07 }, /* set end row */
|
||||
{ 0x95, 0x00 }, /* set start column to 0 */
|
||||
{ 0x96, 0x1f }, /* set end column */
|
||||
{ 0x97, 0x04 }, /* data format and thresholds */
|
||||
{ 0x98, 0x28 }, /* image data control */
|
||||
{ 0x99, 0x00 }, /* disable general purpose outputs */
|
||||
{ 0x9a, 0x0b }, /* set initial scan state */
|
||||
{ 0x9b, 0x00 }, /* clear challenge word bits */
|
||||
{ 0x9c, 0x00 }, /* clear challenge word bits */
|
||||
{ 0x9d, 0x09 }, /* set some challenge word bits */
|
||||
{ 0x9e, 0x53 }, /* clear challenge word bits */
|
||||
{ 0x9f, 0x6b }, /* set some challenge word bits */
|
||||
{ 0, 0 },
|
||||
|
||||
{ 0x80, 0x00 },
|
||||
{ 0x81, 0x00 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x04 },
|
||||
{ 0, 0 },
|
||||
{ 0x81, 0x00 },
|
||||
};
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
int r;
|
||||
struct aes3k_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0)
|
||||
fp_err("could not claim interface 0");
|
||||
|
||||
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);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x5731 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes3500_driver = {
|
||||
.driver = {
|
||||
.id = AES3500_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES3500",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
|
||||
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
|
||||
|
||||
/* temporarily lowered until image quality improves */
|
||||
.bz3_threshold = 9,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = aes3k_dev_activate,
|
||||
.deactivate = aes3k_dev_deactivate,
|
||||
};
|
||||
|
||||
169
libfprint/drivers/aes3k.c
Normal file
169
libfprint/drivers/aes3k.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* AuthenTec AES3500/AES4000 common routines
|
||||
*
|
||||
* The AES3500 and AES4000 sensors are press-typed, and could capture
|
||||
* fingerprint images in 128x128 and 96x96 pixels respectively. They
|
||||
* share a same communication interface: a number of frames are
|
||||
* transferred and captured, from which a final image could be
|
||||
* assembled. Each frame has fixed height of 16 pixels.
|
||||
*
|
||||
* As the imaging area is a bit small, only a part of finger could be
|
||||
* captured, the detected minutiae are not so many that the NBIS
|
||||
* matching works not so good. The verification rate is very low at the
|
||||
* moment.
|
||||
*
|
||||
* This work is derived from Daniel Drake's AES4000 driver.
|
||||
*
|
||||
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes3k"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "aes3k.h"
|
||||
|
||||
#define CTRL_TIMEOUT 1000
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
|
||||
static void do_capture(struct fp_img_dev *dev);
|
||||
|
||||
static void aes3k_assemble_image(unsigned char *input, size_t width, size_t height,
|
||||
unsigned char *output)
|
||||
{
|
||||
size_t row, column;
|
||||
|
||||
for (column = 0; column < width; column++) {
|
||||
for (row = 0; row < height; row += 2) {
|
||||
output[width * row + column] = (*input & 0x0f) * 17;
|
||||
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
|
||||
input++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void img_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
unsigned char *ptr = transfer->buffer;
|
||||
struct fp_img *tmp;
|
||||
struct fp_img *img;
|
||||
int i;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
goto err;
|
||||
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto err;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
|
||||
tmp = fpi_img_new(aesdev->frame_width * aesdev->frame_width);
|
||||
tmp->width = aesdev->frame_width;
|
||||
tmp->height = aesdev->frame_width;
|
||||
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
|
||||
for (i = 0; i < aesdev->frame_number; i++) {
|
||||
fp_dbg("frame header byte %02x", *ptr);
|
||||
ptr++;
|
||||
aes3k_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
|
||||
ptr += aesdev->frame_size;
|
||||
}
|
||||
|
||||
/* FIXME: this is an ugly hack to make the image big enough for NBIS
|
||||
* to process reliably */
|
||||
img = fpi_im_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
|
||||
fp_img_free(tmp);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
|
||||
/* FIXME: rather than assuming finger has gone, we should poll regs until
|
||||
* it really has, then restart the capture */
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
|
||||
do_capture(dev);
|
||||
|
||||
err:
|
||||
g_free(transfer->buffer);
|
||||
aesdev->img_trf = NULL;
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void do_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
aesdev->img_trf = libusb_alloc_transfer(0);
|
||||
if (!aesdev->img_trf) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_malloc(aesdev->data_buflen);
|
||||
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
|
||||
aesdev->data_buflen, img_cb, dev, 0);
|
||||
|
||||
r = libusb_submit_transfer(aesdev->img_trf);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(aesdev->img_trf);
|
||||
aesdev->img_trf = NULL;
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
{
|
||||
fpi_imgdev_activate_complete(dev, result);
|
||||
if (result == 0)
|
||||
do_capture(dev);
|
||||
}
|
||||
|
||||
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aes3k_dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
|
||||
/* FIXME: should wait for cancellation to complete before returning
|
||||
* from deactivation, otherwise app may legally exit before we've
|
||||
* cleaned up */
|
||||
if (aesdev->img_trf)
|
||||
libusb_cancel_transfer(aesdev->img_trf);
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
58
libfprint/drivers/aes3k.h
Normal file
58
libfprint/drivers/aes3k.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* AuthenTec AES3500/AES4000 common routines
|
||||
*
|
||||
* The AES3500 and AES4000 sensors are press-typed, and could capture
|
||||
* fingerprint images in 128x128 and 96x96 pixels respectively. They
|
||||
* share a same communication interface: a number of frames are
|
||||
* transferred and captured, from which a final image could be
|
||||
* assembled. Each frame has fixed height of 16 pixels.
|
||||
*
|
||||
* As the imaging area is a bit small, only a part of finger could be
|
||||
* captured, the detected minutiae are not so many that the NBIS
|
||||
* matching works not so good. The verification rate is very low at the
|
||||
* moment.
|
||||
*
|
||||
* This work is derived from Daniel Drake's AES4000 driver.
|
||||
*
|
||||
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AES3K_H
|
||||
#define __AES3K_H
|
||||
|
||||
#define AES3K_FRAME_HEIGHT 16
|
||||
|
||||
struct aes3k_dev {
|
||||
struct libusb_transfer *img_trf;
|
||||
size_t frame_width; /* image size = frame_width x frame_width */
|
||||
size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
|
||||
size_t frame_number; /* number of frames */
|
||||
size_t enlarge_factor;
|
||||
|
||||
size_t data_buflen; /* buffer length of usb bulk transfer */
|
||||
struct aes_regwrite *init_reqs; /* initial values sent to device */
|
||||
size_t init_reqs_len;
|
||||
};
|
||||
|
||||
|
||||
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||
void aes3k_dev_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,12 @@
|
||||
/*
|
||||
* AuthenTec AES4000 driver for libfprint
|
||||
*
|
||||
* AES4000 is a press-typed sensor, which captures image in 96x96
|
||||
* pixels.
|
||||
*
|
||||
* This work is derived from Daniel Drake's AES4000 driver.
|
||||
*
|
||||
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -27,24 +34,19 @@
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "aes3k.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
#define CTRL_TIMEOUT 1000
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
#define DATA_BUFLEN 0x1259
|
||||
#define NR_SUBARRAYS 6
|
||||
#define SUBARRAY_LEN 768
|
||||
#define DATA_BUFLEN 0x1259
|
||||
|
||||
#define IMG_HEIGHT 96
|
||||
#define IMG_WIDTH 96
|
||||
#define ENLARGE_FACTOR 3
|
||||
/* image size = FRAME_WIDTH x FRAME_WIDTH */
|
||||
#define FRAME_WIDTH 96
|
||||
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
|
||||
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
|
||||
#define ENLARGE_FACTOR 3
|
||||
|
||||
struct aes4k_dev {
|
||||
struct libusb_transfer *img_trf;
|
||||
};
|
||||
|
||||
static const struct aes_regwrite init_reqs[] = {
|
||||
static struct aes_regwrite init_reqs[] = {
|
||||
/* master reset */
|
||||
{ 0x80, 0x01 },
|
||||
{ 0, 0 },
|
||||
@@ -119,119 +121,28 @@ static const struct aes_regwrite init_reqs[] = {
|
||||
{ 0x81, 0x00 },
|
||||
};
|
||||
|
||||
static void do_capture(struct fp_img_dev *dev);
|
||||
|
||||
static void img_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
struct aes4k_dev *aesdev = dev->priv;
|
||||
unsigned char *ptr = transfer->buffer;
|
||||
struct fp_img *tmp;
|
||||
struct fp_img *img;
|
||||
int i;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
goto err;
|
||||
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto err;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
|
||||
tmp = fpi_img_new(IMG_WIDTH * IMG_HEIGHT);
|
||||
tmp->width = IMG_WIDTH;
|
||||
tmp->height = IMG_HEIGHT;
|
||||
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
|
||||
for (i = 0; i < NR_SUBARRAYS; i++) {
|
||||
fp_dbg("subarray header byte %02x", *ptr);
|
||||
ptr++;
|
||||
aes_assemble_image(ptr, 96, 16, tmp->data + (i * 96 * 16));
|
||||
ptr += SUBARRAY_LEN;
|
||||
}
|
||||
|
||||
/* FIXME: this is an ugly hack to make the image big enough for NBIS
|
||||
* to process reliably */
|
||||
img = fpi_im_resize(tmp, ENLARGE_FACTOR, ENLARGE_FACTOR);
|
||||
fp_img_free(tmp);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
|
||||
/* FIXME: rather than assuming finger has gone, we should poll regs until
|
||||
* it really has, then restart the capture */
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
|
||||
do_capture(dev);
|
||||
|
||||
err:
|
||||
g_free(transfer->buffer);
|
||||
aesdev->img_trf = NULL;
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void do_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes4k_dev *aesdev = dev->priv;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
aesdev->img_trf = libusb_alloc_transfer(0);
|
||||
if (!aesdev->img_trf) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_malloc(DATA_BUFLEN);
|
||||
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
|
||||
DATA_BUFLEN, img_cb, dev, 0);
|
||||
|
||||
r = libusb_submit_transfer(aesdev->img_trf);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(aesdev->img_trf);
|
||||
aesdev->img_trf = NULL;
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
{
|
||||
fpi_imgdev_activate_complete(dev, result);
|
||||
if (result == 0)
|
||||
do_capture(dev);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
||||
{
|
||||
aes_write_regv(dev, init_reqs, G_N_ELEMENTS(init_reqs), init_reqs_cb, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes4k_dev *aesdev = dev->priv;
|
||||
|
||||
/* FIXME: should wait for cancellation to complete before returning
|
||||
* from deactivation, otherwise app may legally exit before we've
|
||||
* cleaned up */
|
||||
if (aesdev->img_trf)
|
||||
libusb_cancel_transfer(aesdev->img_trf);
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
int r;
|
||||
struct aes3k_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0)
|
||||
fp_err("could not claim interface 0");
|
||||
|
||||
dev->priv = g_malloc0(sizeof(struct aes4k_dev));
|
||||
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);
|
||||
|
||||
return r;
|
||||
@@ -239,11 +150,13 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
g_free(dev->priv);
|
||||
struct aes3k_dev *aesdev = dev->priv;
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x5501 },
|
||||
{ 0, 0, 0, },
|
||||
@@ -258,15 +171,15 @@ struct fp_img_driver aes4000_driver = {
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = IMG_HEIGHT * ENLARGE_FACTOR,
|
||||
.img_width = IMG_WIDTH * ENLARGE_FACTOR,
|
||||
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
|
||||
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
|
||||
|
||||
/* temporarily lowered until image quality improves */
|
||||
.bz3_threshold = 9,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
.activate = aes3k_dev_activate,
|
||||
.deactivate = aes3k_dev_deactivate,
|
||||
};
|
||||
|
||||
|
||||
@@ -273,23 +273,25 @@ enum capture_states {
|
||||
/* Returns number of processed bytes */
|
||||
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
|
||||
{
|
||||
struct aes_stripe *stripe;
|
||||
unsigned char *stripdata;
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct aesX660_dev *aesdev = dev->priv;
|
||||
|
||||
stripdata = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2); /* 4 bits per pixel */
|
||||
if (!stripdata) {
|
||||
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
||||
return 1;
|
||||
}
|
||||
stripe = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bpp */
|
||||
stripdata = stripe->data;
|
||||
|
||||
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
|
||||
data[AESX660_LAST_FRAME_OFFSET]);
|
||||
|
||||
stripe->delta_x = (int8_t)data[AESX660_FRAME_DELTA_X_OFFSET];
|
||||
stripe->delta_y = -(int8_t)data[AESX660_FRAME_DELTA_Y_OFFSET];
|
||||
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);
|
||||
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
|
||||
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
|
||||
aesdev->strips_len++;
|
||||
return (data[AESX660_LAST_FRAME_OFFSET] & AESX660_LAST_FRAME_BIT);
|
||||
} else {
|
||||
@@ -306,22 +308,15 @@ static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
struct fp_img *img, *tmp;
|
||||
struct fp_img *img;
|
||||
|
||||
aesdev->strips = g_slist_reverse(aesdev->strips);
|
||||
tmp = aes_assemble(aesdev->strips, aesdev->strips_len,
|
||||
aesdev->frame_width, FRAME_HEIGHT);
|
||||
img = aes_assemble(aesdev->strips, aesdev->strips_len,
|
||||
aesdev->frame_width, FRAME_HEIGHT, aesdev->frame_width + aesdev->frame_width / 2);
|
||||
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
|
||||
g_slist_free(aesdev->strips);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
if (aesdev->h_scale_factor > 1) {
|
||||
img = fpi_im_resize(tmp, aesdev->h_scale_factor, 1);
|
||||
fp_img_free(tmp);
|
||||
} else {
|
||||
img = tmp;
|
||||
tmp = NULL;
|
||||
}
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
@@ -359,7 +354,7 @@ static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
|
||||
if (aesdev->buffer_size == aesdev->buffer_max) {
|
||||
if (aesdev->buffer_max == AESX660_HEADER_SIZE) {
|
||||
aesdev->buffer_max = aesdev->buffer[AESX660_RESPONSE_SIZE_LSB_OFFSET] +
|
||||
(aesdev->buffer[AESX660_RESPONSE_SIZE_MSB_OFFSEt] << 8) + AESX660_HEADER_SIZE;
|
||||
(aesdev->buffer[AESX660_RESPONSE_SIZE_MSB_OFFSET] << 8) + AESX660_HEADER_SIZE;
|
||||
fp_dbg("Got frame, type %.2x size %.4x",
|
||||
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
|
||||
aesdev->buffer_max);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#define AESX660_HEADER_SIZE 3
|
||||
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
|
||||
#define AESX660_RESPONSE_SIZE_LSB_OFFSET 0x01
|
||||
#define AESX660_RESPONSE_SIZE_MSB_OFFSEt 0x02
|
||||
#define AESX660_RESPONSE_SIZE_MSB_OFFSET 0x02
|
||||
|
||||
#define AESX660_CALIBRATE_RESPONSE 0x06
|
||||
#define AESX660_FINGER_DET_RESPONSE 0x40
|
||||
@@ -35,6 +35,9 @@
|
||||
#define AESX660_LAST_FRAME_OFFSET 0x04
|
||||
#define AESX660_LAST_FRAME_BIT 0x01
|
||||
|
||||
#define AESX660_FRAME_DELTA_X_OFFSET 16
|
||||
#define AESX660_FRAME_DELTA_Y_OFFSET 17
|
||||
|
||||
#define AESX660_IMAGE_OFFSET 43
|
||||
#define AESX660_BULK_TRANSFER_SIZE 4096
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ enum {
|
||||
UPEKE2_ID = 13,
|
||||
AES1660_ID = 14,
|
||||
AES2660_ID = 15,
|
||||
AES3500_ID = 16,
|
||||
UPEKTC_IMG_ID = 17,
|
||||
ETES603_ID = 18,
|
||||
VFS5011_ID = 19,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1513
libfprint/drivers/etes603.c
Normal file
1513
libfprint/drivers/etes603.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,10 @@
|
||||
#define MSG_READ_BUF_SIZE 0x40
|
||||
#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9)
|
||||
|
||||
enum {
|
||||
UPEKE2_2016,
|
||||
};
|
||||
|
||||
struct upeke2_dev {
|
||||
gboolean enroll_passed;
|
||||
gboolean first_verify_iteration;
|
||||
@@ -848,8 +852,7 @@ static struct fpi_ssm *deinitsm_new(struct fp_dev *dev)
|
||||
|
||||
static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
|
||||
{
|
||||
/* Revision 2 is what we're interested in */
|
||||
if (dsc->bcdDevice == 2)
|
||||
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -1069,6 +1072,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
|
||||
size_t data_len)
|
||||
{
|
||||
struct fp_print_data *fdata = NULL;
|
||||
struct fp_print_data_item *item = NULL;
|
||||
int result = -EPROTO;
|
||||
|
||||
if (data_len < sizeof(scan_comp)) {
|
||||
@@ -1077,9 +1081,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
|
||||
fp_err("unrecognised data prefix %x %x %x %x %x",
|
||||
data[0], data[1], data[2], data[3], data[4]);
|
||||
} else {
|
||||
fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
|
||||
memcpy(fdata->data, data + sizeof(scan_comp),
|
||||
fdata = fpi_print_data_new(dev);
|
||||
item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
|
||||
memcpy(item->data, data + sizeof(scan_comp),
|
||||
data_len - sizeof(scan_comp));
|
||||
fdata->prints = g_slist_prepend(fdata->prints, item);
|
||||
|
||||
result = FP_ENROLL_COMPLETE;
|
||||
}
|
||||
@@ -1241,12 +1247,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
|
||||
break;
|
||||
case VERIFY_INIT: ;
|
||||
struct fp_print_data *print = dev->verify_data;
|
||||
size_t data_len = sizeof(verify_hdr) + print->length;
|
||||
struct fp_print_data_item *item = print->prints->data;
|
||||
size_t data_len = sizeof(verify_hdr) + item->length;
|
||||
unsigned char *data = g_malloc(data_len);
|
||||
struct libusb_transfer *transfer;
|
||||
|
||||
memcpy(data, verify_hdr, sizeof(verify_hdr));
|
||||
memcpy(data + sizeof(verify_hdr), print->data, print->length);
|
||||
memcpy(data + sizeof(verify_hdr), item->data, item->length);
|
||||
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
|
||||
verify_init_2803_cb, ssm);
|
||||
g_free(data);
|
||||
@@ -1453,7 +1460,7 @@ static int verify_stop(struct fp_dev *dev, gboolean iterating)
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x147e, .product = 0x2016 },
|
||||
{ .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKE2_2016 },
|
||||
{ 0, 0, 0, }, /* terminating entry */
|
||||
};
|
||||
|
||||
|
||||
674
libfprint/drivers/upektc_img.c
Normal file
674
libfprint/drivers/upektc_img.c
Normal file
@@ -0,0 +1,674 @@
|
||||
/*
|
||||
* UPEK TouchChip driver for libfprint
|
||||
* Copyright (C) 2013 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 "upekts_img"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <aeslib.h>
|
||||
#include <fp_internal.h>
|
||||
|
||||
#include "upektc_img.h"
|
||||
#include "driver_ids.h"
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void start_deactivation(struct fp_img_dev *dev);
|
||||
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
#define CTRL_TIMEOUT 4000
|
||||
#define BULK_TIMEOUT 4000
|
||||
|
||||
#define IMAGE_WIDTH 144
|
||||
#define IMAGE_HEIGHT 384
|
||||
#define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT)
|
||||
|
||||
#define MAX_CMD_SIZE 64
|
||||
#define MAX_RESPONSE_SIZE 2052
|
||||
#define SHORT_RESPONSE_SIZE 64
|
||||
|
||||
struct upekts_img_dev {
|
||||
unsigned char cmd[MAX_CMD_SIZE];
|
||||
unsigned char response[MAX_RESPONSE_SIZE];
|
||||
unsigned char image_bits[IMAGE_SIZE * 2];
|
||||
unsigned char seq;
|
||||
size_t image_size;
|
||||
size_t response_rest;
|
||||
gboolean deactivating;
|
||||
};
|
||||
|
||||
/****** HELPERS ******/
|
||||
|
||||
static const uint16_t crc_table[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||
};
|
||||
|
||||
static uint16_t udf_crc(unsigned char *buffer, size_t size)
|
||||
{
|
||||
uint16_t crc = 0;
|
||||
while (size--)
|
||||
crc = (uint16_t) ((crc << 8) ^
|
||||
crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]);
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void upektc_img_cmd_fix_seq(unsigned char *cmd_buf, unsigned char seq)
|
||||
{
|
||||
uint8_t byte;
|
||||
|
||||
byte = cmd_buf[5];
|
||||
byte &= 0x0f;
|
||||
byte |= (seq << 4);
|
||||
cmd_buf[5] = byte;
|
||||
}
|
||||
|
||||
static void upektc_img_cmd_update_crc(unsigned char *cmd_buf, size_t size)
|
||||
{
|
||||
/* CRC does not cover Ciao prefix (4 bytes) and CRC location (2 bytes) */
|
||||
uint16_t crc = udf_crc(cmd_buf + 4, size - 6);
|
||||
|
||||
cmd_buf[size - 2] = (crc & 0x00ff);
|
||||
cmd_buf[size - 1] = (crc & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
static void upektc_img_submit_req(struct fpi_ssm *ssm,
|
||||
const unsigned char *buf, size_t buf_size, unsigned char seq,
|
||||
libusb_transfer_cb_fn cb)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
||||
int r;
|
||||
|
||||
BUG_ON(buf_size > MAX_CMD_SIZE);
|
||||
|
||||
if (!transfer) {
|
||||
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
|
||||
memcpy(upekdev->cmd, buf, buf_size);
|
||||
upektc_img_cmd_fix_seq(upekdev->cmd, seq);
|
||||
upektc_img_cmd_update_crc(upekdev->cmd, buf_size);
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, upekdev->cmd, buf_size,
|
||||
cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_aborted(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t buf_offset, libusb_transfer_cb_fn cb)
|
||||
{
|
||||
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
int r;
|
||||
|
||||
if (!transfer) {
|
||||
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
BUG_ON(buf_size > MAX_RESPONSE_SIZE);
|
||||
|
||||
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, upekdev->response + buf_offset, buf_size,
|
||||
cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_aborted(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_INIT_CAPTURE,
|
||||
CAPTURE_READ_DATA,
|
||||
CAPTURE_ACK_00_28,
|
||||
CAPTURE_ACK_08,
|
||||
CAPTURE_ACK_FRAME,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
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 {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static int upektc_img_process_image_frame(unsigned char *image_buf, unsigned char *cmd_res)
|
||||
{
|
||||
int offset = 8;
|
||||
int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]);
|
||||
|
||||
len -= 1;
|
||||
if (cmd_res[7] == 0x2c) {
|
||||
len -= 10;
|
||||
offset += 10;
|
||||
}
|
||||
if (cmd_res[7] == 0x20) {
|
||||
len -= 4;
|
||||
}
|
||||
memcpy(image_buf, cmd_res + offset, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned char *data = upekdev->response;
|
||||
struct fp_img *img;
|
||||
size_t response_size;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("request is not completed, %d", transfer->status);
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
if (upekdev->deactivating) {
|
||||
fp_dbg("Deactivate requested\n");
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_dbg("request completed, len: %.4x", transfer->actual_length);
|
||||
if (transfer->actual_length == 0) {
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!upekdev->response_rest) {
|
||||
response_size = ((data[5] & 0x0f) << 8) + data[6];
|
||||
response_size += 9; /* 7 bytes for header, 2 for CRC */
|
||||
if (response_size > transfer->actual_length) {
|
||||
fp_dbg("response_size is %d, actual_length is %d\n",
|
||||
response_size, transfer->actual_length);
|
||||
fp_dbg("Waiting for rest of transfer");
|
||||
BUG_ON(upekdev->response_rest);
|
||||
upekdev->response_rest = response_size - transfer->actual_length;
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
|
||||
return;
|
||||
}
|
||||
}
|
||||
upekdev->response_rest = 0;
|
||||
|
||||
switch (data[4]) {
|
||||
case 0x00:
|
||||
switch (data[7]) {
|
||||
/* No finger */
|
||||
case 0x28:
|
||||
fp_dbg("18th byte is %.2x\n", data[18]);
|
||||
switch (data[18]) {
|
||||
case 0x0c:
|
||||
/* no finger */
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
|
||||
break;
|
||||
case 0x00:
|
||||
/* finger is present! */
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* Image frame with additional info */
|
||||
case 0x2c:
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
/* Plain image frame */
|
||||
case 0x24:
|
||||
upekdev->image_size +=
|
||||
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
|
||||
data);
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_FRAME);
|
||||
break;
|
||||
/* Last image frame */
|
||||
case 0x20:
|
||||
upekdev->image_size +=
|
||||
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
|
||||
data);
|
||||
BUG_ON(upekdev->image_size != IMAGE_SIZE);
|
||||
fp_dbg("Image size is %d\n", upekdev->image_size);
|
||||
img = fpi_img_new(IMAGE_SIZE);
|
||||
memcpy(img->data, upekdev->image_bits, IMAGE_SIZE);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
break;
|
||||
default:
|
||||
fp_err("Uknown response!\n");
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x08:
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_08);
|
||||
break;
|
||||
default:
|
||||
fp_err("Not handled response!\n");
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_run_state(struct fpi_ssm *ssm)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
|
||||
switch (ssm->cur_state) {
|
||||
case CAPTURE_INIT_CAPTURE:
|
||||
upektc_img_submit_req(ssm, upek2020_init_capture, sizeof(upek2020_init_capture),
|
||||
upekdev->seq, capture_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
case CAPTURE_READ_DATA:
|
||||
if (!upekdev->response_rest)
|
||||
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
|
||||
else
|
||||
upektc_img_read_data(ssm, MAX_RESPONSE_SIZE - SHORT_RESPONSE_SIZE,
|
||||
SHORT_RESPONSE_SIZE, capture_read_data_cb);
|
||||
break;
|
||||
case CAPTURE_ACK_00_28:
|
||||
upektc_img_submit_req(ssm, upek2020_ack_00_28, sizeof(upek2020_ack_00_28),
|
||||
upekdev->seq, capture_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
case CAPTURE_ACK_08:
|
||||
upektc_img_submit_req(ssm, upek2020_ack_08, sizeof(upek2020_ack_08),
|
||||
0, capture_reqs_cb);
|
||||
break;
|
||||
case CAPTURE_ACK_FRAME:
|
||||
upektc_img_submit_req(ssm, upek2020_ack_frame, sizeof(upek2020_ack_frame),
|
||||
upekdev->seq, capture_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(struct fpi_ssm *ssm)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
int err = ssm->error;
|
||||
|
||||
fp_dbg("Capture completed, %d", err);
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (upekdev->deactivating)
|
||||
start_deactivation(dev);
|
||||
else if (err)
|
||||
fpi_imgdev_session_error(dev, err);
|
||||
else
|
||||
start_capture(dev);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
struct fpi_ssm *ssm;
|
||||
|
||||
upekdev->image_size = 0;
|
||||
|
||||
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
|
||||
ssm->priv = dev;
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
enum deactivate_states {
|
||||
DEACTIVATE_DEINIT,
|
||||
DEACTIVATE_READ_DEINIT_DATA,
|
||||
DEACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void deactivate_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 {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: process response properly */
|
||||
static void deactivate_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void deactivate_run_state(struct fpi_ssm *ssm)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
|
||||
switch (ssm->cur_state) {
|
||||
case DEACTIVATE_DEINIT:
|
||||
upektc_img_submit_req(ssm, upek2020_deinit, sizeof(upek2020_deinit),
|
||||
upekdev->seq, deactivate_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
case DEACTIVATE_READ_DEINIT_DATA:
|
||||
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, deactivate_read_data_cb);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void deactivate_sm_complete(struct fpi_ssm *ssm)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
int err = ssm->error;
|
||||
|
||||
fp_dbg("Deactivate completed");
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (err) {
|
||||
fpi_imgdev_session_error(dev, err);
|
||||
return;
|
||||
}
|
||||
|
||||
upekdev->deactivating = FALSE;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static void start_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
struct fpi_ssm *ssm;
|
||||
|
||||
upekdev->image_size = 0;
|
||||
|
||||
ssm = fpi_ssm_new(dev->dev, deactivate_run_state, DEACTIVATE_NUM_STATES);
|
||||
ssm->priv = dev;
|
||||
fpi_ssm_start(ssm, deactivate_sm_complete);
|
||||
}
|
||||
|
||||
enum activate_states {
|
||||
ACTIVATE_CONTROL_REQ_1,
|
||||
ACTIVATE_READ_CTRL_RESP_1,
|
||||
ACTIVATE_INIT_1,
|
||||
ACTIVATE_READ_INIT_1_RESP,
|
||||
ACTIVATE_INIT_2,
|
||||
ACTIVATE_READ_INIT_2_RESP,
|
||||
ACTIVATE_CONTROL_REQ_2,
|
||||
ACTIVATE_READ_CTRL_RESP_2,
|
||||
ACTIVATE_INIT_3,
|
||||
ACTIVATE_READ_INIT_3_RESP,
|
||||
ACTIVATE_INIT_4,
|
||||
ACTIVATE_READ_INIT_4_RESP,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void init_reqs_ctrl_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_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_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: process response properly */
|
||||
static void init_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int r;
|
||||
|
||||
switch (ssm->cur_state) {
|
||||
case ACTIVATE_CONTROL_REQ_1:
|
||||
case ACTIVATE_CONTROL_REQ_2:
|
||||
{
|
||||
unsigned char *data;
|
||||
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
if (!transfer) {
|
||||
fpi_ssm_mark_aborted(ssm, -ENOMEM);
|
||||
break;
|
||||
}
|
||||
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER |
|
||||
LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
|
||||
data = g_malloc0(LIBUSB_CONTROL_SETUP_SIZE + 1);
|
||||
libusb_fill_control_setup(data,
|
||||
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x0c, 0x100, 0x0400, 1);
|
||||
libusb_fill_control_transfer(transfer, ssm->dev->udev, data,
|
||||
init_reqs_ctrl_cb, ssm, CTRL_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_aborted(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTIVATE_INIT_1:
|
||||
upektc_img_submit_req(ssm, upek2020_init_1, sizeof(upek2020_init_1),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_2:
|
||||
upektc_img_submit_req(ssm, upek2020_init_2, sizeof(upek2020_init_2),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_3:
|
||||
upektc_img_submit_req(ssm, upek2020_init_3, sizeof(upek2020_init_3),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_4:
|
||||
upektc_img_submit_req(ssm, upek2020_init_4, sizeof(upek2020_init_4),
|
||||
upekdev->seq, init_reqs_cb);
|
||||
/* Seq should be updated after 4th init */
|
||||
upekdev->seq++;
|
||||
break;
|
||||
case ACTIVATE_READ_CTRL_RESP_1:
|
||||
case ACTIVATE_READ_CTRL_RESP_2:
|
||||
case ACTIVATE_READ_INIT_1_RESP:
|
||||
case ACTIVATE_READ_INIT_2_RESP:
|
||||
case ACTIVATE_READ_INIT_3_RESP:
|
||||
case ACTIVATE_READ_INIT_4_RESP:
|
||||
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, init_read_data_cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(struct fpi_ssm *ssm)
|
||||
{
|
||||
struct fp_img_dev *dev = ssm->priv;
|
||||
int err = ssm->error;
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
fp_dbg("%s status %d", __func__, err);
|
||||
fpi_imgdev_activate_complete(dev, err);
|
||||
|
||||
if (!err)
|
||||
start_capture(dev);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
||||
{
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
|
||||
ACTIVATE_NUM_STATES);
|
||||
ssm->priv = dev;
|
||||
upekdev->seq = 0;
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upekts_img_dev *upekdev = dev->priv;
|
||||
|
||||
upekdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
|
||||
r = libusb_claim_interface(dev->udev, 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0");
|
||||
return r;
|
||||
}
|
||||
|
||||
dev->priv = g_malloc0(sizeof(struct upekts_img_dev));
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
g_free(dev->priv);
|
||||
libusb_release_interface(dev->udev, 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
|
||||
{
|
||||
if (dsc->idProduct == 0x2020 && dsc->bcdDevice == 1)
|
||||
return 1;
|
||||
#ifndef ENABLE_UPEKE2
|
||||
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
#ifndef ENABLE_UPEKE2
|
||||
{ .vendor = 0x147e, .product = 0x2016 },
|
||||
#endif
|
||||
{ .vendor = 0x147e, .product = 0x2020 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver upektc_img_driver = {
|
||||
.driver = {
|
||||
.id = UPEKTC_IMG_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "Upek TouchChip Fingerprint Coprocessor",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
.discover = discover,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = IMAGE_HEIGHT,
|
||||
.img_width = IMAGE_WIDTH,
|
||||
.bz3_threshold = 70,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
||||
144
libfprint/drivers/upektc_img.h
Normal file
144
libfprint/drivers/upektc_img.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Upek TouchChip Fingerprint Coprocessor definitions
|
||||
* Copyright (c) 2013 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 __UPEKTC_IMG_H
|
||||
#define __UPEKTC_IMG_H
|
||||
|
||||
static const unsigned char upek2020_init_1[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x04,
|
||||
0x00, 0x0d,
|
||||
0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0xda, 0xc1
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_init_2[] = {
|
||||
0x43, 0x69, 0x61, 0x6f,
|
||||
0x07,
|
||||
0x00, 0x01,
|
||||
0x01,
|
||||
0x3d, 0x72
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_init_3[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x04,
|
||||
0x00, 0x0d,
|
||||
0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x55, 0x2f
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_init_4[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x00,
|
||||
0x00, 0x07,
|
||||
0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04,
|
||||
0xc0, 0xd6
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_deinit[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x07,
|
||||
0x00, 0x01,
|
||||
0x01,
|
||||
0x3d,
|
||||
0x72
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_init_capture[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x00,
|
||||
0x00, 0x0e, /* Seq = 7, len = 0x00e */
|
||||
0x28, /* CMD = 0x28 */
|
||||
0x0b, 0x00, /* Inner len = 0x000b */
|
||||
0x00, 0x00,
|
||||
0x0e, /* SUBCMD = 0x0e */
|
||||
0x02,
|
||||
0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */
|
||||
0x02,
|
||||
0x00, /* Wait for acceptable finger */
|
||||
0x02,
|
||||
0x14, 0x9a /* CRC */
|
||||
};
|
||||
|
||||
#if 0
|
||||
static const unsigned char finger_status[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x00,
|
||||
0x70, 0x14, /* Seq = 7, len = 0x014 */
|
||||
0x28, /* CMD = 0x28 */
|
||||
0x11, 0x00, /* Inner len = 0x0011 */
|
||||
0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
|
||||
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
0x26, 0x03, /* CRC */
|
||||
0x00, 0x00, 0x00, /* Rest is garbage */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
static const unsigned char upek2020_ack_00_28[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x00,
|
||||
0x80, 0x08, /* Seq = 8, len = 0x008 */
|
||||
0x28, /* CMD = 0x28 */
|
||||
0x05, 0x00, /* Inner len = 0x0005 */
|
||||
0x00, 0x00, 0x00, 0x30, 0x01,
|
||||
0x6a, 0xc4 /* CRC */
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* No seq should be tracked here */
|
||||
static const unsigned char got_finger[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x08,
|
||||
0x00, 0x00, /* Seq = 0, len = 0x000 */
|
||||
0xa1, 0xa9, /* CRC */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
/* No seq should be put in there */
|
||||
static const unsigned char upek2020_ack_08[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x09,
|
||||
0x00, 0x00, /* Seq = 0, len = 0x0 */
|
||||
0x91, 0x9e /* CRC */
|
||||
};
|
||||
|
||||
static const unsigned char upek2020_ack_frame[] = {
|
||||
'C', 'i', 'a', 'o',
|
||||
0x00,
|
||||
0x50, 0x01, /* Seq = 5, len = 0x001 */
|
||||
0x30,
|
||||
0xac, 0x5b /* CRC */
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1077,6 +1077,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
|
||||
size_t data_len)
|
||||
{
|
||||
struct fp_print_data *fdata = NULL;
|
||||
struct fp_print_data_item *item = NULL;
|
||||
int result = -EPROTO;
|
||||
|
||||
if (data_len < sizeof(scan_comp)) {
|
||||
@@ -1085,9 +1086,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
|
||||
fp_err("unrecognised data prefix %x %x %x %x %x",
|
||||
data[0], data[1], data[2], data[3], data[4]);
|
||||
} else {
|
||||
fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
|
||||
memcpy(fdata->data, data + sizeof(scan_comp),
|
||||
fdata = fpi_print_data_new(dev);
|
||||
item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
|
||||
memcpy(item->data, data + sizeof(scan_comp),
|
||||
data_len - sizeof(scan_comp));
|
||||
fdata->prints = g_slist_prepend(fdata->prints, item);
|
||||
|
||||
result = FP_ENROLL_COMPLETE;
|
||||
}
|
||||
@@ -1249,12 +1252,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
|
||||
break;
|
||||
case VERIFY_INIT: ;
|
||||
struct fp_print_data *print = dev->verify_data;
|
||||
size_t data_len = sizeof(verify_hdr) + print->length;
|
||||
struct fp_print_data_item *item = print->prints->data;
|
||||
size_t data_len = sizeof(verify_hdr) + item->length;
|
||||
unsigned char *data = g_malloc(data_len);
|
||||
struct libusb_transfer *transfer;
|
||||
|
||||
memcpy(data, verify_hdr, sizeof(verify_hdr));
|
||||
memcpy(data + sizeof(verify_hdr), print->data, print->length);
|
||||
memcpy(data + sizeof(verify_hdr), item->data, item->length);
|
||||
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
|
||||
verify_init_2803_cb, ssm);
|
||||
g_free(data);
|
||||
|
||||
@@ -789,15 +789,18 @@ static void imaging_complete(struct fpi_ssm *ssm)
|
||||
int r = ssm->error;
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
/* Report error before exiting imaging loop - the error handler
|
||||
* can request state change, which needs to be postponed to end of
|
||||
* this function. */
|
||||
if (r)
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
|
||||
g_free(urudev->img_data);
|
||||
urudev->img_data = NULL;
|
||||
|
||||
libusb_free_transfer(urudev->img_transfer);
|
||||
urudev->img_transfer = NULL;
|
||||
|
||||
if (r)
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
|
||||
r = execute_state_change(dev);
|
||||
if (r)
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
@@ -994,16 +997,19 @@ static void init_scanpwr_irq_cb(struct fp_img_dev *dev, int status,
|
||||
uint16_t type, void *user_data)
|
||||
{
|
||||
struct fpi_ssm *ssm = user_data;
|
||||
struct uru4k_dev *urudev = dev->priv;
|
||||
|
||||
if (status)
|
||||
fpi_ssm_mark_aborted(ssm, status);
|
||||
else if (type != IRQDATA_SCANPWR_ON)
|
||||
fp_dbg("ignoring interrupt");
|
||||
else if (ssm->cur_state != INIT_AWAIT_SCAN_POWER)
|
||||
fp_err("ignoring scanpwr interrupt due to being in wrong state %d",
|
||||
ssm->cur_state);
|
||||
else
|
||||
else if (ssm->cur_state != INIT_AWAIT_SCAN_POWER) {
|
||||
fp_dbg("early scanpwr interrupt");
|
||||
urudev->scanpwr_irq_timeouts = -1;
|
||||
} else {
|
||||
fp_dbg("late scanpwr interrupt");
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_scanpwr_timeout(void *user_data)
|
||||
@@ -1053,14 +1059,21 @@ static void init_run_state(struct fpi_ssm *ssm)
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case INIT_POWERUP: ;
|
||||
if (!IRQ_HANDLER_IS_RUNNING(urudev)) {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
break;
|
||||
}
|
||||
urudev->irq_cb_data = ssm;
|
||||
urudev->irq_cb = init_scanpwr_irq_cb;
|
||||
|
||||
struct fpi_ssm *powerupsm = fpi_ssm_new(dev->dev, powerup_run_state,
|
||||
POWERUP_NUM_STATES);
|
||||
powerupsm->priv = dev;
|
||||
fpi_ssm_start_subsm(ssm, powerupsm);
|
||||
break;
|
||||
case INIT_AWAIT_SCAN_POWER:
|
||||
if (!IRQ_HANDLER_IS_RUNNING(urudev)) {
|
||||
fpi_ssm_mark_aborted(ssm, -EIO);
|
||||
if (urudev->scanpwr_irq_timeouts < 0) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1073,13 +1086,12 @@ static void init_run_state(struct fpi_ssm *ssm)
|
||||
fpi_ssm_mark_aborted(ssm, -ETIME);
|
||||
break;
|
||||
}
|
||||
|
||||
urudev->irq_cb_data = ssm;
|
||||
urudev->irq_cb = init_scanpwr_irq_cb;
|
||||
break;
|
||||
case INIT_DONE:
|
||||
fpi_timeout_cancel(urudev->scanpwr_irq_timeout);
|
||||
urudev->scanpwr_irq_timeout = NULL;
|
||||
if (urudev->scanpwr_irq_timeout) {
|
||||
fpi_timeout_cancel(urudev->scanpwr_irq_timeout);
|
||||
urudev->scanpwr_irq_timeout = NULL;
|
||||
}
|
||||
urudev->irq_cb_data = NULL;
|
||||
urudev->irq_cb = NULL;
|
||||
fpi_ssm_next_state(ssm);
|
||||
|
||||
@@ -64,9 +64,6 @@
|
||||
/* Best image contrast */
|
||||
#define VFS_IMG_BEST_CONRAST 128
|
||||
|
||||
/* Number of enroll stages */
|
||||
#define VFS_NR_ENROLL 3
|
||||
|
||||
/* Device parameters address */
|
||||
#define VFS_PAR_000E 0x000e
|
||||
#define VFS_PAR_0011 0x0011
|
||||
@@ -656,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 < VFS_NR_ENROLL))
|
||||
(vdev->enroll_stage < 1))
|
||||
/* Enroll not completed, return false */
|
||||
return FALSE;
|
||||
|
||||
@@ -1511,9 +1508,6 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Set enroll stage number */
|
||||
dev->dev->nr_enroll_stages = VFS_NR_ENROLL;
|
||||
|
||||
/* Initialize private structure */
|
||||
vdev = g_malloc0(sizeof(struct vfs101_dev));
|
||||
vdev->seqnum = -1;
|
||||
|
||||
@@ -244,9 +244,6 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Set enroll stage number */
|
||||
dev->dev->nr_enroll_stages = 1;
|
||||
|
||||
/* Initialize private structure */
|
||||
vdev = g_malloc0(sizeof(vfs301_dev_t));
|
||||
dev->priv = vdev;
|
||||
|
||||
1020
libfprint/drivers/vfs5011.c
Normal file
1020
libfprint/drivers/vfs5011.c
Normal file
File diff suppressed because it is too large
Load Diff
6186
libfprint/drivers/vfs5011_proto.h
Normal file
6186
libfprint/drivers/vfs5011_proto.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -91,6 +91,10 @@ enum fp_dev_state {
|
||||
DEV_STATE_IDENTIFYING,
|
||||
DEV_STATE_IDENTIFY_DONE,
|
||||
DEV_STATE_IDENTIFY_STOPPING,
|
||||
DEV_STATE_CAPTURE_STARTING,
|
||||
DEV_STATE_CAPTURING,
|
||||
DEV_STATE_CAPTURE_DONE,
|
||||
DEV_STATE_CAPTURE_STOPPING,
|
||||
};
|
||||
|
||||
struct fp_driver **fprint_get_drivers (void);
|
||||
@@ -108,8 +112,8 @@ struct fp_dev {
|
||||
|
||||
/* drivers should not mess with any of the below */
|
||||
enum fp_dev_state state;
|
||||
|
||||
int __enroll_stage;
|
||||
int unconditional_capture;
|
||||
|
||||
/* async I/O callbacks and data */
|
||||
/* FIXME: convert this to generic state operational data mechanism? */
|
||||
@@ -129,6 +133,10 @@ struct fp_dev {
|
||||
void *identify_cb_data;
|
||||
fp_identify_stop_cb identify_stop_cb;
|
||||
void *identify_stop_cb_data;
|
||||
fp_capture_cb capture_cb;
|
||||
void *capture_cb_data;
|
||||
fp_capture_stop_cb capture_stop_cb;
|
||||
void *capture_stop_cb_data;
|
||||
|
||||
/* FIXME: better place to put this? */
|
||||
struct fp_print_data **identify_gallery;
|
||||
@@ -146,6 +154,7 @@ enum fp_imgdev_action {
|
||||
IMG_ACTION_ENROLL,
|
||||
IMG_ACTION_VERIFY,
|
||||
IMG_ACTION_IDENTIFY,
|
||||
IMG_ACTION_CAPTURE,
|
||||
};
|
||||
|
||||
enum fp_imgdev_enroll_state {
|
||||
@@ -170,7 +179,9 @@ struct fp_img_dev {
|
||||
int action_state;
|
||||
|
||||
struct fp_print_data *acquire_data;
|
||||
struct fp_print_data *enroll_data;
|
||||
struct fp_img *acquire_img;
|
||||
int enroll_stage;
|
||||
int action_result;
|
||||
|
||||
/* FIXME: better place to put this? */
|
||||
@@ -179,8 +190,6 @@ struct fp_img_dev {
|
||||
void *priv;
|
||||
};
|
||||
|
||||
int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
|
||||
struct fp_img **image);
|
||||
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
|
||||
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
|
||||
|
||||
@@ -215,6 +224,8 @@ struct fp_driver {
|
||||
int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
|
||||
int (*identify_start)(struct fp_dev *dev);
|
||||
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
|
||||
int (*capture_start)(struct fp_dev *dev);
|
||||
int (*capture_stop)(struct fp_dev *dev);
|
||||
};
|
||||
|
||||
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
|
||||
@@ -267,6 +278,9 @@ extern struct fp_img_driver aes2550_driver;
|
||||
#ifdef ENABLE_AES2660
|
||||
extern struct fp_img_driver aes2660_driver;
|
||||
#endif
|
||||
#ifdef ENABLE_AES3500
|
||||
extern struct fp_img_driver aes3500_driver;
|
||||
#endif
|
||||
#ifdef ENABLE_AES4000
|
||||
extern struct fp_img_driver aes4000_driver;
|
||||
#endif
|
||||
@@ -282,6 +296,15 @@ extern struct fp_img_driver vfs101_driver;
|
||||
#ifdef ENABLE_VFS301
|
||||
extern struct fp_img_driver vfs301_driver;
|
||||
#endif
|
||||
#ifdef ENABLE_VFS5011
|
||||
extern struct fp_img_driver vfs5011_driver;
|
||||
#endif
|
||||
#ifdef ENABLE_UPEKTC_IMG
|
||||
extern struct fp_img_driver upektc_img_driver;
|
||||
#endif
|
||||
#ifdef ENABLE_ETES603
|
||||
extern struct fp_img_driver etes603_driver;
|
||||
#endif
|
||||
|
||||
extern libusb_context *fpi_usb_ctx;
|
||||
extern GSList *opened_devices;
|
||||
@@ -310,15 +333,19 @@ enum fp_print_data_type {
|
||||
PRINT_DATA_NBIS_MINUTIAE,
|
||||
};
|
||||
|
||||
struct fp_print_data {
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
enum fp_print_data_type type;
|
||||
struct fp_print_data_item {
|
||||
size_t length;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
struct fpi_print_data_fp1 {
|
||||
struct fp_print_data {
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
enum fp_print_data_type type;
|
||||
GSList *prints;
|
||||
};
|
||||
|
||||
struct fpi_print_data_fp2 {
|
||||
char prefix[3];
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
@@ -326,8 +353,14 @@ struct fpi_print_data_fp1 {
|
||||
unsigned char data[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct fpi_print_data_item_fp2 {
|
||||
uint32_t length;
|
||||
unsigned char data[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
void fpi_data_exit(void);
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length);
|
||||
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev);
|
||||
struct fp_print_data_item *fpi_print_data_item_new(size_t length);
|
||||
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
|
||||
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
|
||||
enum fp_print_data_type type2);
|
||||
@@ -436,6 +469,11 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img);
|
||||
void fpi_drvcb_identify_stopped(struct fp_dev *dev);
|
||||
|
||||
void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
|
||||
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
|
||||
struct fp_img *img);
|
||||
void fpi_drvcb_capture_stopped(struct fp_dev *dev);
|
||||
|
||||
/* for image drivers */
|
||||
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
|
||||
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);
|
||||
|
||||
@@ -30,6 +30,8 @@ static const struct usb_id whitelist_id_table[] = {
|
||||
|
||||
static const struct usb_id blacklist_id_table[] = {
|
||||
{ .vendor = 0x0483, .product = 0x2016 },
|
||||
/* https://bugs.freedesktop.org/show_bug.cgi?id=66659 */
|
||||
{ .vendor = 0x045e, .product = 0x00bb },
|
||||
{ 0, 0, 0 },
|
||||
};
|
||||
|
||||
@@ -52,7 +54,7 @@ static void print_driver (struct fp_driver *driver)
|
||||
blacklist = 0;
|
||||
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
|
||||
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
|
||||
driver->id_table[j].product == blacklist_id_table[j].product) {
|
||||
driver->id_table[i].product == blacklist_id_table[j].product) {
|
||||
blacklist = 1;
|
||||
break;
|
||||
}
|
||||
@@ -72,7 +74,7 @@ static void print_driver (struct fp_driver *driver)
|
||||
if (num_printed == 0)
|
||||
printf ("# %s\n", driver->full_name);
|
||||
|
||||
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
|
||||
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);
|
||||
num_printed++;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,17 @@ uint32_t fp_dev_get_devtype(struct fp_dev *dev);
|
||||
int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data);
|
||||
int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print);
|
||||
|
||||
/** \ingroup dev
|
||||
* Image capture result codes returned from fp_dev_img_capture().
|
||||
*/
|
||||
enum fp_capture_result {
|
||||
/** Capture completed successfully, the capture data has been
|
||||
* returned to the caller. */
|
||||
FP_CAPTURE_COMPLETE = 0,
|
||||
/** Capture failed for some reason */
|
||||
FP_CAPTURE_FAIL,
|
||||
};
|
||||
|
||||
int fp_dev_supports_imaging(struct fp_dev *dev);
|
||||
int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
|
||||
struct fp_img **image);
|
||||
@@ -340,6 +351,13 @@ typedef void (*fp_identify_stop_cb)(struct fp_dev *dev, void *user_data);
|
||||
int fp_async_identify_stop(struct fp_dev *dev, fp_identify_stop_cb callback,
|
||||
void *user_data);
|
||||
|
||||
typedef void (*fp_capture_cb)(struct fp_dev *dev, int result,
|
||||
struct fp_img *img, void *user_data);
|
||||
int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_capture_cb callback, void *user_data);
|
||||
|
||||
typedef void (*fp_capture_stop_cb)(struct fp_dev *dev, void *user_data);
|
||||
int fp_async_capture_stop(struct fp_dev *dev, fp_capture_stop_cb callback, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Imaging utility functions for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
|
||||
{
|
||||
int new_width = img->width * w_factor;
|
||||
int new_height = img->height * h_factor;
|
||||
GdkPixbuf *orig, *resized;
|
||||
struct fp_img *newimg;
|
||||
guchar *pixels;
|
||||
guint y;
|
||||
int rowstride;
|
||||
|
||||
g_type_init ();
|
||||
|
||||
/* It is possible to implement resizing using a simple algorithm, however
|
||||
* we use gdk-pixbuf because it applies some kind of smoothing to the
|
||||
* result, which improves matching performances in my experiments. */
|
||||
|
||||
/* Create the original pixbuf, and fill it in from the grayscale data */
|
||||
orig = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
|
||||
FALSE,
|
||||
8,
|
||||
img->width,
|
||||
img->height);
|
||||
rowstride = gdk_pixbuf_get_rowstride (orig);
|
||||
pixels = gdk_pixbuf_get_pixels (orig);
|
||||
for (y = 0; y < img->height; y++) {
|
||||
guint x;
|
||||
for (x = 0; x < img->width; x++) {
|
||||
guchar *p, *r;
|
||||
|
||||
p = pixels + y * rowstride + x * 3;
|
||||
r = img->data + y * img->width + x;
|
||||
p[0] = r[0];
|
||||
p[1] = r[0];
|
||||
p[2] = r[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize the pixbuf, and create the new fp_img */
|
||||
resized = gdk_pixbuf_scale_simple (orig, new_width, new_height, GDK_INTERP_HYPER);
|
||||
g_object_unref (orig);
|
||||
|
||||
newimg = fpi_img_new(new_width * new_height);
|
||||
newimg->width = new_width;
|
||||
newimg->height = new_height;
|
||||
newimg->flags = img->flags;
|
||||
|
||||
rowstride = gdk_pixbuf_get_rowstride (resized);
|
||||
pixels = gdk_pixbuf_get_pixels (resized);
|
||||
for (y = 0; y < newimg->height; y++) {
|
||||
guint x;
|
||||
for (x = 0; x < newimg->width; x++) {
|
||||
guchar *p, *r;
|
||||
|
||||
r = newimg->data + y * newimg->width + x;
|
||||
p = pixels + y * rowstride + x * 3;
|
||||
r[0] = p[0];
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (resized);
|
||||
|
||||
return newimg;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@
|
||||
|
||||
struct fp_img *fpi_img_new(size_t length)
|
||||
{
|
||||
struct fp_img *img = g_malloc(sizeof(*img) + length);
|
||||
memset(img, 0, sizeof(*img));
|
||||
struct fp_img *img = g_malloc0(sizeof(*img) + length);
|
||||
fp_dbg("length=%zd", length);
|
||||
img->length = length;
|
||||
return img;
|
||||
@@ -314,6 +313,7 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
|
||||
struct fp_print_data **ret)
|
||||
{
|
||||
struct fp_print_data *print;
|
||||
struct fp_print_data_item *item;
|
||||
int r;
|
||||
|
||||
if (!img->minutiae) {
|
||||
@@ -328,9 +328,11 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
|
||||
|
||||
/* FIXME: space is wasted if we dont hit the max minutiae count. would
|
||||
* be good to make this dynamic. */
|
||||
print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
|
||||
print = fpi_print_data_new(imgdev->dev);
|
||||
item = fpi_print_data_item_new(sizeof(struct xyt_struct));
|
||||
print->type = PRINT_DATA_NBIS_MINUTIAE;
|
||||
minutiae_to_xyt(img->minutiae, img->width, img->height, print->data);
|
||||
minutiae_to_xyt(img->minutiae, img->width, img->height, item->data);
|
||||
print->prints = g_slist_prepend(print->prints, item);
|
||||
|
||||
/* FIXME: the print buffer at this point is endian-specific, and will
|
||||
* only work when loaded onto machines with identical endianness. not good!
|
||||
@@ -343,42 +345,73 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
|
||||
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
|
||||
struct fp_print_data *new_print)
|
||||
{
|
||||
struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->data;
|
||||
struct xyt_struct *pstruct = (struct xyt_struct *) new_print->data;
|
||||
GTimer *timer;
|
||||
int r;
|
||||
int score, max_score = 0, probe_len;
|
||||
struct xyt_struct *pstruct = NULL;
|
||||
struct xyt_struct *gstruct = NULL;
|
||||
struct fp_print_data_item *data_item;
|
||||
GSList *list_item;
|
||||
|
||||
if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
|
||||
new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
|
||||
new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
|
||||
fp_err("invalid print format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timer = g_timer_new();
|
||||
r = bozorth_main(pstruct, gstruct);
|
||||
g_timer_stop(timer);
|
||||
fp_dbg("bozorth processing took %f seconds, score=%d",
|
||||
g_timer_elapsed(timer, NULL), r);
|
||||
g_timer_destroy(timer);
|
||||
if (g_slist_length(new_print->prints) != 1) {
|
||||
fp_err("new_print contains more than one sample, is it enrolled print?");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return r;
|
||||
data_item = new_print->prints->data;
|
||||
pstruct = (struct xyt_struct *)data_item->data;
|
||||
|
||||
probe_len = bozorth_probe_init(pstruct);
|
||||
list_item = enrolled_print->prints;
|
||||
do {
|
||||
data_item = list_item->data;
|
||||
gstruct = (struct xyt_struct *)data_item->data;
|
||||
score = bozorth_to_gallery(probe_len, pstruct, gstruct);
|
||||
fp_dbg("score %d", score);
|
||||
max_score = max(score, max_score);
|
||||
list_item = g_slist_next(list_item);
|
||||
} while (list_item);
|
||||
|
||||
return max_score;
|
||||
}
|
||||
|
||||
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
|
||||
struct fp_print_data **gallery, int match_threshold, size_t *match_offset)
|
||||
{
|
||||
struct xyt_struct *pstruct = (struct xyt_struct *) print->data;
|
||||
struct xyt_struct *pstruct;
|
||||
struct xyt_struct *gstruct;
|
||||
struct fp_print_data *gallery_print;
|
||||
int probe_len = bozorth_probe_init(pstruct);
|
||||
struct fp_print_data_item *data_item;
|
||||
int probe_len;
|
||||
size_t i = 0;
|
||||
int r;
|
||||
GSList *list_item;
|
||||
|
||||
if (g_slist_length(print->prints) != 1) {
|
||||
fp_err("new_print contains more than one sample, is it enrolled print?");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_item = print->prints->data;
|
||||
pstruct = (struct xyt_struct *)data_item->data;
|
||||
|
||||
probe_len = bozorth_probe_init(pstruct);
|
||||
while ((gallery_print = gallery[i++])) {
|
||||
struct xyt_struct *gstruct = (struct xyt_struct *) gallery_print->data;
|
||||
int r = bozorth_to_gallery(probe_len, pstruct, gstruct);
|
||||
if (r >= match_threshold) {
|
||||
*match_offset = i - 1;
|
||||
return FP_VERIFY_MATCH;
|
||||
}
|
||||
list_item = gallery_print->prints;
|
||||
do {
|
||||
data_item = list_item->data;
|
||||
gstruct = (struct xyt_struct *)data_item->data;
|
||||
r = bozorth_to_gallery(probe_len, pstruct, gstruct);
|
||||
if (r >= match_threshold) {
|
||||
*match_offset = i - 1;
|
||||
return FP_VERIFY_MATCH;
|
||||
}
|
||||
list_item = g_slist_next(list_item);
|
||||
} while (list_item);
|
||||
}
|
||||
return FP_VERIFY_NO_MATCH;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#define MIN_ACCEPTABLE_MINUTIAE 10
|
||||
#define BOZORTH3_DEFAULT_THRESHOLD 40
|
||||
#define IMG_ENROLL_STAGES 5
|
||||
|
||||
static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
@@ -33,8 +34,9 @@ static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
|
||||
int r = 0;
|
||||
|
||||
imgdev->dev = dev;
|
||||
imgdev->enroll_stage = 0;
|
||||
dev->priv = imgdev;
|
||||
dev->nr_enroll_stages = 1;
|
||||
dev->nr_enroll_stages = IMG_ENROLL_STAGES;
|
||||
|
||||
/* for consistency in driver code, allow udev access through imgdev */
|
||||
imgdev->udev = dev->udev;
|
||||
@@ -144,8 +146,17 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
fp_dbg("reporting enroll result");
|
||||
fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img);
|
||||
if (r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
|
||||
data = imgdev->enroll_data;
|
||||
if (r == FP_ENROLL_COMPLETE) {
|
||||
imgdev->enroll_data = NULL;
|
||||
}
|
||||
fpi_drvcb_enroll_stage_completed(imgdev->dev, r,
|
||||
r == FP_ENROLL_COMPLETE ? data : NULL,
|
||||
img);
|
||||
/* the callback can cancel enrollment, so recheck current
|
||||
* action and the status to see if retry is needed */
|
||||
if (imgdev->action == IMG_ACTION_ENROLL &&
|
||||
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);
|
||||
@@ -160,6 +171,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
|
||||
imgdev->identify_match_offset, img);
|
||||
fp_print_data_free(data);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_report_capture_result(imgdev->dev, r, img);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
@@ -228,24 +242,41 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
|
||||
|
||||
fp_img_standardize(img);
|
||||
imgdev->acquire_img = img;
|
||||
r = fpi_img_to_print_data(imgdev, img, &print);
|
||||
if (r < 0) {
|
||||
fp_dbg("image to print data conversion error: %d", r);
|
||||
imgdev->action_result = FP_ENROLL_RETRY;
|
||||
goto next_state;
|
||||
} else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
|
||||
fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
|
||||
MIN_ACCEPTABLE_MINUTIAE);
|
||||
fp_print_data_free(print);
|
||||
/* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
|
||||
imgdev->action_result = FP_ENROLL_RETRY;
|
||||
goto next_state;
|
||||
if (imgdev->action != IMG_ACTION_CAPTURE) {
|
||||
r = fpi_img_to_print_data(imgdev, img, &print);
|
||||
if (r < 0) {
|
||||
fp_dbg("image to print data conversion error: %d", r);
|
||||
imgdev->action_result = FP_ENROLL_RETRY;
|
||||
goto next_state;
|
||||
} else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
|
||||
fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
|
||||
MIN_ACCEPTABLE_MINUTIAE);
|
||||
fp_print_data_free(print);
|
||||
/* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
|
||||
imgdev->action_result = FP_ENROLL_RETRY;
|
||||
goto next_state;
|
||||
}
|
||||
}
|
||||
|
||||
imgdev->acquire_data = print;
|
||||
switch (imgdev->action) {
|
||||
case IMG_ACTION_ENROLL:
|
||||
imgdev->action_result = FP_ENROLL_COMPLETE;
|
||||
if (!imgdev->enroll_data) {
|
||||
imgdev->enroll_data = fpi_print_data_new(imgdev->dev);
|
||||
}
|
||||
BUG_ON(g_slist_length(print->prints) != 1);
|
||||
/* Move print data from acquire data into enroll_data */
|
||||
imgdev->enroll_data->prints =
|
||||
g_slist_prepend(imgdev->enroll_data->prints, print->prints->data);
|
||||
print->prints = g_slist_remove(print->prints, print->prints->data);
|
||||
|
||||
fp_print_data_free(imgdev->acquire_data);
|
||||
imgdev->acquire_data = NULL;
|
||||
imgdev->enroll_stage++;
|
||||
if (imgdev->enroll_stage == imgdev->dev->nr_enroll_stages)
|
||||
imgdev->action_result = FP_ENROLL_COMPLETE;
|
||||
else
|
||||
imgdev->action_result = FP_ENROLL_PASS;
|
||||
break;
|
||||
case IMG_ACTION_VERIFY:
|
||||
verify_process_img(imgdev);
|
||||
@@ -253,6 +284,9 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
identify_process_img(imgdev);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
imgdev->action_result = FP_CAPTURE_COMPLETE;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
@@ -277,6 +311,9 @@ void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error)
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_report_identify_result(imgdev->dev, error, 0, NULL);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_report_capture_result(imgdev->dev, error, NULL);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
@@ -297,6 +334,9 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_identify_started(imgdev->dev, status);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_capture_started(imgdev->dev, status);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
return;
|
||||
@@ -322,6 +362,9 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
|
||||
case IMG_ACTION_IDENTIFY:
|
||||
fpi_drvcb_identify_stopped(imgdev->dev);
|
||||
break;
|
||||
case IMG_ACTION_CAPTURE:
|
||||
fpi_drvcb_capture_stopped(imgdev->dev);
|
||||
break;
|
||||
default:
|
||||
fp_err("unhandled action %d", imgdev->action);
|
||||
break;
|
||||
@@ -382,6 +425,7 @@ static int generic_acquire_start(struct fp_dev *dev, int action)
|
||||
fp_dbg("action %d", action);
|
||||
imgdev->action = action;
|
||||
imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
|
||||
imgdev->enroll_stage = 0;
|
||||
|
||||
r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
|
||||
if (r < 0)
|
||||
@@ -397,8 +441,10 @@ static void generic_acquire_stop(struct fp_img_dev *imgdev)
|
||||
dev_deactivate(imgdev);
|
||||
|
||||
fp_print_data_free(imgdev->acquire_data);
|
||||
fp_print_data_free(imgdev->enroll_data);
|
||||
fp_img_free(imgdev->acquire_img);
|
||||
imgdev->acquire_data = NULL;
|
||||
imgdev->enroll_data = NULL;
|
||||
imgdev->acquire_img = NULL;
|
||||
imgdev->action_result = 0;
|
||||
}
|
||||
@@ -418,6 +464,14 @@ static int img_dev_identify_start(struct fp_dev *dev)
|
||||
return generic_acquire_start(dev, IMG_ACTION_IDENTIFY);
|
||||
}
|
||||
|
||||
static int img_dev_capture_start(struct fp_dev *dev)
|
||||
{
|
||||
/* Unconditional capture is not supported yet */
|
||||
if (dev->unconditional_capture)
|
||||
return -ENOTSUP;
|
||||
return generic_acquire_start(dev, IMG_ACTION_CAPTURE);
|
||||
}
|
||||
|
||||
static int img_dev_enroll_stop(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->priv;
|
||||
@@ -443,6 +497,14 @@ static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int img_dev_capture_stop(struct fp_dev *dev)
|
||||
{
|
||||
struct fp_img_dev *imgdev = dev->priv;
|
||||
BUG_ON(imgdev->action != IMG_ACTION_CAPTURE);
|
||||
generic_acquire_stop(imgdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fpi_img_driver_setup(struct fp_img_driver *idriver)
|
||||
{
|
||||
idriver->driver.type = DRIVER_IMAGING;
|
||||
@@ -454,5 +516,7 @@ void fpi_img_driver_setup(struct fp_img_driver *idriver)
|
||||
idriver->driver.verify_stop = img_dev_verify_stop;
|
||||
idriver->driver.identify_start = img_dev_identify_start;
|
||||
idriver->driver.identify_stop = img_dev_identify_stop;
|
||||
idriver->driver.capture_start = img_dev_capture_start;
|
||||
idriver->driver.capture_stop = img_dev_capture_stop;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Imaging utility functions for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2013 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
|
||||
@@ -17,50 +18,45 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <magick/ImageMagick.h>
|
||||
#include <pixman.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
|
||||
{
|
||||
Image *mimg;
|
||||
Image *resized;
|
||||
ExceptionInfo exception;
|
||||
MagickBooleanType ret;
|
||||
int new_width = img->width * w_factor;
|
||||
int new_height = img->height * h_factor;
|
||||
pixman_image_t *orig, *resized;
|
||||
pixman_transform_t transform;
|
||||
struct fp_img *newimg;
|
||||
|
||||
/* It is possible to implement resizing using a simple algorithm, however
|
||||
* we use ImageMagick because it applies some kind of smoothing to the
|
||||
* result, which improves matching performances in my experiments. */
|
||||
orig = pixman_image_create_bits(PIXMAN_a8, img->width, img->height, (uint32_t *)img->data, img->width);
|
||||
resized = pixman_image_create_bits(PIXMAN_a8, new_width, new_height, NULL, new_width);
|
||||
|
||||
if (!IsMagickInstantiated())
|
||||
InitializeMagick(NULL);
|
||||
|
||||
GetExceptionInfo(&exception);
|
||||
mimg = ConstituteImage(img->width, img->height, "I", CharPixel, img->data,
|
||||
&exception);
|
||||
|
||||
GetExceptionInfo(&exception);
|
||||
resized = ResizeImage(mimg, new_width, new_height, 0, 1.0, &exception);
|
||||
pixman_transform_init_identity(&transform);
|
||||
pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(w_factor), pixman_int_to_fixed(h_factor));
|
||||
pixman_image_set_transform(orig, &transform);
|
||||
pixman_image_set_filter(orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
orig, /* src */
|
||||
NULL, /* mask */
|
||||
resized, /* dst */
|
||||
0, 0, /* src x y */
|
||||
0, 0, /* mask x y */
|
||||
0, 0, /* dst x y */
|
||||
new_width, new_height /* width height */
|
||||
);
|
||||
|
||||
newimg = fpi_img_new(new_width * new_height);
|
||||
newimg->width = new_width;
|
||||
newimg->height = new_height;
|
||||
newimg->flags = img->flags;
|
||||
|
||||
GetExceptionInfo(&exception);
|
||||
ret = ExportImagePixels(resized, 0, 0, new_width, new_height, "I",
|
||||
CharPixel, newimg->data, &exception);
|
||||
if (ret != MagickTrue) {
|
||||
fp_err("export failed");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
|
||||
|
||||
DestroyImage(mimg);
|
||||
DestroyImage(resized);
|
||||
pixman_image_unref(orig);
|
||||
pixman_image_unref(resized);
|
||||
|
||||
return newimg;
|
||||
}
|
||||
@@ -512,3 +512,100 @@ err:
|
||||
return r;
|
||||
}
|
||||
|
||||
struct sync_capture_data {
|
||||
gboolean populated;
|
||||
int result;
|
||||
struct fp_img *img;
|
||||
};
|
||||
|
||||
static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img,
|
||||
void *user_data)
|
||||
{
|
||||
struct sync_capture_data *vdata = user_data;
|
||||
vdata->result = result;
|
||||
vdata->img = img;
|
||||
vdata->populated = TRUE;
|
||||
}
|
||||
|
||||
static void capture_stop_cb(struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
gboolean *stopped = user_data;
|
||||
fp_dbg("");
|
||||
*stopped = TRUE;
|
||||
}
|
||||
/** \ingroup dev
|
||||
* Captures an \ref img "image" from a device. The returned image is the raw
|
||||
* image provided by the device, you may wish to \ref img_std "standardize" it.
|
||||
*
|
||||
* If set, the <tt>unconditional</tt> flag indicates that the device should
|
||||
* capture an image unconditionally, regardless of whether a finger is there
|
||||
* or not. If unset, this function will block until a finger is detected on
|
||||
* the sensor.
|
||||
*
|
||||
* \param dev the device
|
||||
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
|
||||
* \param img a location to return the captured image. Must be freed with
|
||||
* fp_img_free() after use.
|
||||
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
|
||||
* unconditional flag was set but the device does not support this, or that the
|
||||
* device does not support imaging.
|
||||
* \sa fp_dev_supports_imaging()
|
||||
*/
|
||||
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
|
||||
struct fp_img **img)
|
||||
{
|
||||
struct sync_capture_data *vdata;
|
||||
gboolean stopped = FALSE;
|
||||
int r;
|
||||
|
||||
if (!dev->drv->capture_start) {
|
||||
fp_dbg("image capture is not supported on %s device", dev->drv->name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
fp_dbg("to be handled by %s", dev->drv->name);
|
||||
vdata = g_malloc0(sizeof(struct sync_capture_data));
|
||||
r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata);
|
||||
if (r < 0) {
|
||||
fp_dbg("capture_start error %d", r);
|
||||
g_free(vdata);
|
||||
return r;
|
||||
}
|
||||
|
||||
while (!vdata->populated) {
|
||||
r = fp_handle_events();
|
||||
if (r < 0) {
|
||||
g_free(vdata);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (img)
|
||||
*img = vdata->img;
|
||||
else
|
||||
fp_img_free(vdata->img);
|
||||
|
||||
r = vdata->result;
|
||||
g_free(vdata);
|
||||
switch (r) {
|
||||
case FP_CAPTURE_COMPLETE:
|
||||
fp_dbg("result: complete");
|
||||
break;
|
||||
case FP_CAPTURE_FAIL:
|
||||
fp_dbg("result: fail");
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognised return code %d", r);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
err:
|
||||
fp_dbg("ending capture");
|
||||
if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0)
|
||||
while (!stopped)
|
||||
if (fp_handle_events() < 0)
|
||||
break;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user