Merge tag 'v_0_5_0' into debian

0.5.0
This commit is contained in:
Didier Raboud
2013-01-15 17:58:15 +01:00
55 changed files with 12686 additions and 1167 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ Copyright (C) 2006 Pavel Machek <pavel@suse.cz>
Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
Copyright (C) 2007 Cyrille Bagard
Copyright (C) 2007 Vasily Khoruzhick
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>
+3 -3
View File
@@ -85,11 +85,11 @@ be reflected by updating the appropriate doxygen comments.
Contributing
============
Patches should be sent to the fprint mailing list detailed on the website.
A subscription is required.
Patches should be sent to the fprint bugzilla:
https://bugs.freedesktop.org/enter_bug.cgi?product=libfprint
Information about libfprint development repositories can be found here:
http://www.reactivated.net/fprint/Libfprint_development
http://www.freedesktop.org/wiki/Software/fprint/libfprint
If you're looking for ideas for things to work on, look at the TODO file or
grep the source code for FIXMEs.
+4 -1
View File
@@ -1,4 +1,3 @@
AUTOMAKE_OPTIONS = dist-bzip2
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
DISTCLEANFILES = ChangeLog libfprint.pc
@@ -9,6 +8,10 @@ if BUILD_EXAMPLES
SUBDIRS += examples
endif
DIST_SUBDIRS = libfprint doc examples
DISTCHECK_CONFIGURE_FLAGS = --with-drivers=all --enable-examples-build --enable-x11-examples-build --with-udev-rules-dir='$${libdir}/udev/rules.d-distcheck'
pkgconfigdir=$(libdir)/pkgconfig
pkgconfig_DATA=libfprint.pc
+25
View File
@@ -1,6 +1,31 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2012-12-03: v0.5.0 release
* Drivers:
- New VFS300/VFS301 driver
- New AES2550/AES2810 drivers
- New AES1660 driver
- New AES2660 driver
- New DigitalPersona URU4500 driver
- Avoid empty capture and improve image contrast in the
AES2501 and AES2550 drivers
- Update upektc driver, add support for Eikon Touch 300
- Fix UrU4000 image capture on ARM
* Library:
- Fix global variable collisions with libusb and other system headers
- Fix possible crash in NBIS image processing with some fingerprints
* Udev rules:
- Fix power control path for newer kernels
- Move udev rules to the correct directory
- Don't print duplicated udev rules
- Include udev rules in the tarball
- Allow disabling of udev rules for cross-compiling
- Add driver names in the generated rules
2011-04-18: v0.4.0 release
* Add support for Validity VFS101 (USB ID 138a:0001)
* Fix crasher when resizing a fingerprint image
+1
View File
@@ -8,3 +8,4 @@ Toby Howard (University of Manchester)
Seemant Kulleen
Pavel Herrman
Bastien Nocera
Greg Kerr and Martin Konecny from AuthenTec Inc - hardware donations (AES2550 device), datasheets for AES2550 and AES2810
+3 -1
View File
@@ -4,5 +4,7 @@ aclocal || exit 1
autoheader || exit 1
autoconf || exit 1
automake -a -c || exit 1
./configure --enable-maintainer-mode --enable-examples-build \
if test -z "$NOCONFIGURE"; then
exec ./configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*
fi
+91 -11
View File
@@ -1,5 +1,5 @@
AC_INIT([libfprint], [0.4.0])
AM_INIT_AUTOMAKE([1.11 dist-bzip2 no-dist-gzip check-news])
AC_INIT([libfprint], [0.5.0])
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
AM_CONFIG_HEADER([config.h])
@@ -23,10 +23,11 @@ AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000 vfs101"
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes4000 vfs101 vfs301"
require_imaging='no'
require_aeslib='no'
require_aesX660='no'
enable_upeke2='no'
enable_upekts='no'
enable_upektc='no'
@@ -35,15 +36,23 @@ enable_vcom5s='no'
enable_uru4000='no'
enable_fdu2000='no'
enable_aes1610='no'
enable_aes1660='no'
enable_aes2501='no'
enable_aes2550='no'
enable_aes2660='no'
enable_aes4000='no'
enable_vfs101='no'
enable_vfs301='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
[drivers="$withval"],
[drivers="$all_drivers"])
if test "x$drivers" = "xall" ; then
drivers=$all_drivers
fi
for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
case ${driver} in
upekts)
@@ -56,8 +65,7 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
;;
upektc)
AC_DEFINE([ENABLE_UPEKTC], [], [Build UPEK TouchChip driver])
enable_upektc="no"
# Driver not ported
enable_upektc="yes"
;;
upeksonly)
AC_DEFINE([ENABLE_UPEKSONLY], [], [Build UPEK TouchStrip sensor-only driver])
@@ -81,11 +89,28 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
require_aeslib="yes"
enable_aes2501="yes"
;;
aes2550)
AC_DEFINE([ENABLE_AES2550], [], [Build AuthenTec AES2550/AES2810 driver])
require_aeslib="yes"
enable_aes2550="yes"
;;
aes1610)
AC_DEFINE([ENABLE_AES1610], [], [Build AuthenTec AES1610 driver])
require_aeslib="yes"
enable_aes1610="yes"
;;
aes1660)
AC_DEFINE([ENABLE_AES1660], [], [Build AuthenTec AES1660 driver])
require_aeslib="yes"
require_aesX660="yes"
enable_aes1660="yes"
;;
aes2660)
AC_DEFINE([ENABLE_AES2660], [], [Build AuthenTec AES1660 driver])
require_aeslib="yes"
require_aesX660="yes"
enable_aes2660="yes"
;;
aes4000)
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
require_aeslib="yes"
@@ -96,21 +121,30 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
AC_DEFINE([ENABLE_VFS101], [], [Build Validity VFS101 driver])
enable_vfs101="yes"
;;
vfs301)
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
enable_vfs301="yes"
;;
esac
done
AM_CONDITIONAL([ENABLE_UPEKTS], [test "$enable_upekts" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKE2], [test "$enable_upeke2" = "yes"])
#AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" = "yes"])
AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" = "yes"])
AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" = "yes"])
AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" = "yes"])
AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" = "yes"])
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_AES4000], [test "$enable_aes4000" = "yes"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
@@ -129,6 +163,27 @@ AC_SUBST(GLIB_LIBS)
imagemagick_found=no
gdkpixbuf_found=no
AC_ARG_ENABLE(udev-rules,
AC_HELP_STRING([--enable-udev-rules],[Update the udev rules]),
[case "${enableval}" in
yes) ENABLE_UDEV_RULES=yes ;;
no) ENABLE_UDEV_RULES=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-udev-rules) ;;
esac],
[ENABLE_UDEV_RULES=yes]) dnl Default value
AM_CONDITIONAL(ENABLE_UDEV_RULES, test x$ENABLE_UDEV_RULES = "xyes")
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],
[ac_with_udev_rules_dir=""])
if test "${ac_with_udev_rules_dir}" = ""; then
ac_with_udev_rules_dir=`$PKG_CONFIG --variable=udevdir udev`/rules.d
fi
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
@@ -171,17 +226,17 @@ if test "x$build_x11_examples" != "xno"; then
AC_MSG_CHECKING(for Xv extensions)
AC_TRY_COMPILE([
#include <X11/Xlib.h>
#include <X11/extensions/Xvlib.h>],[
#include <X11/extensions/Xvlib.h>],[
int main(void) { (void) XvGetPortAttribute(0, 0, 0, 0); return 0; }
],xv=yes,xv=no);
AC_MSG_RESULT($xv)
if test x$xv = xyes; then
XV_LIBS="-lXv -lXext"
XV_CFLAGS=""
if test x$xv = xyes; then
XV_LIBS="-lXv -lXext"
XV_CFLAGS=""
AC_DEFINE(HAVE_XV,1,[defined if XV video overlay is available])
else
AC_MSG_ERROR([XV is required for X11 examples])
fi
fi
])
AC_CHECK_XV
fi
@@ -262,11 +317,26 @@ if test x$enable_aes1610 != xno ; then
else
AC_MSG_NOTICE([ aes1610 driver disabled])
fi
if test x$enable_aes1660 != xno ; then
AC_MSG_NOTICE([** aes1660 driver enabled])
else
AC_MSG_NOTICE([ aes1660 driver disabled])
fi
if test x$enable_aes2501 != xno ; then
AC_MSG_NOTICE([** aes2501 driver enabled])
else
AC_MSG_NOTICE([ aes2501 driver disabled])
fi
if test x$enable_aes2550 != xno ; then
AC_MSG_NOTICE([** aes2550/aes2810 driver enabled])
else
AC_MSG_NOTICE([ aes2550/aes2810 driver disabled])
fi
if test x$enable_aes2660 != xno ; then
AC_MSG_NOTICE([** aes2660 driver enabled])
else
AC_MSG_NOTICE([ aes2660 driver disabled])
fi
if test x$enable_aes4000 != xno ; then
AC_MSG_NOTICE([** aes4000 driver enabled])
else
@@ -277,11 +347,21 @@ if test x$enable_vfs101 != xno ; then
else
AC_MSG_NOTICE([ vfs101 driver disabled])
fi
if test x$enable_vfs301 != xno ; then
AC_MSG_NOTICE([** vfs301 driver enabled])
else
AC_MSG_NOTICE([ vfs301 driver disabled])
fi
if test x$require_aeslib != xno ; then
AC_MSG_NOTICE([** aeslib helper functions enabled])
else
AC_MSG_NOTICE([ aeslib helper functions disabled])
fi
if test x$require_aesX660 != xno ; then
AC_MSG_NOTICE([** aesX660 common routines enabled])
else
AC_MSG_NOTICE([ aesX660 common routines disabled])
fi
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT
+3 -3
View File
@@ -29,7 +29,7 @@
#define FORMAT 0x32595559
static int adaptor = -1;
static unsigned char *framebuffer = NULL;
static char *framebuffer = NULL;
static Display *display = NULL;
static Window window=(Window)NULL;
@@ -50,7 +50,7 @@ static int connection = -1;
u = u > 255 ? 255 : u;\
v = v > 255 ? 255 : v
static void grey2yuy2 (unsigned char *grey, unsigned char *YUV, int num) {
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
int i, j;
int y0, y1, u0, u1, v0, v1;
int gval;
@@ -86,7 +86,7 @@ static void display_frame(struct fp_img *img)
static void QueryXv()
{
int num_adaptors;
unsigned int num_adaptors;
int num_formats;
XvImageFormatValues *formats = NULL;
int i,j;
+39 -6
View File
@@ -4,15 +4,19 @@ MOSTLYCLEANFILES = $(udev_rules_DATA)
UPEKE2_SRC = drivers/upeke2.c
UPEKTS_SRC = drivers/upekts.c
UPEKTC_SRC = drivers/upektc.c
UPEKTC_SRC = drivers/upektc.c drivers/upektc.h
UPEKSONLY_SRC = drivers/upeksonly.c
URU4000_SRC = drivers/uru4000.c
AES1610_SRC = drivers/aes1610.c
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
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
EXTRA_DIST = \
$(UPEKE2_SRC) \
@@ -21,14 +25,22 @@ EXTRA_DIST = \
$(UPEKSONLY_SRC) \
$(URU4000_SRC) \
$(AES1610_SRC) \
$(AES1660_SRC) \
$(AES2501_SRC) \
$(AES2550_SRC) \
$(AES2660_SRC) \
$(AES4000_SRC) \
$(FDU2000_SRC) \
$(VCOM5S_SRC) \
$(VFS101_SRC) \
$(VFS301_SRC) \
drivers/aesx660.c \
drivers/aesx660.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
imagemagick.c \
gdkpixbuf.c
gdkpixbuf.c \
60-fprint-autosuspend.rules
DRIVER_SRC =
OTHER_SRC =
@@ -79,10 +91,11 @@ fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(L
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la
udev_rules_DATA = 60-fprint-autosuspend.rules
udev_rulesdir = $(libdir)/udev/rules.d/
if ENABLE_UDEV_RULES
$(udev_rules_DATA): fprint-list-udev-rules
$(builddir)/fprint-list-udev-rules > $@
endif
if ENABLE_UPEKE2
DRIVER_SRC += $(UPEKE2_SRC)
@@ -96,9 +109,9 @@ if ENABLE_UPEKSONLY
DRIVER_SRC += $(UPEKSONLY_SRC)
endif
#if ENABLE_UPEKTC
#DRIVER_SRC += $(UPEKTC_SRC)
#endif
if ENABLE_UPEKTC
DRIVER_SRC += $(UPEKTC_SRC)
endif
if ENABLE_URU4000
DRIVER_SRC += $(URU4000_SRC)
@@ -116,10 +129,22 @@ if ENABLE_AES1610
DRIVER_SRC += $(AES1610_SRC)
endif
if ENABLE_AES1660
DRIVER_SRC += $(AES1660_SRC)
endif
if ENABLE_AES2501
DRIVER_SRC += $(AES2501_SRC)
endif
if ENABLE_AES2550
DRIVER_SRC += $(AES2550_SRC)
endif
if ENABLE_AES2660
DRIVER_SRC += $(AES2660_SRC)
endif
if ENABLE_AES4000
DRIVER_SRC += $(AES4000_SRC)
endif
@@ -128,6 +153,10 @@ if ENABLE_VFS101
DRIVER_SRC += $(VFS101_SRC)
endif
if ENABLE_VFS301
DRIVER_SRC += $(VFS301_SRC)
endif
if REQUIRE_IMAGEMAGICK
OTHER_SRC += imagemagick.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
@@ -144,6 +173,10 @@ if REQUIRE_AESLIB
OTHER_SRC += aeslib.c aeslib.h
endif
if REQUIRE_AESX660
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
endif
libfprint_la_SOURCES = \
fp_internal.h \
async.c \
+113 -2
View File
@@ -20,6 +20,7 @@
#define FP_COMPONENT "aeslib"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
@@ -164,10 +165,120 @@ void aes_assemble_image(unsigned char *input, size_t width, size_t height,
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x07) * 36;
output[width * (row + 1) + column] = ((*input & 0x70) >> 4) * 36;
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
/* 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 frame_width, unsigned int frame_height,
unsigned char *output, gboolean reverse, unsigned int *errors_sum)
{
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;
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);
if (reverse)
output -= frame_size;
else
output += frame_size;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_stripes; frame++) {
int not_overlapped;
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);
}
return image_height;
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height)
{
size_t final_size;
struct fp_img *img;
unsigned int frame_size = frame_width * frame_height;
unsigned int errors_sum, r_errors_sum;
BUG_ON(stripes_len == 0);
/* create buffer big enough for max image */
img = fpi_img_new(stripes_len * frame_size);
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);
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");
}
/* 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;
return img;
}
+3
View File
@@ -36,5 +36,8 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output);
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height);
#endif
+20 -8
View File
@@ -291,25 +291,25 @@ void fpi_log(enum fpi_log_level level, const char *component,
#ifndef ENABLE_DEBUG_LOGGING
if (!log_level)
return;
if (level == LOG_LEVEL_WARNING && log_level < 2)
if (level == FPRINT_LOG_LEVEL_WARNING && log_level < 2)
return;
if (level == LOG_LEVEL_INFO && log_level < 3)
if (level == FPRINT_LOG_LEVEL_INFO && log_level < 3)
return;
#endif
switch (level) {
case LOG_LEVEL_INFO:
case FPRINT_LOG_LEVEL_INFO:
prefix = "info";
break;
case LOG_LEVEL_WARNING:
case FPRINT_LOG_LEVEL_WARNING:
stream = stderr;
prefix = "warning";
break;
case LOG_LEVEL_ERROR:
case FPRINT_LOG_LEVEL_ERROR:
stream = stderr;
prefix = "error";
break;
case LOG_LEVEL_DEBUG:
case FPRINT_LOG_LEVEL_DEBUG:
stream = stderr;
prefix = "debug";
break;
@@ -355,6 +355,9 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_AES2501
&aes2501_driver,
#endif
#ifdef ENABLE_AES2550
&aes2550_driver,
#endif
#ifdef ENABLE_URU4000
&uru4000_driver,
#endif
@@ -368,13 +371,22 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_AES1610
&aes1610_driver,
#endif
#ifdef ENABLE_AES1660
&aes1660_driver,
#endif
#ifdef ENABLE_AES2660
&aes2660_driver,
#endif
#ifdef ENABLE_VFS101
&vfs101_driver,
#endif
/*#ifdef ENABLE_UPEKTC
#ifdef ENABLE_VFS301
&vfs301_driver,
#endif
#ifdef ENABLE_UPEKTC
&upektc_driver,
#endif
#ifdef ENABLE_FDU2000
/*#ifdef ENABLE_FDU2000
&fdu2000_driver,
#endif
*/
+15 -300
View File
@@ -4,6 +4,7 @@
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from libfprint aes2501 driver.
*
@@ -32,6 +33,8 @@
#include <aeslib.h>
#include <fp_internal.h>
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static int adjust_gain(unsigned char *buffer, int status);
@@ -110,103 +113,6 @@ static void generic_ignore_data_cb(struct libusb_transfer *transfer)
libusb_free_transfer(transfer);
}
static void read_regs_data_cb(struct libusb_transfer *transfer)
{
struct aes1610_read_regs *rdata = transfer->user_data;
unsigned char *retdata = NULL;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
r = -EIO;
} else if (transfer->length != transfer->actual_length) {
r = -EPROTO;
} else {
r = 0;
retdata = transfer->buffer;
}
rdata->callback(rdata->dev, r, retdata, rdata->user_data);
g_free(rdata);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct aes1610_read_regs *rdata = user_data;
struct libusb_transfer *transfer;
unsigned char *data;
int r;
g_free(rdata->regwrite);
if (result != 0)
goto err;
transfer = libusb_alloc_transfer(0);
if (!transfer) {
result = -ENOMEM;
goto err;
}
data = g_malloc(126);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126,
read_regs_data_cb, rdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
result = -EIO;
goto err;
}
return;
err:
rdata->callback(dev, result, NULL, rdata->user_data);
g_free(rdata);
}
// XXX: this comes from aes2501 driver but it is unused here
static void read_regs(struct fp_img_dev *dev, aes1610_read_regs_cb callback,
void *user_data)
{
/* FIXME: regwrite is dynamic because of asynchronity. is this really
* required? */
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
struct aes1610_read_regs *rdata = g_malloc(sizeof(*rdata));
fp_dbg("");
//regwrite->reg = AES1610_REG_CTRL2;
//regwrite->value = AES1610_CTRL2_READ_REGS;
rdata->dev = dev;
rdata->callback = callback;
rdata->user_data = user_data;
rdata->regwrite = regwrite;
//aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1,
// read_regs_rq_cb, rdata);
}
/* Read the value of a specific register from a register dump */
static int regval_from_dump(unsigned char *data, uint8_t target)
{
if (*data != FIRST_AES1610_REG) {
fp_err("not a register dump");
return -EILSEQ;
}
if (!(FIRST_AES1610_REG <= target && target <= LAST_AES1610_REG)) {
fp_err("out of range");
return -EINVAL;
}
target -= FIRST_AES1610_REG;
target *= 2;
return data[target + 1];
}
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
@@ -217,8 +123,6 @@ static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
fpi_ssm_mark_aborted(ssm, result);
}
/* read the specified number of bytes from the IN endpoint but throw them
* away, then increment the SSM */
static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
@@ -244,150 +148,6 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
}
}
/****** IMAGE PROCESSING ******/
static int sum_histogram_values(unsigned char *data, uint8_t threshold)
{
int r = 0;
int i;
uint16_t *histogram = (uint16_t *)(data + 1);
if (*data != 0xde)
return -EILSEQ;
if (threshold > 0x0f)
return -EINVAL;
/* FIXME endianness */
for (i = threshold; i < 16; i++)
r += histogram[i];
return r;
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
*min_error = 255 * FRAME_SIZE;
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(struct aes1610_dev *aesdev, unsigned char *output,
gboolean reverse, unsigned int *errors_sum)
{
uint8_t *assembled = output;
int frame;
uint32_t image_height = FRAME_HEIGHT;
unsigned int min_error;
size_t num_strips = aesdev->strips_len;
GSList *list_entry = aesdev->strips;
*errors_sum = 0;
if (num_strips < 1)
return 0;
/* Rotating given data by 90 degrees
* Taken from document describing aes1610 image format
* TODO: move reversing detection here */
if (reverse)
output += (num_strips - 1) * FRAME_SIZE;
for (frame = 0; frame < num_strips; frame++) {
aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
size_t final_size;
struct fp_img *img;
unsigned int errors_sum, r_errors_sum;
fp_dbg("");
BUG_ON(aesdev->strips_len == 0);
/* reverse list */
aesdev->strips = g_slist_reverse(aesdev->strips);
/* create buffer big enough for max image */
img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(aesdev, 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");
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * FRAME_WIDTH;
img = fpi_img_resize(img, final_size);
/* FIXME: ugly workaround */
if (img->height < 12)
img->height = 12;
fpi_imgdev_image_captured(dev, img);
/* free strips and strip list */
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
}
/****** FINGER PRESENCE DETECTION ******/
@@ -416,15 +176,6 @@ static const struct aes_regwrite finger_det_reqs[] = {
{ 0x81, 0x04 }
};
static const struct aes_regwrite finger_det_none[] = {
{ 0x80, 0x01 },
{ 0x82, 0x00 },
{ 0x86, 0x00 },
{ 0xB1, 0x28 },
{ 0x1D, 0x00 }
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
@@ -461,12 +212,6 @@ out:
libusb_free_transfer(transfer);
}
static void finger_det_none_cb(struct fp_img_dev *dev, int result, void *user_data){
fpi_imgdev_report_finger_status(dev, FALSE);
start_finger_detection(dev);
}
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
struct libusb_transfer *transfer;
@@ -500,7 +245,6 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_da
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct libusb_transfer *transfer;
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -780,7 +524,7 @@ static int adjust_gain(unsigned char *buffer, int status)
/*
* Restore the default gain values */
static void restore_gain()
static void restore_gain(void)
{
strip_scan_reqs[0].value = list_BE_values[0];
strip_scan_reqs[1].value = 0x04;
@@ -815,7 +559,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
struct aes1610_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
int sum, i;
int threshold;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
@@ -826,17 +569,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
//stripdata = g_malloc(128 * 4);
//memcpy(stripdata, data + 1, 128 * 4);
//aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
//aesdev->strips_len++;
/*threshold = regval_from_dump(data + 1 + 128*8 + 1 + 16*2 + 1 + 8,
0x97);
if (threshold < 0) {
fpi_ssm_mark_aborted(ssm, threshold);
goto out;
}*/
sum = 0;
for (i = 516; i < 530; i++)
@@ -876,11 +608,19 @@ 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;
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);
/* assemble image and submit it to library */
assemble_and_submit_image(dev);
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
@@ -987,26 +727,9 @@ static const struct aes_regwrite stop_reader[] = {
enum activate_states {
WRITE_INIT,
// READ_DATA,
// READ_REGS,
ACTIVATE_NUM_STATES,
};
/* this come from aes2501 and is unused here
void activate_read_regs_cb(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct aes1610_dev *aesdev = dev->priv;
if (status != 0) {
fpi_ssm_mark_aborted(ssm, status);
} else {
fpi_ssm_next_state(ssm);
}
}
*/
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
@@ -1018,14 +741,6 @@ static void activate_run_state(struct fpi_ssm *ssm)
fp_dbg("write init");
aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm);
break;
/* case READ_DATA:
fp_dbg("read data");
generic_read_ignore_data(ssm, 20);
break;
case READ_REGS:
fp_dbg("read regs");
read_regs(dev, activate_read_regs_cb, ssm);
break;*/
}
}
@@ -1106,7 +821,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes1610_driver = {
.driver = {
.id = 6,
.id = AES1610_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1610",
.id_table = id_table,
+126
View File
@@ -0,0 +1,126 @@
/*
* AuthenTec AES1660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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 "aes1660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "aesx660.h"
#include "aes1660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 128
#define SCALE_FACTOR 2
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
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;
aesdev->init_seqs_len[1] = array_n_elements(aes1660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes1660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes1660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x1660 },
{ .vendor = 0x08ff, .product = 0x1680 },
{ .vendor = 0x08ff, .product = 0x1681 },
{ .vendor = 0x08ff, .product = 0x1682 },
{ .vendor = 0x08ff, .product = 0x1683 },
{ .vendor = 0x08ff, .product = 0x1684 },
{ .vendor = 0x08ff, .product = 0x1685 },
{ .vendor = 0x08ff, .product = 0x1686 },
{ .vendor = 0x08ff, .product = 0x1687 },
{ .vendor = 0x08ff, .product = 0x1688 },
{ .vendor = 0x08ff, .product = 0x1689 },
{ .vendor = 0x08ff, .product = 0x168a },
{ .vendor = 0x08ff, .product = 0x168b },
{ .vendor = 0x08ff, .product = 0x168c },
{ .vendor = 0x08ff, .product = 0x168d },
{ .vendor = 0x08ff, .product = 0x168e },
{ .vendor = 0x08ff, .product = 0x168f },
{ 0, 0, 0, },
};
struct fp_img_driver aes1660_driver = {
.driver = {
.id = AES1660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.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,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};
File diff suppressed because it is too large Load Diff
+48 -134
View File
@@ -2,7 +2,7 @@
* AuthenTec AES2501 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2007-2008, 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from http://home.gna.org/aes2501, relicensed with permission
*
@@ -30,7 +30,9 @@
#include <aeslib.h>
#include <fp_internal.h>
#include "aes2501.h"
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
@@ -67,6 +69,7 @@ struct aes2501_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int no_finger_cnt;
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
@@ -245,119 +248,6 @@ static int sum_histogram_values(unsigned char *data, uint8_t threshold)
return r;
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
*min_error = 255 * FRAME_SIZE;
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(struct aes2501_dev *aesdev, unsigned char *output,
gboolean reverse, unsigned int *errors_sum)
{
uint8_t *assembled = output;
int frame;
uint32_t image_height = FRAME_HEIGHT;
unsigned int min_error;
size_t num_strips = aesdev->strips_len;
GSList *list_entry = aesdev->strips;
*errors_sum = 0;
/* Rotating given data by 90 degrees
* Taken from document describing aes2501 image format
* TODO: move reversing detection here */
if (reverse)
output += (num_strips - 1) * FRAME_SIZE;
for (frame = 0; frame < num_strips; frame++) {
aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
size_t final_size;
struct fp_img *img;
unsigned int errors_sum, r_errors_sum;
BUG_ON(aesdev->strips_len == 0);
/* reverse list */
aesdev->strips = g_slist_reverse(aesdev->strips);
/* create buffer big enough for max image */
img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(aesdev, 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");
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * FRAME_WIDTH;
img = fpi_img_resize(img, final_size);
fpi_imgdev_image_captured(dev, img);
/* free strips and strip list */
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
}
/****** FINGER PRESENCE DETECTION ******/
static const struct aes_regwrite finger_det_reqs[] = {
@@ -514,13 +404,13 @@ static const struct aes_regwrite capture_reqs_2[] = {
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
static const struct aes_regwrite strip_scan_reqs[] = {
static struct aes_regwrite strip_scan_reqs[] = {
{ AES2501_REG_IMAGCTRL,
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE },
{ AES2501_REG_STRTCOL, 0x00 },
{ AES2501_REG_ENDCOL, 0x2f },
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
{ AES2501_REG_ADREFHI, 0x5b },
{ AES2501_REG_ADREFHI, AES2501_ADREFHI_MAX_VALUE },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
@@ -559,12 +449,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
goto out;
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
memcpy(stripdata, data + 1, 192*8);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
threshold = regval_from_dump(data + 1 + 192*8 + 1 + 16*2 + 1 + 8,
AES2501_REG_DATFMT);
if (threshold < 0) {
@@ -572,27 +456,54 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
goto out;
}
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
if (sum < 0) {
fpi_ssm_mark_aborted(ssm, sum);
goto out;
}
fp_dbg("sum=%d", sum);
/* FIXME: 0 might be too low as a threshold */
/* FIXME: sometimes we get 0 in the middle of a scan, should we wait for
* a few consecutive zeroes? */
/* FIXME: we should have an upper limit on the number of strips */
if (sum < AES2501_SUM_LOW_THRESH) {
strip_scan_reqs[4].value -= 0x8;
if (strip_scan_reqs[4].value < AES2501_ADREFHI_MIN_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MIN_VALUE;
} else if (sum > AES2501_SUM_HIGH_THRESH) {
strip_scan_reqs[4].value += 0x8;
if (strip_scan_reqs[4].value > AES2501_ADREFHI_MAX_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
}
fp_dbg("ADREFHI is %.2x", strip_scan_reqs[4].value);
/* If sum is 0, finger has been removed */
/* Sum is 0, maybe finger was removed? Wait for 3 empty frames
* to ensure
*/
if (sum == 0) {
/* assemble image and submit it to library */
assemble_and_submit_image(dev);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
memcpy(stripdata, data + 1, 192*8);
aesdev->no_finger_cnt = 0;
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
@@ -677,6 +588,9 @@ static void start_capture(struct fp_img_dev *dev)
return;
}
aesdev->no_finger_cnt = 0;
/* Reset gain */
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
@@ -945,7 +859,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes2501_driver = {
.driver = {
.id = 4,
.id = AES2501_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2501",
.id_table = id_table,
+6
View File
@@ -167,4 +167,10 @@ enum aes2501_sensor_gain2 {
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
#define AES2501_ADREFHI_MIN_VALUE 0x28
#define AES2501_ADREFHI_MAX_VALUE 0x58
#define AES2501_SUM_HIGH_THRESH 1000
#define AES2501_SUM_LOW_THRESH 700
#endif /* __AES2501_H */
+650
View File
@@ -0,0 +1,650 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2012 Vasily Khoruzhick
*
* Based on AES2501 driver
*
* 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 "aes2550"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aes2550.h"
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
/*
* The AES2550 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 192x16 frame to the
* computer.
* Unless the user is scanning their finger unreasonably fast, the frames
* *will* overlap. The implementation below detects this overlap and produces
* a contiguous image as the end result.
* The fact that the user determines the length of the swipe (and hence the
* number of useful frames) and also the fact that overlap varies means that
* images returned from this driver vary in height.
*/
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
struct aes2550_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int heartbeat_cnt;
};
/****** FINGER PRESENCE DETECTION ******/
static unsigned char finger_det_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x95, (8 << AES2550_REG95_COL_SCANNED_OFS) | (1 << AES2550_REG95_EPIX_AVG_OFS),
0xad, 0x00,
0xbd, (0 << AES2550_REGBD_LPO_IN_15_8_OFS),
0xbe, (0 << AES2550_REGBE_LPO_IN_7_0_OFS),
0xcf, AES2550_REGCF_INTERFERENCE_CHK_EN,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_RUN_FD,
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
unsigned char *data = transfer->buffer;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
}
fp_dbg("transfer completed, len: %.4x, data: %.2x %.2x",
transfer->actual_length, (int)data[0], (int)data[1]);
/* Check if we got 2 bytes, reg address 0x83 and its value */
if ((transfer->actual_length >= 2) && (data[0] == 0x83) && (data[1] & AES2550_REG83_FINGER_PRESENT)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_reqs_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, got %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
goto exit_free_transfer;
}
/* 2 bytes of result */
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct aes2550_dev *aesdev = dev->priv;
struct libusb_transfer *transfer;
fp_dbg("");
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, finger_det_reqs,
sizeof(finger_det_reqs), finger_det_reqs_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
static unsigned char capture_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_HGC_ENABLE),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0x8f, AES2550_REG8F_AUTH_DISABLE | AES2550_REG8F_EHISTO_DISABLE,
0xbf, AES2550_REGBF_RSR_DIR_UPDOWN_MOTION | AES2550_REGBF_RSR_LEVEL_SUPER_RSR,
0xcf, (3 << AES2550_REGCF_INTERFERENCE_AVG_OFFS) | AES2550_REGCF_INTERFERENCE_AVG_EN,
0xdc, (1 << AES2550_REGDC_BP_NUM_REF_SWEEP_OFS),
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x03, /* Heart beat cmd, 3 * 16 cycles without sending image */
AES2550_CMD_GET_ENROLL_IMG,
};
static unsigned char capture_set_idle_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_SET_IDLE_MODE,
};
enum capture_states {
CAPTURE_WRITE_REQS,
CAPTURE_READ_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
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;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
fp_dbg("Bogus magic: %.2x\n", (int)(data[0]));
return -EPROTO;
}
len = data[1] * 256 + data[2];
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;
}
memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
return 0;
}
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_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
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 aes2550_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length >= 2)
fp_dbg("data: %.2x %.2x", (int)data[0], (int)data[1]);
switch (transfer->actual_length) {
case AES2550_STRIP_SIZE:
r = process_strip_data(ssm, data);
if (r < 0) {
fp_dbg("Processing strip data failed: %d", r);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
aesdev->heartbeat_cnt = 0;
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
case AES2550_HEARTBEAT_SIZE:
if (data[0] == AES2550_HEARTBEAT_MAGIC) {
/* No data for a long time => finger was removed or there's no movement */
aesdev->heartbeat_cnt++;
if (aesdev->heartbeat_cnt == 3) {
/* Got 3 heartbeat message, that's enough to consider that finger was removed,
* assemble image and submit it to the library */
fp_dbg("Got 3 heartbeats => finger removed");
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
}
}
break;
default:
fp_dbg("Short frame %d, skip", transfer->actual_length);
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int r;
switch (ssm->cur_state) {
case CAPTURE_WRITE_REQS:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_reqs,
sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
case CAPTURE_SET_IDLE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_set_idle_reqs,
sizeof(capture_set_idle_reqs), capture_set_idle_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
fp_dbg("Capture completed");
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aesdev->heartbeat_cnt = 0;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
static unsigned char init_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_FORCE_FINGER_PRESENT),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0xa8, AES2550_REGA8_DIG_BIT_EN,
0x81, AES2550_REG81_NSHOT,
};
static unsigned char calibrate_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
AES2550_CMD_CALIBRATE,
AES2550_CMD_READ_CALIBRATION_DATA,
};
enum activate_states {
WRITE_INIT,
READ_DATA,
CALIBRATE,
READ_CALIB_TABLE,
ACTIVATE_NUM_STATES,
};
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);
}
libusb_free_transfer(transfer);
}
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);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/* TODO: use calibration table, datasheet is rather terse on that
* need more info for implementaion */
static void calibrate_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);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int r;
switch (ssm->cur_state) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, init_reqs,
sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
init_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
case CALIBRATE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, calibrate_reqs,
sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_CALIB_TABLE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
calibrate_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
if (!ssm->error)
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
fp_dbg("");
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}
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 aes2550_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 const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2550 }, /* AES2550 */
{ .vendor = 0x08ff, .product = 0x2810 }, /* AES2810 */
{ 0, 0, 0, },
};
struct fp_img_driver aes2550_driver = {
.driver = {
.id = AES2550_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2550/AES2810",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};
+114
View File
@@ -0,0 +1,114 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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 __AES2550_H
#define __AES2550_H
/* Registers bits */
#define AES2550_REG80_MASTER_RESET (1 << 0)
#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1)
#define AES2550_REG80_LPO_START (1 << 2)
#define AES2550_REG80_HGC_ENABLE (1 << 3)
#define AES2550_REG80_SENSOR_MODE_OFS (4)
#define AES2550_REG80_AUTO_RESTART_FD (1 << 6)
#define AES2550_REG80_EXT_REG_ENABLE (1 << 7)
#define AES2550_REG81_CONT_SCAN (1 << 0)
#define AES2550_REG81_READ_REG (1 << 1)
#define AES2550_REG81_NSHOT (1 << 2)
#define AES2550_REG81_RUN_FD (1 << 3)
#define AES2550_REG81_READ_ID (1 << 4)
#define AES2550_REG81_RUN_CAL (1 << 5)
#define AES2550_REG81_RUN_TIMER (1 << 6)
#define AES2550_REG81_RUN_BIST (1 << 7)
#define AES2550_REG83_FINGER_PRESENT (1 << 7)
#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7)
#define AES2550_REG8F_EDATA_DISABLE (1 << 1)
#define AES2550_REG8F_AUTH_DISABLE (1 << 2)
#define AES2550_REG8F_EHISTO_DISABLE (1 << 3)
#define AES2550_REG8F_HISTO64 (1 << 4)
#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6)
#define AES2550_REG95_COL_SCANNED_OFS (0)
#define AES2550_REG95_EPIX_AVG_OFS (4)
#define AES2550_REGA8_DIG_BIT_DATA_OFS (0)
#define AES2550_REGA8_DIG_BIT_EN (1 << 4)
#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5)
#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6)
#define AES2550_REGAD_LPFD_AVG_OFS (0)
#define AES2550_REGAD_DETECT_FGROFF (1 << 4)
#define AES2550_REGAD_ADVRANGE_2V (1 << 6)
#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1)
#define AES2550_REGB1_ANALOG_RESET (1 << 2)
#define AES2550_REGB1_ANALOG_PD (1 << 3)
#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4)
#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5)
#define AES2550_REGB1_RESET_UHSM (1 << 6)
#define AES2550_REGB1_RESET_SENSOR (1 << 7)
#define AES2550_REGBD_LPO_IN_15_8_OFS (0)
#define AES2550_REGBE_LPO_IN_7_0_OFS (0)
#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0)
#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0)
#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0)
#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0)
#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2)
#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2)
#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2)
#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4)
#define AES2550_REGBF_QUADRATURE_MODE (1 << 5)
#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0)
#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1)
#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4)
#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0)
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
/* Commands */
enum aes2550_cmds {
AES2550_CMD_SET_IDLE_MODE = 0x00,
AES2550_CMD_RUN_FD = 0x01,
AES2550_CMD_GET_ENROLL_IMG = 0x02,
AES2550_CMD_CALIBRATE = 0x06,
AES2550_CMD_READ_CALIBRATION_DATA = 0x10,
AES2550_CMD_HEARTBEAT = 0x70,
};
/* Messages */
#define AES2550_STRIP_SIZE (0x31e + 3)
#define AES2550_HEARTBEAT_SIZE (4 + 3)
#define AES2550_EDATA_MAGIC 0xe0
#define AES2550_HEARTBEAT_MAGIC 0xdb
#define AES2550_EP_IN_BUF_SIZE 8192
#endif
+121
View File
@@ -0,0 +1,121 @@
/*
* AuthenTec AES2660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* 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 "aes2660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "aesx660.h"
#include "aes2660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 192
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
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;
aesdev->init_seqs_len[1] = array_n_elements(aes2660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes2660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes2660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2660 },
{ .vendor = 0x08ff, .product = 0x2680 },
{ .vendor = 0x08ff, .product = 0x2681 },
{ .vendor = 0x08ff, .product = 0x2682 },
{ .vendor = 0x08ff, .product = 0x2683 },
{ .vendor = 0x08ff, .product = 0x2684 },
{ .vendor = 0x08ff, .product = 0x2685 },
{ .vendor = 0x08ff, .product = 0x2686 },
{ .vendor = 0x08ff, .product = 0x2687 },
{ .vendor = 0x08ff, .product = 0x2688 },
{ .vendor = 0x08ff, .product = 0x2689 },
{ .vendor = 0x08ff, .product = 0x268a },
{ .vendor = 0x08ff, .product = 0x268b },
{ .vendor = 0x08ff, .product = 0x268c },
{ .vendor = 0x08ff, .product = 0x268d },
{ .vendor = 0x08ff, .product = 0x268e },
{ .vendor = 0x08ff, .product = 0x268f },
{ .vendor = 0x08ff, .product = 0x2691 },
{ 0, 0, 0, },
};
struct fp_img_driver aes2660_driver = {
.driver = {
.id = AES2660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -27,6 +27,8 @@
#include <aeslib.h>
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
@@ -153,7 +155,7 @@ static void img_cb(struct libusb_transfer *transfer)
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_im_resize(tmp, ENLARGE_FACTOR);
img = fpi_im_resize(tmp, ENLARGE_FACTOR, ENLARGE_FACTOR);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
@@ -249,7 +251,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes4000_driver = {
.driver = {
.id = 3,
.id = AES4000_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES4000",
.id_table = id_table,
+637
View File
@@ -0,0 +1,637 @@
/*
* AuthenTec AES1660/AES2660 common routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2008,2012 Vasily Khoruzhick
*
* Based on AES2550 driver
*
* 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 "aesX660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aesx660.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FRAME_HEIGHT 8
#define min(a, b) (((a) < (b)) ? (a) : (b))
static void aesX660_send_cmd_timeout(struct fpi_ssm *ssm, const unsigned char *cmd,
size_t cmd_len, libusb_transfer_cb_fn callback, int timeout)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT,
(unsigned char *)cmd, cmd_len,
callback, ssm, timeout);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("failed to submit transfer\n");
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
static void aesX660_send_cmd(struct fpi_ssm *ssm, const unsigned char *cmd,
size_t cmd_len, libusb_transfer_cb_fn callback)
{
return aesX660_send_cmd_timeout(ssm, cmd, cmd_len, callback, BULK_TIMEOUT);
}
static void aesX660_read_response(struct fpi_ssm *ssm, size_t buf_len,
libusb_transfer_cb_fn callback)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
data = g_malloc(buf_len);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
data, buf_len,
callback, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("Failed to submit rx transfer: %d\n", r);
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
static void aesX660_send_cmd_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 {
fp_dbg("tx transfer status: %d, actual_len: %.4x\n",
transfer->status, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void aesX660_read_calibrate_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* Calibrate response was read correctly? */
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE) {
fp_dbg("Bogus calibrate response: %.2x\n", data[0]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
fpi_ssm_next_state(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/****** FINGER PRESENCE DETECTION ******/
enum finger_det_states {
FINGER_DET_SEND_LED_CMD,
FINGER_DET_SEND_FD_CMD,
FINGER_DET_READ_FD_DATA,
FINGER_DET_SET_IDLE,
FINGER_DET_NUM_STATES,
};
static void finger_det_read_fd_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
aesdev->fd_data_transfer = NULL;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("Cancelling transfer...\n");
fpi_ssm_next_state(ssm);
goto out;
}
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("Failed to read FD data\n");
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE) {
fp_dbg("Bogus FD response: %.2x\n", data[0]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
if (data[AESX660_FINGER_PRESENT_OFFSET] == AESX660_FINGER_PRESENT || aesdev->deactivating) {
/* Finger present or we're deactivating... */
fpi_ssm_next_state(ssm);
} else {
fp_dbg("Wait for finger returned %.2x as result\n",
data[AESX660_FINGER_PRESENT_OFFSET]);
fpi_ssm_jump_to_state(ssm, FINGER_DET_SEND_FD_CMD);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_set_idle_cmd_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_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void finger_det_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
int err = ssm->error;
fp_dbg("Finger detection completed");
fpi_imgdev_report_finger_status(dev, TRUE);
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else {
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
}
}
static void finger_det_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case FINGER_DET_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_blink_cmd, sizeof(led_blink_cmd),
aesX660_send_cmd_cb);
break;
case FINGER_DET_SEND_FD_CMD:
aesX660_send_cmd_timeout(ssm, wait_for_finger_cmd, sizeof(wait_for_finger_cmd),
aesX660_send_cmd_cb, 0);
break;
case FINGER_DET_READ_FD_DATA:
/* Should return 4 byte of response */
aesX660_read_response(ssm, 4, finger_det_read_fd_data_cb);
break;
case FINGER_DET_SET_IDLE:
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
finger_det_set_idle_cmd_cb);
break;
}
}
static void start_finger_detection(struct fp_img_dev *dev)
{
struct fpi_ssm *ssm;
struct aesX660_dev *aesdev = dev->priv;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, finger_det_run_state, FINGER_DET_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, finger_det_sm_complete);
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_SEND_LED_CMD,
CAPTURE_SEND_CAPTURE_CMD,
CAPTURE_READ_STRIPE_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
{
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;
}
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
data[AESX660_LAST_FRAME_OFFSET]);
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_len++;
return (data[AESX660_LAST_FRAME_OFFSET] & AESX660_LAST_FRAME_BIT);
} else {
return 0;
}
}
static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img, *tmp;
aesdev->strips = g_slist_reverse(aesdev->strips);
tmp = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT);
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);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
int finger_missing = 0;
size_t copied, actual_len = transfer->actual_length;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
fp_dbg("Got %d bytes of data", actual_len);
do {
copied = min(aesdev->buffer_max - aesdev->buffer_size, actual_len);
memcpy(aesdev->buffer + aesdev->buffer_size,
data,
copied);
actual_len -= copied;
data += copied;
aesdev->buffer_size += copied;
fp_dbg("Copied %.4x bytes into internal buffer",
copied);
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;
fp_dbg("Got frame, type %.2x size %.4x",
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
aesdev->buffer_max);
continue;
} else {
finger_missing |= process_stripe_data(ssm, aesdev->buffer);
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesdev->buffer_size = 0;
}
}
} while (actual_len);
fp_dbg("finger %s\n", finger_missing ? "missing" : "present");
if (finger_missing) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_STRIPE_DATA);
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
switch (ssm->cur_state) {
case CAPTURE_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_solid_cmd, sizeof(led_solid_cmd),
aesX660_send_cmd_cb);
break;
case CAPTURE_SEND_CAPTURE_CMD:
aesdev->buffer_size = 0;
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesX660_send_cmd(ssm, aesdev->start_imaging_cmd,
aesdev->start_imaging_cmd_len,
aesX660_send_cmd_cb);
break;
case CAPTURE_READ_STRIPE_DATA:
aesX660_read_response(ssm, AESX660_BULK_TRANSFER_SIZE,
capture_read_stripe_data_cb);
break;
case CAPTURE_SET_IDLE:
fp_dbg("Got %d frames\n", aesdev->strips_len);
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
capture_set_idle_cmd_cb);
break;
}
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
int err = ssm->error;
fp_dbg("Capture completed");
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else
start_finger_detection(dev);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
enum activate_states {
ACTIVATE_SET_IDLE,
ACTIVATE_SEND_READ_ID_CMD,
ACTIVATE_READ_ID,
ACTIVATE_SEND_CALIBRATE_CMD,
ACTIVATE_READ_CALIBRATE_DATA,
ACTIVATE_SEND_INIT_CMD,
ACTIVATE_READ_INIT_RESPONSE,
ACTIVATE_NUM_STATES,
};
static void activate_read_id_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_id cmd failed\n");
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] == 0x07) {
fp_dbg("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x\n",
data[4], data[3], data[5], data[6], data[7]);
} else {
fp_dbg("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
switch (aesdev->init_seq_idx) {
case 0:
aesdev->init_seq = aesdev->init_seqs[0];
aesdev->init_seq_len = aesdev->init_seqs_len[0];
aesdev->init_seq_idx = 1;
aesdev->init_cmd_idx = 0;
/* Do calibration only after 1st init sequence */
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
break;
case 1:
aesdev->init_seq = aesdev->init_seqs[1];
aesdev->init_seq_len = aesdev->init_seqs_len[1];
aesdev->init_seq_idx = 2;
aesdev->init_cmd_idx = 0;
fpi_ssm_next_state(ssm);
break;
default:
fp_dbg("Failed to init device! init status: %.2x\n", data[7]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_read_init_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
fp_dbg("read_init_cb\n");
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_init transfer status: %d, actual_len: %d\n", transfer->status, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] != 0x42 || data[3] != 0x01) {
fp_dbg("Bogus read init response: %.2x %.2x\n", data[0],
data[3]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
aesdev->init_cmd_idx++;
if (aesdev->init_cmd_idx == aesdev->init_seq_len) {
if (aesdev->init_seq_idx < 2)
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_READ_ID_CMD);
else
fpi_ssm_mark_completed(ssm);
goto out;
}
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
switch (ssm->cur_state) {
case ACTIVATE_SET_IDLE:
aesdev->init_seq_idx = 0;
fp_dbg("Activate: set idle\n");
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_SEND_READ_ID_CMD:
fp_dbg("Activate: read ID\n");
aesX660_send_cmd(ssm, read_id_cmd, sizeof(read_id_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_ID:
/* Should return 8-byte response */
aesX660_read_response(ssm, 8, activate_read_id_cb);
break;
case ACTIVATE_SEND_INIT_CMD:
fp_dbg("Activate: send init seq #%d cmd #%d\n",
aesdev->init_seq_idx,
aesdev->init_cmd_idx);
aesX660_send_cmd(ssm,
aesdev->init_seq[aesdev->init_cmd_idx].cmd,
aesdev->init_seq[aesdev->init_cmd_idx].len,
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_INIT_RESPONSE:
fp_dbg("Activate: read init response\n");
/* Should return 4-byte response */
aesX660_read_response(ssm, 4, activate_read_init_cb);
break;
case ACTIVATE_SEND_CALIBRATE_CMD:
aesX660_send_cmd(ssm, calibrate_cmd, sizeof(calibrate_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_CALIBRATE_DATA:
/* Should return 4-byte response */
aesX660_read_response(ssm, 4, aesX660_read_calibrate_data_cb);
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int err = ssm->error;
fp_dbg("status %d", err);
fpi_imgdev_activate_complete(dev, err);
fpi_ssm_free(ssm);
if (!err)
start_finger_detection(dev);
}
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
void aesX660_dev_deactivate(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
if (aesdev->fd_data_transfer)
libusb_cancel_transfer(aesdev->fd_data_transfer);
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
fp_dbg("");
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}
+117
View File
@@ -0,0 +1,117 @@
/*
* AuthenTec AES1660/AES2660 common definitions
* Copyright (c) 2012 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 __AESX660_H
#define __AESX660_H
#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_CALIBRATE_RESPONSE 0x06
#define AESX660_FINGER_DET_RESPONSE 0x40
#define AESX660_FINGER_PRESENT_OFFSET 0x03
#define AESX660_FINGER_PRESENT 0x01
#define AESX660_IMAGE_OK_OFFSET 0x03
#define AESX660_IMAGE_OK 0x0d
#define AESX660_LAST_FRAME_OFFSET 0x04
#define AESX660_LAST_FRAME_BIT 0x01
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096
struct aesX660_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
struct aesX660_cmd *init_seq;
size_t init_seq_len;
unsigned int init_cmd_idx;
unsigned int init_seq_idx;
struct libusb_transfer *fd_data_transfer;
unsigned char *buffer;
size_t buffer_size;
size_t buffer_max;
/* Device-specific stuff */
int h_scale_factor;
struct aesX660_cmd *init_seqs[2];
size_t init_seqs_len[2];
unsigned char *start_imaging_cmd;
size_t start_imaging_cmd_len;
unsigned int frame_width;
};
struct aesX660_cmd {
const unsigned char *cmd;
size_t len;
};
/* 0x77 cmd seems to control LED, this sequence
* makes LED blink
*/
static const unsigned char led_blink_cmd[] = {
0x77, 0x18, 0x00,
0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf3,
0x01, 0x00, 0x7f
};
/* This sequence makes LED light solid
*/
static const unsigned char led_solid_cmd[] = {
0x77, 0x18, 0x00, 0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f
};
static const unsigned char wait_for_finger_cmd[] = {
0x20,
0x40, 0x04, 0x00, 0x02, 0x1e, 0x00, 0x32
};
/* 0x40 cmd response
*
static const unsigned char pkt1371[] = {
0x40, 0x01, 0x00, 0x01
};
*/
static const unsigned char set_idle_cmd[] = {
0x0d, /* Reset or "set idle"? */
};
static const unsigned char read_id_cmd[] = {
0x44, 0x02, 0x00, 0x08, 0x00, /* Max transfer size is 8 */
0x07, /* Read ID? */
};
static const unsigned char calibrate_cmd[] = {
0x44, 0x02, 0x00, 0x04, 0x00,
0x06,
};
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
void aesX660_dev_deactivate(struct fp_img_dev *dev);
#endif
+41
View File
@@ -0,0 +1,41 @@
/*
* Driver IDs
* Copyright (C) 2012 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 __DRIVER_IDS
#define __DRIVER_IDS
enum {
UPEKTS_ID = 1,
URU4000_ID = 2,
AES4000_ID = 3,
AES2501_ID = 4,
UPEKTC_ID = 5,
AES1610_ID = 6,
FDU2000_ID = 7,
VCOM5S_ID = 8,
UPEKSONLY_ID = 9,
VFS101_ID = 10,
VFS301_ID = 11,
AES2550_ID = 12,
UPEKE2_ID = 13,
AES1660_ID = 14,
AES2660_ID = 15,
};
#endif
+3 -1
View File
@@ -26,6 +26,8 @@
#define FP_COMPONENT "fdu2000"
#include <fp_internal.h>
#include "driver_ids.h"
#ifndef HAVE_MEMMEM
gpointer
memmem(const gpointer haystack, size_t haystack_len, const gpointer needle, size_t needle_len) {
@@ -305,7 +307,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver fdu2000_driver = {
.driver = {
.id = 7,
.id = FDU2000_ID,
.name = FP_COMPONENT,
.full_name = "Secugen FDU 2000",
.id_table = id_table,
+5 -3
View File
@@ -37,6 +37,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define TIMEOUT 5000
@@ -738,14 +740,14 @@ static void initsm_run_state(struct fpi_ssm *ssm)
break;
case SEND28_51: ;
unsigned char dummy28_51[] = { 0x04, 0x0a, 0x00, 0x00, 0x00 };
initsm_send_msg28_handler(ssm, 0x51, &dummy28_51, 5);
initsm_send_msg28_handler(ssm, 0x51, dummy28_51, 5);
break;
case READ28_51:
initsm_read_msg_handler(ssm, read28_51_cb);
break;
case SEND28_07: ;
unsigned char dummy28_07[] = { 0x04, 0x20, 0x00, 0x00, 0x00 };
initsm_send_msg28_handler(ssm, 0x07, &dummy28_07, 5);
initsm_send_msg28_handler(ssm, 0x07, dummy28_07, 5);
break;
case READ28_07:
initsm_read_msg_handler(ssm, read28_07_cb);
@@ -1456,7 +1458,7 @@ static const struct usb_id id_table[] = {
};
struct fp_driver upeke2_driver = {
.id = 1,
.id = UPEKE2_ID,
.name = FP_COMPONENT,
.full_name = "UPEK Eikon 2",
.id_table = id_table,
+4 -7
View File
@@ -30,6 +30,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_TIMEOUT 1000
#define IMG_WIDTH 288
#define NUM_BULK_TRANSFERS 24
@@ -467,7 +469,6 @@ static void write_regs_finished(struct write_regs_data *wrdata, int result)
static void write_regs_iterate(struct write_regs_data *wrdata)
{
struct fpi_ssm *ssm;
struct libusb_control_setup *setup;
const struct sonly_regwrite *regwrite;
int r;
@@ -478,7 +479,6 @@ static void write_regs_iterate(struct write_regs_data *wrdata)
}
regwrite = &wrdata->regs[wrdata->regs_written];
ssm = wrdata->ssm;
fp_dbg("set %02x=%02x", regwrite->reg, regwrite->value);
setup = libusb_control_transfer_get_setup(wrdata->transfer);
@@ -1019,9 +1019,6 @@ static void initsm_2016_run_state(struct fpi_ssm *ssm)
static void initsm_1000_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case INITSM_1000_WRITEV_1:
sm_write_regs(ssm, initsm_1000_writev_1, G_N_ELEMENTS(initsm_1000_writev_1));
@@ -1183,7 +1180,7 @@ static void initsm_complete(struct fpi_ssm *ssm)
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct sonly_dev *sdev = dev->priv;
struct fpi_ssm *ssm;
struct fpi_ssm *ssm = NULL;
int i;
sdev->deactivating = FALSE;
@@ -1272,7 +1269,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver upeksonly_driver = {
.driver = {
.id = 9,
.id = UPEKSONLY_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip Sensor-Only",
.id_table = id_table,
+425 -336
View File
@@ -1,6 +1,7 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
* Copyright (C) 2012 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,400 +18,488 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upektc"
#define FP_COMPONENT "upektc"
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#define SENSOR_FULL_IMAGE 59904
#define WAIT_COUNT 5
#include "upektc.h"
#include "driver_ids.h"
typedef char sint8;
typedef unsigned char uint8;
typedef int sint32;
typedef unsigned int uint32;
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
#define UPEKET_EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define UPEKET_EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
/** scan command */
static const unsigned char anScanCommand[ 0x40 ] = {
0x0e, 0x00, 0x03, 0xa8, 0x00, 0xb6, 0xbb, 0xbb,
0xb8, 0xb7, 0xb8, 0xb5, 0xb8, 0xb9, 0xb8, 0xb9,
0xbb, 0xbb, 0xbe, 0xbb, 0x4e, 0x16, 0xf4, 0x77,
0xa8, 0x07, 0x32, 0x00, 0x6a, 0x16, 0xf4, 0x77,
0x78, 0x24, 0x61, 0x00, 0xc8, 0x00, 0xec, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3c, 0xf3, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00
struct upektc_dev {
gboolean deactivating;
const struct setup_cmd *setup_commands;
size_t setup_commands_len;
int ep_in;
int ep_out;
int init_idx;
int sum_threshold;
};
/** init command */
static const unsigned char anInitCommand[ 0x40 ] = {
0x03, 0x00, 0x00, 0x00, 0x02, 0xfb, 0x0f, 0x00,
0xc4, 0xf9, 0x2f, 0x01, 0x6d, 0x4f, 0x01, 0x10,
0x44, 0xf9, 0x2f, 0x01, 0x40, 0x00, 0x00, 0x00,
0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
enum upektc_driver_data {
UPEKTC_2015,
UPEKTC_3001,
};
/**
* \brief Common interaktion routine for the sensor device
* \param dev fingerprint image device pointer
* \param pnRawString raw data string
* \param nLen length we want to read, if 0 do not read at all
* \param pnBuffer buffer pointer we want to store the read buffer
* \return error code
*/
static sint32 askScanner( struct fp_img_dev *dev, const unsigned char *pnRawString, sint32 nLen, sint8 *pnBuffer ) {
sint8 anBuf[ 65535 ];
sint32 nRet;
int transferred;
struct libusb_bulk_transfer msg1 = {
.endpoint = 3,
.data = pnRawString,
.length = 0x40,
};
struct libusb_bulk_transfer msg2 = {
.endpoint = 0x82,
.data = anBuf,
.length = nLen,
};
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static void start_finger_detection(struct fp_img_dev *dev);
nRet = libusb_bulk_transfer(dev->udev, &msg1, &transferred, 1003);
if (transferred != 0x40) {
return -1;
}
/****** INITIALIZATION/DEINITIALIZATION ******/
if ( !nLen ) {
return 0;
}
enum activate_states {
WRITE_INIT,
READ_DATA,
ACTIVATE_NUM_STATES,
};
nRet = libusb_bulk_transfer(dev->udev, &msg2, &transferred, 1003);
if ( ( transferred == nLen ) && ( pnBuffer != NULL ) ) {
memcpy( pnBuffer, anBuf, nLen );
return transferred;
}
static void upektc_next_init_cmd(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
return nRet;
upekdev->init_idx += 1;
if (upekdev->init_idx == upekdev->setup_commands_len)
fpi_ssm_mark_completed(ssm);
else
fpi_ssm_jump_to_state(ssm, WRITE_INIT);
}
/**
* \brief Quick test if finger is on sensor
* \param pnImage image pointer
* \return 1 on yes, 0 on no
*/
static sint32 ValidScan( sint8 *pnImage ) {
sint32 nIndex, nSum;
static void write_init_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
nSum = 0;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
if (upekdev->setup_commands[upekdev->init_idx].response_len)
fpi_ssm_next_state(ssm);
else
upektc_next_init_cmd(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
for ( nIndex = 0; nIndex < SENSOR_FULL_IMAGE; nIndex++ ) {
if ( ( uint8 ) pnImage[ nIndex ] < 160 ) {
nSum++;
static void read_init_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
upektc_next_init_cmd(ssm);
else
fpi_ssm_mark_aborted(ssm, -EIO);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char*)upekdev->setup_commands[upekdev->init_idx].cmd,
UPEKTC_CMD_LEN, write_init_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data,
upekdev->setup_commands[upekdev->init_idx].response_len,
read_init_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
if (!ssm->error)
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
/****** FINGER PRESENCE DETECTION ******/
static int finger_present(unsigned char *img, size_t len, int sum_threshold)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++) {
if (img[i] < 160) {
sum++;
}
}
return nSum < 500 ? 0 : 1;
fp_dbg("finger_present: sum is %d\n", sum);
return sum < sum_threshold ? 0 : 1;
}
/**
* \brief Setup Sensor device
* \param dev fingerprint image device pointer
* \return error code
*/
static sint32 SetupSensor( struct fp_img_dev *dev ) {
libusb_claim_interface(dev->udev, 0);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct upektc_dev *upekdev = dev->priv;
unsigned char *data = transfer->buffer;
/* setup sensor */
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xcc\xf8\x2f\x01\x09\x48\xe7\x77\xf0\xfa\x2f\x01\x09\x48\xe7\x77\xe0\x3a\xe6\x77", 0x00, NULL ) < 0 ) {
return -1;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, got %d bytes", transfer->length,
transfer->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
}
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x58\xf9\x2f\x01\xe9\x4f\x01\x10\xd8\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f", 0x40, NULL ) < 0 ) {
return -2;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xcd\x00\x2c\xf9\x2f\x01\x6d\x4f\x01\x10\xac\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -3;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf8\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c\x00\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -4;
};
if ( askScanner( dev, "\x8b\x00\x00\x00\x3a\x50\xf9\x2f\x01\x18\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x88\xf9\x2f\x01\x91\x99\x00\x10\xf8\x00\x00\x00\xbe\x99\x00\x10\xa0\xa6\x04\x10\x01\x9b\x00\x10\x18\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00", 0x40, NULL ) < 0 ) {
return -5;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -6;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -7;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -8;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x0c\x37\x6a\x3d\x73\x3d\x71\x0e\x01\x0e\x81\x3d\x51\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xf0\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -9;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -10;
};
if ( askScanner( dev, "\x8b\x00\x01\x7c\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x14\xf5\x2f\x01\xa0\x20\x14\x00\x40\xf8\x2f\x01\x05\x90\xf6\x77\x04\x00\x00\x00\x08\x00\x00\x00\x50\xf8\x2f\x01\x40\x39\xf4\x77\xa8\x20\x14\x00\x1c\xf6\x2f\x01\x2c\x20\xf4\x77\x80\x4d\xfb\x77", 0x40, NULL ) < 0 ) {
return -11;
};
if ( askScanner( dev, "\x8b\x00\x03\xc8\x3a\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x40\xf9\x2f\x01\x4f\x9d\x00\x10\x3a\x00\x00\x00\x04\xf9\x01", 0x40, NULL ) < 0 ) {
return -12;
};
if ( askScanner( dev, "\x8b\x00\x04\x02\x06\x0b\x07\x13\x0e\x55\x56\x01\x44\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\x40\x40\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -13;
};
if ( askScanner( dev, "\x07\x00\x20\x00\x3a\x0e\x13\x07\x0f\x14\x07\x10\x15\x07\x12\x16\x07\x13\x17\x07\x14\x18\x07\x15\x18\x07\x16\x19\x07\x17\x1a\x07\x19\x1b\x07\x1a\x1c\x07\x1b\x1d\x07\x1c\x1e\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -14;
};
if ( askScanner( dev, "\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -15;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x0e\x81\x0e\x81\x09\x4d\x00\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21", 0x00, NULL ) < 0 ) {
return -16;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -17;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0e\x85\x36\xd8\xf8\x2f\x01\xf1\x9d\x00\x10\xf8\xf8\x2f\x01\x99\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -18;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\x0d\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -19;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -20;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62", 0x40, NULL ) < 0 ) {
return -21;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xf4\x00\x14\xf9\x2f\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -22;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x20\x6c\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -23;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf9\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -24;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf9\x01\x00\x1c\xf9\x2f\x01\x6d\x4f\x01\x10\x9c\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -25;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x12\x1c\x0c\x1b\x08\x1a\x07\x30\x08\x09\x6d\x08\x27\x00\x9e\x00\x1e\x23\x47\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09", 0x00, NULL ) < 0 ) {
return -26;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -27;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -28;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -29;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x08\x0e\x85\x09\xed\x09\x6d\x09\xed\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01", 0x00, NULL ) < 0 ) {
return -30;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf3\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01\x04\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\x00\x46\x03\x10\x08\x00\x00\x00\x08\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -31;
};
if ( askScanner( dev, "\x84\x00\x00\x00\x32\x02\xa3\x04\x10\x3b\xa3\x04\x10\x1a\xa3\x04\x10\xf9\xa2\x04\x10\xd8\xa2\x00\xb9\x19\xe2\x87\xba\x56\x78\x72\x68\x9e\x7a\xf4\x65\x6d\xd9\xde\xf6\x33\xa2\x04\x10\x12\xa2\x04\x10\xf1\xa1\x04\x10\x04\x00\x00\x00\x00\x00\x00\xb4\x2d\x6c\xe9", 0x40, NULL ) < 0 ) {
return -32;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x1a\x07\x1b\x08\x1c\x0c\x77\x21\xac\xe5\x77\x00\x00\x00\x00\xaa\x4e\x01\x10\x3c\x01\x00\x00\xc4\xf8\x2f\x01\xdc\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\xb9\x19\xe2\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -33;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\xcc\xf8\x2f\x01\x8b\x41\x01\x10\x8c\xf8\x2f\x01\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -34;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x04\x3d\x51\x0a\x00\x01\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfc\xf9\x2f\x01\x31\x10\x01\x10\xd0\xf9\x2f\x01\x00\x00\x00\x00\x1a\x07\x1b\x08\x1c\x0c\xc6\xf8\x66\xbc\xc4\xbe\x0b\x25\xc5\x4c\xf4\x03\x10\x2f\x11\x3f\x12\x44", 0x00, NULL ) < 0 ) {
return -35;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -36;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0a\x10\x36\x88\xf9\x2f\x01\xf1\x9d\x00\x10\xa8\xf9\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -37;
};
if ( askScanner( dev, "\x8b\x00\x00\xbc\x3a\x40\xd3\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8\xf4\x2f\x01\x80\x69\x67\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x3c\x01\x00\x00\xa0\xf5\x2f\x01\x03\x01\x00\x00\x9a\x11\xf4\x77\x9f\x11\xf4\x77\x3c\x01\x00\x00\xa0\xf5\x01", 0x40, NULL ) < 0 ) {
return -38;
};
if ( askScanner( dev, "\x8b\x00\x00\xf6\x3a\x0b\x07\xa5\x03\x2f\x63\x97\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -39;
};
if ( askScanner( dev, "\x8b\x00\x01\x30\x3a\x0b\x00\x00\x00\x00\x00\x00\x12\xcd\xa6\x3c\x36\xec\x6a\x73\x00\x64\x75\xdf\x2e\x13\xec\xca\x3c\x03\x00\x00\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -40;
};
if ( askScanner( dev, "\x8b\x00\x01\x6a\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -41;
};
if ( askScanner( dev, "\x8b\x00\x01\xa4\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xa5\x83\x1b\x8e\xac\x00\x00\x0b\xa5\x08\x08\x03\x00\x00\x01\x02\x03\x06\x00\x00\x00\x00\x00\x8d\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -42;
};
if ( askScanner( dev, "\x8b\x00\x01\xde\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -43;
};
if ( askScanner( dev, "\x8b\x00\x02\x18\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -44;
};
if ( askScanner( dev, "\x8b\x00\x02\x52\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -45;
};
if ( askScanner( dev, "\x8b\x00\x02\x8c\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -46;
};
if ( askScanner( dev, "\x8b\x00\x02\xc6\x2a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -47;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf1\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\x9c\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -48;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf1\x01\x00\xb4\xf9\x2f\x01\x6d\x4f\x01\x10\x34\xf9\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -49;
};
if ( askScanner( dev, "\x8b\x00\x01\x10\x3a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -50;
};
if ( askScanner( dev, "\x8b\x00\x01\x4a\x2e\x0b\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xfb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
/* enable sensor */
if ( askScanner( dev, anInitCommand, 0x00, NULL ) < 0 ) {
return -52;
if (finger_present(data, IMAGE_SIZE, upekdev->sum_threshold)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_cmd_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
struct upektc_dev *upekdev = dev->priv;
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, sent %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
goto exit_free_transfer;
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct upektc_dev *upekdev = dev->priv;
struct libusb_transfer *transfer;
fp_dbg("");
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
finger_det_cmd_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_WRITE_CMD,
CAPTURE_READ_DATA,
CAPTURE_NUM_STATES,
};
static void capture_cmd_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);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
unsigned char *data = transfer->buffer;
struct fp_img *img;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, sent %d bytes", transfer->length, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
img = fpi_img_new(IMAGE_SIZE);
memcpy(img->data, data, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
case CAPTURE_WRITE_CMD:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
capture_cmd_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
fp_dbg("Capture completed");
if (upekdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct upektc_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
upekdev->init_idx = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static int DetectFinger( struct fp_img_dev *dev ) {
sint32 nRet = 0;
uint8 *pnData = NULL;
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet != SENSOR_FULL_IMAGE ) {
nRet = 0;
goto end;
}
nRet = ValidScan( pnData );
end:
g_free( pnData );
return nRet;
upekdev->deactivating = TRUE;
}
static int awaitFingerOn( struct fp_img_dev *dev ) {
int nRet = 0;
int nCount = WAIT_COUNT;
static void complete_deactivation(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
fp_dbg("");
/* wait until a finger is present */
do {
nRet = DetectFinger( dev );
} while ( nRet == 0 );
/* give user time to scan his full finger */
while ( nCount-- ) {
nRet = DetectFinger( dev );
}
return nRet != 1 ? nRet : 0;
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static int capture( struct fp_img_dev *dev, gboolean unconditional, struct fp_img **ppsRet ) {
struct fp_img *psImg = NULL;
uint8 *pnData = NULL;
sint32 nRet = 0;
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct upektc_dev *upekdev;
psImg = fpi_img_new_for_imgdev( dev );
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet == SENSOR_FULL_IMAGE ) {
memcpy( psImg -> data, pnData, SENSOR_FULL_IMAGE );
*ppsRet = psImg;
nRet = 0;
} else {
nRet = -1;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
g_free( pnData );
return nRet;
}
static int dev_init( struct fp_img_dev *dev, unsigned long driver_data ) {
int nResult;
nResult = libusb_claim_interface(dev->udev, 0);
if ( nResult < 0 ) {
fp_err( "could not claim interface 0" );
return nResult;
dev->priv = upekdev = g_malloc0(sizeof(struct upektc_dev));
switch (driver_data) {
case UPEKTC_2015:
upekdev->ep_in = UPEKTC_EP_IN;
upekdev->ep_out = UPEKTC_EP_OUT;
upekdev->setup_commands = upektc_setup_commands;
upekdev->setup_commands_len = array_n_elements(upektc_setup_commands);
upekdev->sum_threshold = UPEKTC_SUM_THRESHOLD;
break;
case UPEKTC_3001:
upekdev->ep_in = UPEKET_EP_IN;
upekdev->ep_out = UPEKET_EP_OUT;
upekdev->setup_commands = upeket_setup_commands;
upekdev->setup_commands_len = array_n_elements(upeket_setup_commands);
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
break;
default:
fp_err("Device variant %d is not known\n", driver_data);
g_free(upekdev);
dev->priv = NULL;
return -ENODEV;
break;
}
nResult = SetupSensor( dev );
return nResult;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_exit( struct fp_img_dev *dev ) {
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 const struct usb_id id_table[] = {
{ .vendor = 0x0483, .product = 0x2015 },
{ .vendor = 0x0483, .product = 0x2015, .driver_data = UPEKTC_2015 },
{ .vendor = 0x147e, .product = 0x3001, .driver_data = UPEKTC_3001 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_driver = {
.driver = {
.id = 5,
.id = UPEKTC_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchChip",
.full_name = "UPEK TouchChip/Eikon Touch 300",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
.img_height = 288,
.img_width = 208,
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 30,
.init = dev_init,
.exit = dev_exit,
.await_finger_on = awaitFingerOn,
.capture = capture,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -35,6 +35,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define TIMEOUT 5000
@@ -1464,7 +1466,7 @@ static const struct usb_id id_table[] = {
};
struct fp_driver upekts_driver = {
.id = 1,
.id = UPEKTS_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip",
.id_table = id_table,
+335 -250
View File
@@ -1,6 +1,7 @@
/*
* Digital Persona U.are.U 4000/4000B driver for libfprint
* Digital Persona U.are.U 4000/4000B/4500 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2012 Timo Teräs <timo.teras@iki.fi>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,19 +30,21 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define EP_INTR (1 | LIBUSB_ENDPOINT_IN)
#define EP_DATA (2 | LIBUSB_ENDPOINT_IN)
#define USB_RQ 0x04
#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
#define CTRL_TIMEOUT 5000
#define BULK_TIMEOUT 5000
#define DATABLK_RQLEN 0x1b340
#define DATABLK_EXPECT 0x1b1c0
#define CAPTURE_HDRLEN 64
#define CTRL_TIMEOUT 5000
#define BULK_TIMEOUT 5000
#define IRQ_LENGTH 64
#define CR_LENGTH 16
#define IMAGE_HEIGHT 290
#define IMAGE_WIDTH 384
enum {
IRQDATA_SCANPWR_ON = 0x56aa,
IRQDATA_FINGER_ON = 0x0101,
@@ -51,7 +54,10 @@ enum {
enum {
REG_HWSTAT = 0x07,
REG_SCRAMBLE_DATA_INDEX = 0x33,
REG_SCRAMBLE_DATA_KEY = 0x34,
REG_MODE = 0x4e,
REG_DEVICE_INFO = 0xf0,
/* firmware starts at 0x100 */
REG_RESPONSE = 0x2000,
REG_CHALLENGE = 0x2010,
@@ -62,7 +68,8 @@ enum {
MODE_AWAIT_FINGER_ON = 0x10,
MODE_AWAIT_FINGER_OFF = 0x12,
MODE_CAPTURE = 0x20,
MODE_SHUT_UP = 0x30,
MODE_CAPTURE_AUX = 0x30,
MODE_OFF = 0x70,
MODE_READY = 0x80,
};
@@ -78,6 +85,7 @@ enum {
static const struct uru4k_dev_profile {
const char *name;
gboolean auth_cr;
gboolean encryption;
} uru4k_dev_info[] = {
[MS_KBD] = {
.name = "Microsoft Keyboard with Fingerprint Reader",
@@ -102,21 +110,10 @@ static const struct uru4k_dev_profile {
[DP_URU4000B] = {
.name = "Digital Persona U.are.U 4000B",
.auth_cr = FALSE,
.encryption = TRUE,
},
};
/* As we don't know the encryption scheme, we have to disable encryption
* by powering the device down and modifying the firmware. The location of
* the encryption control byte changes based on device revision.
*
* We use a search approach to find it: we look at the 3 bytes of data starting
* from these addresses, looking for a pattern "ff X7 41" (where X is dontcare)
* When we find a pattern we know that the encryption byte ius the X7 byte.
*/
static const uint16_t fwenc_offsets[] = {
0x510, 0x62d, 0x792, 0x7f4,
};
typedef void (*irq_cb_fn)(struct fp_img_dev *dev, int status, uint16_t type,
void *user_data);
typedef void (*irqs_stopped_cb_fn)(struct fp_img_dev *dev);
@@ -125,11 +122,14 @@ struct uru4k_dev {
const struct uru4k_dev_profile *profile;
uint8_t interface;
enum fp_imgdev_state activate_state;
unsigned char last_reg_rd;
unsigned char last_reg_rd[16];
unsigned char last_hwstat;
struct libusb_transfer *irq_transfer;
struct libusb_transfer *img_transfer;
void *img_data;
uint16_t img_lines_done, img_block;
uint32_t img_enc_seed;
irq_cb_fn irq_cb;
void *irq_cb_data;
@@ -225,7 +225,7 @@ static int write_reg(struct fp_img_dev *dev, uint16_t reg,
}
typedef void (*read_regs_cb_fn)(struct fp_img_dev *dev, int status,
unsigned char *data, void *user_data);
uint16_t num_regs, unsigned char *data, void *user_data);
struct read_regs_data {
struct fp_img_dev *dev;
@@ -248,7 +248,7 @@ static void read_regs_cb(struct libusb_transfer *transfer)
else
data = libusb_control_transfer_get_data(transfer);
rrdata->callback(rrdata->dev, r, data, rrdata->user_data);
rrdata->callback(rrdata->dev, r, transfer->actual_length, data, rrdata->user_data);
g_free(rrdata);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
@@ -284,12 +284,6 @@ static int read_regs(struct fp_img_dev *dev, uint16_t first_reg,
return r;
}
static int read_reg(struct fp_img_dev *dev, uint16_t reg,
read_regs_cb_fn callback, void *user_data)
{
return read_regs(dev, reg, 1, callback, user_data);
}
/*
* HWSTAT
*
@@ -325,7 +319,7 @@ static void response_cb(struct fp_img_dev *dev, int status, void *user_data)
}
static void challenge_cb(struct fp_img_dev *dev, int status,
unsigned char *data, void *user_data)
uint16_t num_regs, unsigned char *data, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
@@ -469,95 +463,10 @@ static void stop_irq_handler(struct fp_img_dev *dev, irqs_stopped_cb_fn cb)
}
}
/***** IMAGING LOOP *****/
static int start_imaging_loop(struct fp_img_dev *dev);
static void image_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct uru4k_dev *urudev = dev->priv;
int hdr_skip = CAPTURE_HDRLEN;
int image_size = DATABLK_EXPECT - CAPTURE_HDRLEN;
struct fp_img *img;
int r = 0;
/* remove the global reference early: otherwise we may report results,
* leading to immediate deactivation of driver, which will potentially
* try to cancel an already-completed transfer */
urudev->img_transfer = NULL;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("cancelled");
g_free(transfer->buffer);
libusb_free_transfer(transfer);
return;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
r = -EIO;
goto out;
}
if (transfer->actual_length == image_size) {
/* no header! this is rather odd, but it happens sometimes with my MS
* keyboard */
fp_dbg("got image with no header!");
hdr_skip = 0;
} else if (transfer->actual_length != DATABLK_EXPECT) {
fp_err("unexpected image capture size (%d)", transfer->actual_length);
r = -EPROTO;
goto out;
}
img = fpi_img_new(image_size);
memcpy(img->data, transfer->buffer + hdr_skip, image_size);
img->flags = FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED | FP_IMG_COLORS_INVERTED;
fpi_imgdev_image_captured(dev, img);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
if (r == 0)
r = start_imaging_loop(dev);
if (r)
fpi_imgdev_session_error(dev, r);
}
static int start_imaging_loop(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
if (!transfer)
return -ENOMEM;
data = g_malloc(DATABLK_RQLEN);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_DATA, data,
DATABLK_RQLEN, image_cb, dev, 0);
urudev->img_transfer = transfer;
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
}
return r;
}
static void stop_imaging_loop(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct libusb_transfer *transfer = urudev->img_transfer;
if (transfer)
libusb_cancel_transfer(transfer);
/* FIXME: should probably wait for cancellation to complete */
}
/***** STATE CHANGING *****/
static int execute_state_change(struct fp_img_dev *dev);
static void finger_presence_irq_cb(struct fp_img_dev *dev, int status,
uint16_t type, void *user_data)
{
@@ -582,33 +491,22 @@ static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct uru4k_dev *urudev = dev->priv;
stop_imaging_loop(dev);
switch (state) {
case IMGDEV_STATE_INACTIVE:
case IMGDEV_STATE_AWAIT_FINGER_ON:
if (!IRQ_HANDLER_IS_RUNNING(urudev))
return -EIO;
urudev->irq_cb = finger_presence_irq_cb;
return write_reg(dev, REG_MODE, MODE_AWAIT_FINGER_ON,
change_state_write_reg_cb, NULL);
case IMGDEV_STATE_CAPTURE:
urudev->irq_cb = NULL;
start_imaging_loop(dev);
return write_reg(dev, REG_MODE, MODE_CAPTURE, change_state_write_reg_cb,
NULL);
case IMGDEV_STATE_AWAIT_FINGER_OFF:
if (!IRQ_HANDLER_IS_RUNNING(urudev))
return -EIO;
urudev->irq_cb = finger_presence_irq_cb;
return write_reg(dev, REG_MODE, MODE_AWAIT_FINGER_OFF,
change_state_write_reg_cb, NULL);
case IMGDEV_STATE_CAPTURE:
break;
default:
fp_err("unrecognised state %d", state);
return -EINVAL;
}
urudev->activate_state = state;
if (urudev->img_transfer != NULL)
return 0;
return execute_state_change(dev);
}
/***** GENERIC STATE MACHINE HELPER FUNCTIONS *****/
@@ -623,17 +521,23 @@ static void sm_write_reg_cb(struct fp_img_dev *dev, int result, void *user_data)
fpi_ssm_next_state(ssm);
}
static void sm_write_reg(struct fpi_ssm *ssm, uint16_t reg,
unsigned char value)
static void sm_write_regs(struct fpi_ssm *ssm, uint16_t first_reg, uint16_t num_regs,
void *data)
{
struct fp_img_dev *dev = ssm->priv;
int r = write_reg(dev, reg, value, sm_write_reg_cb, ssm);
int r = write_regs(dev, first_reg, num_regs, data, sm_write_reg_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
}
static void sm_write_reg(struct fpi_ssm *ssm, uint16_t reg,
unsigned char value)
{
sm_write_regs(ssm, reg, 1, &value);
}
static void sm_read_reg_cb(struct fp_img_dev *dev, int result,
unsigned char *data, void *user_data)
uint16_t num_regs, unsigned char *data, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
@@ -641,27 +545,32 @@ static void sm_read_reg_cb(struct fp_img_dev *dev, int result,
if (result) {
fpi_ssm_mark_aborted(ssm, result);
} else {
urudev->last_reg_rd = *data;
fp_dbg("reg value %x", urudev->last_reg_rd);
memcpy(urudev->last_reg_rd, data, num_regs);
fp_dbg("reg value %x", urudev->last_reg_rd[0]);
fpi_ssm_next_state(ssm);
}
}
static void sm_read_reg(struct fpi_ssm *ssm, uint16_t reg)
static void sm_read_regs(struct fpi_ssm *ssm, uint16_t reg, uint16_t num_regs)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
int r;
fp_dbg("read reg %x", reg);
r = read_reg(dev, reg, sm_read_reg_cb, ssm);
if (num_regs > sizeof(urudev->last_reg_rd)) {
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
fp_dbg("read %d regs at %x", num_regs, reg);
r = read_regs(dev, reg, num_regs, sm_read_reg_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
}
static void sm_set_mode(struct fpi_ssm *ssm, unsigned char mode)
static void sm_read_reg(struct fpi_ssm *ssm, uint16_t reg)
{
fp_dbg("mode %02x", mode);
sm_write_reg(ssm, REG_MODE, mode);
sm_read_regs(ssm, reg, 1);
}
static void sm_set_hwstat(struct fpi_ssm *ssm, unsigned char value)
@@ -670,77 +579,232 @@ static void sm_set_hwstat(struct fpi_ssm *ssm, unsigned char value)
sm_write_reg(ssm, REG_HWSTAT, value);
}
/***** INITIALIZATION *****/
/***** IMAGING LOOP *****/
enum fwfixer_states {
FWFIXER_INIT,
FWFIXER_READ_NEXT,
FWFIXER_WRITE,
FWFIXER_NUM_STATES,
enum imaging_states {
IMAGING_CAPTURE,
IMAGING_SEND_INDEX,
IMAGING_READ_KEY,
IMAGING_DECODE,
IMAGING_REPORT_IMAGE,
IMAGING_NUM_STATES
};
static void fwfixer_read_cb(struct fp_img_dev *dev, int status,
unsigned char *data, void *user_data)
static void image_transfer_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
struct fpi_ssm *ssm = transfer->user_data;
if (status != 0)
fpi_ssm_mark_aborted(ssm, status);
fp_dbg("data: %02x %02x %02x", data[0], data[1], data[2]);
if (data[0] == 0xff && (data[1] & 0x0f) == 0x07 && data[2] == 0x41) {
fp_dbg("using offset %x", fwenc_offsets[urudev->fwfixer_offset]);
urudev->fwfixer_value = data[1];
fpi_ssm_jump_to_state(ssm, FWFIXER_WRITE);
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("cancelled");
fpi_ssm_mark_aborted(ssm, -ECANCELED);
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("error");
fpi_ssm_mark_aborted(ssm, -EIO);
} else {
fpi_ssm_jump_to_state(ssm, FWFIXER_READ_NEXT);
fpi_ssm_next_state(ssm);
}
}
static void fwfixer_run_state(struct fpi_ssm *ssm)
enum {
BLOCKF_CHANGE_KEY = 0x80,
BLOCKF_NO_KEY_UPDATE = 0x04,
BLOCKF_ENCRYPTED = 0x02,
BLOCKF_NOT_PRESENT = 0x01,
};
struct uru4k_image {
uint8_t unknown_00[4];
uint16_t num_lines;
uint8_t key_number;
uint8_t unknown_07[9];
struct {
uint8_t flags;
uint8_t num_lines;
} block_info[15];
uint8_t unknown_2E[18];
uint8_t data[IMAGE_HEIGHT][IMAGE_WIDTH];
};
static uint32_t update_key(uint32_t key)
{
/* linear feedback shift register
* taps at bit positions 1 3 4 7 11 13 20 23 26 29 32 */
uint32_t bit = key & 0x9248144d;
bit ^= bit << 16;
bit ^= bit << 8;
bit ^= bit << 4;
bit ^= bit << 2;
bit ^= bit << 1;
return (bit & 0x80000000) | (key >> 1);
}
static uint32_t do_decode(uint8_t *data, int num_bytes, uint32_t key)
{
uint8_t xorbyte;
int i;
for (i = 0; i < num_bytes - 1; i++) {
/* calculate xor byte and update key */
xorbyte = ((key >> 4) & 1) << 0;
xorbyte |= ((key >> 8) & 1) << 1;
xorbyte |= ((key >> 11) & 1) << 2;
xorbyte |= ((key >> 14) & 1) << 3;
xorbyte |= ((key >> 18) & 1) << 4;
xorbyte |= ((key >> 21) & 1) << 5;
xorbyte |= ((key >> 24) & 1) << 6;
xorbyte |= ((key >> 29) & 1) << 7;
key = update_key(key);
/* decrypt data */
data[i] = data[i+1] ^ xorbyte;
}
/* the final byte is implictly zero */
data[i] = 0;
return update_key(key);
}
static void imaging_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
int r;
struct uru4k_image *img = urudev->img_data;
struct fp_img *fpimg;
uint32_t key;
uint8_t flags, num_lines;
int i, r, to;
char buf[5];
switch (ssm->cur_state) {
case FWFIXER_INIT:
urudev->fwfixer_offset = -1;
fpi_ssm_next_state(ssm);
case IMAGING_CAPTURE:
urudev->img_lines_done = 0;
urudev->img_block = 0;
libusb_fill_bulk_transfer(urudev->img_transfer, dev->udev, EP_DATA,
urudev->img_data, sizeof(struct uru4k_image), image_transfer_cb, ssm, 0);
r = libusb_submit_transfer(urudev->img_transfer);
if (r < 0)
fpi_ssm_mark_aborted(ssm, -EIO);
break;
case FWFIXER_READ_NEXT: ;
int offset = ++urudev->fwfixer_offset;
uint16_t try_addr;
case IMAGING_SEND_INDEX:
fp_dbg("hw header lines %d", img->num_lines);
if (offset == G_N_ELEMENTS(fwenc_offsets)) {
fp_err("could not find encryption byte");
fpi_ssm_mark_aborted(ssm, -ENODEV);
if (img->num_lines >= IMAGE_HEIGHT ||
urudev->img_transfer->actual_length < img->num_lines * IMAGE_WIDTH + 64) {
fp_err("bad captured image (%d lines) or size mismatch %d < %d",
img->num_lines,
urudev->img_transfer->actual_length,
img->num_lines * IMAGE_WIDTH + 64);
fpi_ssm_jump_to_state(ssm, IMAGING_CAPTURE);
return;
}
try_addr = fwenc_offsets[offset];
fp_dbg("looking for encryption byte at %x", try_addr);
r = read_regs(dev, try_addr, 3, fwfixer_read_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
break;
case FWFIXER_WRITE: ;
uint16_t enc_addr = fwenc_offsets[urudev->fwfixer_offset] + 1;
unsigned char cur = urudev->fwfixer_value;
unsigned char new = cur & 0xef;
if (new == cur) {
fp_dbg("encryption is already disabled");
fpi_ssm_next_state(ssm);
} else {
fp_dbg("fixing encryption byte at %x to %02x", enc_addr, new);
sm_write_reg(ssm, enc_addr, new);
if (!urudev->profile->encryption) {
fpi_ssm_jump_to_state(ssm, IMAGING_REPORT_IMAGE);
return;
}
buf[0] = img->key_number;
buf[1] = urudev->img_enc_seed;
buf[2] = urudev->img_enc_seed >> 8;
buf[3] = urudev->img_enc_seed >> 16;
buf[4] = urudev->img_enc_seed >> 24;
sm_write_regs(ssm, REG_SCRAMBLE_DATA_INDEX, 5, buf);
break;
case IMAGING_READ_KEY:
sm_read_regs(ssm, REG_SCRAMBLE_DATA_KEY, 4);
break;
case IMAGING_DECODE:
key = urudev->last_reg_rd[0];
key |= urudev->last_reg_rd[1] << 8;
key |= urudev->last_reg_rd[2] << 16;
key |= urudev->last_reg_rd[3] << 24;
key ^= urudev->img_enc_seed;
fp_dbg("encryption id %02x -> key %08x", img->key_number, key);
while (urudev->img_block < array_n_elements(img->block_info) &&
urudev->img_lines_done < img->num_lines) {
flags = img->block_info[urudev->img_block].flags;
num_lines = img->block_info[urudev->img_block].num_lines;
if (num_lines == 0)
break;
fp_dbg("%d %02x %d", urudev->img_block, flags, num_lines);
if (flags & BLOCKF_CHANGE_KEY) {
fp_dbg("changing encryption keys.\n");
img->block_info[urudev->img_block].flags &= ~BLOCKF_CHANGE_KEY;
img->key_number++;
urudev->img_enc_seed = rand();
fpi_ssm_jump_to_state(ssm, IMAGING_SEND_INDEX);
return;
}
switch (flags & (BLOCKF_NO_KEY_UPDATE | BLOCKF_ENCRYPTED)) {
case BLOCKF_ENCRYPTED:
fp_dbg("decoding %d lines", num_lines);
key = do_decode(&img->data[urudev->img_lines_done][0],
IMAGE_WIDTH*num_lines, key);
break;
case 0:
fp_dbg("skipping %d lines", num_lines);
for (r = 0; r < IMAGE_WIDTH*num_lines; r++)
key = update_key(key);
break;
}
if ((flags & BLOCKF_NOT_PRESENT) == 0)
urudev->img_lines_done += num_lines;
urudev->img_block++;
}
fpi_ssm_next_state(ssm);
break;
case IMAGING_REPORT_IMAGE:
fpimg = fpi_img_new_for_imgdev(dev);
to = r = 0;
for (i = 0; i < array_n_elements(img->block_info) && r < img->num_lines; i++) {
flags = img->block_info[i].flags;
num_lines = img->block_info[i].num_lines;
if (num_lines == 0)
break;
memcpy(&fpimg->data[to], &img->data[r][0],
num_lines * IMAGE_WIDTH);
if (!(flags & BLOCKF_NOT_PRESENT))
r += num_lines;
to += num_lines * IMAGE_WIDTH;
}
fpimg->flags = FP_IMG_COLORS_INVERTED;
if (!urudev->profile->encryption)
fpimg->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
fpi_imgdev_image_captured(dev, fpimg);
if (urudev->activate_state == IMGDEV_STATE_CAPTURE)
fpi_ssm_jump_to_state(ssm, IMAGING_CAPTURE);
else
fpi_ssm_mark_completed(ssm);
break;
}
}
static void imaging_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
int r = ssm->error;
fpi_ssm_free(ssm);
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);
}
/***** INITIALIZATION *****/
/* After closing an app and setting hwstat to 0x80, my ms keyboard gets in a
* confused state and returns hwstat 0x85. On next app run, we don't get the
* 56aa interrupt. This is the best way I've found to fix it: mess around
@@ -793,7 +857,7 @@ static void rebootpwr_run_state(struct fpi_ssm *ssm)
sm_read_reg(ssm, REG_HWSTAT);
break;
case REBOOTPWR_CHECK_HWSTAT:
urudev->last_hwstat = urudev->last_reg_rd;
urudev->last_hwstat = urudev->last_reg_rd[0];
if (urudev->last_hwstat & 0x1)
fpi_ssm_mark_completed(ssm);
else
@@ -876,8 +940,8 @@ static void powerup_run_state(struct fpi_ssm *ssm)
sm_read_reg(ssm, REG_HWSTAT);
break;
case POWERUP_CHECK_HWSTAT:
urudev->last_hwstat = urudev->last_reg_rd;
if ((urudev->last_reg_rd & 0x80) == 0)
urudev->last_hwstat = urudev->last_reg_rd[0];
if ((urudev->last_reg_rd[0] & 0x80) == 0)
fpi_ssm_mark_completed(ssm);
else
fpi_ssm_next_state(ssm);
@@ -890,7 +954,7 @@ static void powerup_run_state(struct fpi_ssm *ssm)
sm_do_challenge_response(ssm);
break;
case POWERUP_CHALLENGE_RESPONSE_SUCCESS:
fpi_ssm_jump_to_state(ssm, POWERUP_SET_HWSTAT);
fpi_ssm_jump_to_state(ssm, POWERUP_SET_HWSTAT);
break;
}
}
@@ -908,12 +972,6 @@ static void powerup_run_state(struct fpi_ssm *ssm)
if ((status & 0x80) == 0)
set_hwstat(status | 0x80);
// disable encryption
fwenc = read_firmware_encryption_byte();
new = fwenc & 0xef;
if (new != fwenc)
write_firmware_encryption_byte(new);
// power device up
run_powerup_sm();
await_irq(IRQDATA_SCANPWR_ON);
@@ -924,10 +982,11 @@ enum init_states {
INIT_CHECK_HWSTAT_REBOOT,
INIT_REBOOT_POWER,
INIT_CHECK_HWSTAT_POWERDOWN,
INIT_FIX_FIRMWARE,
INIT_POWERUP,
INIT_AWAIT_SCAN_POWER,
INIT_DONE,
INIT_GET_VERSION,
INIT_REPORT_VERSION,
INIT_NUM_STATES,
};
@@ -975,7 +1034,7 @@ static void init_run_state(struct fpi_ssm *ssm)
sm_read_reg(ssm, REG_HWSTAT);
break;
case INIT_CHECK_HWSTAT_REBOOT:
urudev->last_hwstat = urudev->last_reg_rd;
urudev->last_hwstat = urudev->last_reg_rd[0];
if ((urudev->last_hwstat & 0x84) == 0x84)
fpi_ssm_next_state(ssm);
else
@@ -993,12 +1052,6 @@ static void init_run_state(struct fpi_ssm *ssm)
else
fpi_ssm_next_state(ssm);
break;
case INIT_FIX_FIRMWARE: ;
struct fpi_ssm *fwsm = fpi_ssm_new(dev->dev, fwfixer_run_state,
FWFIXER_NUM_STATES);
fwsm->priv = dev;
fpi_ssm_start_subsm(ssm, fwsm);
break;
case INIT_POWERUP: ;
struct fpi_ssm *powerupsm = fpi_ssm_new(dev->dev, powerup_run_state,
POWERUP_NUM_STATES);
@@ -1029,6 +1082,17 @@ static void init_run_state(struct fpi_ssm *ssm)
urudev->scanpwr_irq_timeout = NULL;
urudev->irq_cb_data = NULL;
urudev->irq_cb = NULL;
fpi_ssm_next_state(ssm);
break;
case INIT_GET_VERSION:
sm_read_regs(ssm, REG_DEVICE_INFO, 16);
break;
case INIT_REPORT_VERSION:
/* Likely hardware revision, and firmware version.
* Not sure which is which. */
fp_info("Versions %02x%02x and %02x%02x",
urudev->last_reg_rd[10], urudev->last_reg_rd[11],
urudev->last_reg_rd[4], urudev->last_reg_rd[5]);
fpi_ssm_mark_completed(ssm);
break;
}
@@ -1037,7 +1101,6 @@ static void init_run_state(struct fpi_ssm *ssm)
static void activate_initsm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
int r = ssm->error;
fpi_ssm_free(ssm);
@@ -1046,7 +1109,7 @@ static void activate_initsm_complete(struct fpi_ssm *ssm)
return;
}
r = dev_change_state(dev, urudev->activate_state);
r = execute_state_change(dev);
fpi_imgdev_activate_complete(dev, r);
}
@@ -1074,47 +1137,69 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
/***** DEINITIALIZATION *****/
enum deinit_states {
DEINIT_SET_MODE_INIT = 0,
DEINIT_POWERDOWN,
DEINIT_NUM_STATES,
};
static void deinit_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case DEINIT_SET_MODE_INIT:
sm_set_mode(ssm, MODE_INIT);
break;
case DEINIT_POWERDOWN:
sm_set_hwstat(ssm, 0x80);
break;
}
}
static void deactivate_irqs_stopped(struct fp_img_dev *dev)
{
fpi_imgdev_deactivate_complete(dev);
}
static void deactivate_deinitsm_complete(struct fpi_ssm *ssm)
static void deactivate_write_reg_cb(struct fp_img_dev *dev, int status,
void *user_data)
{
struct fp_img_dev *dev = ssm->priv;
fpi_ssm_free(ssm);
stop_irq_handler(dev, deactivate_irqs_stopped);
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, deinit_run_state,
DEINIT_NUM_STATES);
dev_change_state(dev, IMGDEV_STATE_INACTIVE);
}
stop_imaging_loop(dev);
urudev->irq_cb = NULL;
urudev->irq_cb_data = NULL;
ssm->priv = dev;
fpi_ssm_start(ssm, deactivate_deinitsm_complete);
static int execute_state_change(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct fpi_ssm *ssm;
switch (urudev->activate_state) {
case IMGDEV_STATE_INACTIVE:
fp_dbg("deactivating");
urudev->irq_cb = NULL;
urudev->irq_cb_data = NULL;
return write_reg(dev, REG_MODE, MODE_OFF,
deactivate_write_reg_cb, NULL);
break;
case IMGDEV_STATE_AWAIT_FINGER_ON:
fp_dbg("wait finger on");
if (!IRQ_HANDLER_IS_RUNNING(urudev))
return -EIO;
urudev->irq_cb = finger_presence_irq_cb;
return write_reg(dev, REG_MODE, MODE_AWAIT_FINGER_ON,
change_state_write_reg_cb, NULL);
case IMGDEV_STATE_CAPTURE:
fp_dbg("starting capture");
urudev->irq_cb = NULL;
urudev->img_transfer = libusb_alloc_transfer(0);
urudev->img_data = g_malloc(sizeof(struct uru4k_image));
urudev->img_enc_seed = rand();
ssm = fpi_ssm_new(dev->dev, imaging_run_state, IMAGING_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, imaging_complete);
return write_reg(dev, REG_MODE, MODE_CAPTURE,
change_state_write_reg_cb, NULL);
case IMGDEV_STATE_AWAIT_FINGER_OFF:
fp_dbg("await finger off");
if (!IRQ_HANDLER_IS_RUNNING(urudev))
return -EIO;
urudev->irq_cb = finger_presence_irq_cb;
return write_reg(dev, REG_MODE, MODE_AWAIT_FINGER_OFF,
change_state_write_reg_cb, NULL);
}
return 0;
}
/***** LIBRARY STUFF *****/
@@ -1276,15 +1361,15 @@ static const struct usb_id id_table[] = {
struct fp_img_driver uru4000_driver = {
.driver = {
.id = 2,
.id = URU4000_ID,
.name = FP_COMPONENT,
.full_name = "Digital Persona U.are.U 4000/4000B",
.full_name = "Digital Persona U.are.U 4000/4000B/4500",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
.img_height = 289,
.img_width = 384,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.open = dev_init,
.close = dev_deinit,
+3 -1
View File
@@ -34,6 +34,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
@@ -368,7 +370,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver vcom5s_driver = {
.driver = {
.id = 8,
.id = VCOM5S_ID,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.id_table = id_table,
+3 -1
View File
@@ -25,6 +25,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
/* Input-Output usb endpoint */
#define EP_IN(n) (n | LIBUSB_ENDPOINT_IN)
#define EP_OUT(n) (n | LIBUSB_ENDPOINT_OUT)
@@ -1549,7 +1551,7 @@ struct fp_img_driver vfs101_driver =
/* Driver specification */
.driver =
{
.id = 10,
.id = VFS101_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS101",
.id_table = id_table,
+308
View File
@@ -0,0 +1,308 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 "vfs301"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include <unistd.h>
#include <fp_internal.h>
#include "driver_ids.h"
/************************** GENERIC STUFF *************************************/
/* Callback of asynchronous sleep */
static void async_sleep_cb(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_timeout *timeout;
/* Add timeout */
timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_mark_aborted(ssm, -ETIME);
}
}
static int submit_image(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
int height;
struct fp_img *img;
#if 0
/* XXX: This is probably handled by libfprint automagically? */
if (vdev->scanline_count < 20) {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
return 0;
}
#endif
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
if (img == NULL)
return 0;
vfs301_extract_image(vdev, img->data, &height);
/* TODO: how to detect flip? should the resulting image be
* oriented so that it is equal e.g. to a fingerprint on a paper,
* or to the finger when I look at it?) */
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
img->width = VFS301_FP_OUTPUT_WIDTH;
img->height = height;
img = fpi_img_resize(img, img->height * img->width);
fpi_imgdev_image_captured(dev, img);
return 1;
}
/* Loop ssm states */
enum
{
/* Step 0 - Scan finger */
M_REQUEST_PRINT,
M_WAIT_PRINT,
M_CHECK_PRINT,
M_READ_PRINT_START,
M_READ_PRINT_WAIT,
M_READ_PRINT_POLL,
M_SUBMIT_PRINT,
/* Number of states */
M_LOOP_NUM_STATES,
};
/* Exec loop sequential state machine */
static void m_loop_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
switch (ssm->cur_state) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_WAIT_PRINT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(dev->udev, vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_WAIT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(dev->udev, vdev);
assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state(ssm);
}
break;
case M_SUBMIT_PRINT:
if (submit_image(ssm)) {
fpi_ssm_mark_completed(ssm);
/* NOTE: finger off is expected only after submitting image... */
fpi_imgdev_report_finger_status(dev, FALSE);
} else {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
}
break;
}
}
/* Complete loop sequential state machine */
static void m_loop_complete(struct fpi_ssm *ssm)
{
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Exec init sequential state machine */
static void m_init_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
assert(ssm->cur_state == 0);
vfs301_proto_init(dev->udev, vdev);
fpi_ssm_mark_completed(ssm);
}
/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_ssm *ssm_loop;
if (!ssm->error) {
/* Notify activate complete */
fpi_imgdev_activate_complete(dev, 0);
/* Start loop ssm */
ssm_loop = fpi_ssm_new(dev->dev, m_loop_state, M_LOOP_NUM_STATES);
ssm_loop->priv = dev;
fpi_ssm_start(ssm_loop, m_loop_complete);
}
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm;
/* Start init ssm */
ssm = fpi_ssm_new(dev->dev, m_init_state, 1);
ssm->priv = dev;
fpi_ssm_start(ssm, m_init_complete);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
fpi_imgdev_deactivate_complete(dev);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
vfs301_dev_t *vdev = NULL;
int r;
/* Claim usb interface */
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
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;
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
/* Release private structure */
free(((vfs301_dev_t*)dev->priv)->scanline_buf);
g_free(dev->priv);
/* Release usb interface */
libusb_release_interface(dev->udev, 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);
}
/* Usb id table of device */
static const struct usb_id id_table[] =
{
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
{ 0, 0, 0, },
};
/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
/* Driver specification */
.driver =
{
.id = VFS301_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS301_FP_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};
+639
View File
@@ -0,0 +1,639 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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
*/
/*
* TODO:
* - async communication everywhere :)
* - protocol decyphering
* - what is needed and what is redundant
* - is some part of the initial data the firmware?
* - describe some interesting structures better
*/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include "vfs301_proto_fragments.h"
#include <unistd.h>
#define min(a, b) (((a) < (b)) ? (a) : (b))
/************************** USB STUFF *****************************************/
#ifdef DEBUG
static void usb_print_packet(int dir, int rv, const unsigned char *data, int length)
{
fprintf(stderr, "%s, rv %d, len %d\n", dir ? "send" : "recv", rv, length);
#ifdef PRINT_VERBOSE
int i;
for (i = 0; i < min(length, 128); i++) {
fprintf(stderr, "%.2X ", data[i]);
if (i % 8 == 7)
fprintf(stderr, " ");
if (i % 32 == 31)
fprintf(stderr, "\n");
}
#endif
fprintf(stderr, "\n");
}
#endif
static int usb_recv(
vfs301_dev_t *dev,
struct libusb_device_handle *devh, unsigned char endpoint, int max_bytes)
{
assert(max_bytes <= sizeof(dev->recv_buf));
int r = libusb_bulk_transfer(
devh, endpoint,
dev->recv_buf, max_bytes,
&dev->recv_len, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(0, r, dev->recv_buf, dev->recv_len);
#endif
if (r < 0)
return r;
return 0;
}
static int usb_send(
struct libusb_device_handle *devh, const unsigned char *data, int length)
{
int transferred = 0;
int r = libusb_bulk_transfer(
devh, VFS301_SEND_ENDPOINT,
(unsigned char *)data, length, &transferred, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(1, r, data, length);
#endif
assert(r == 0);
if (r < 0)
return r;
if (transferred < length)
return r;
return 0;
}
/************************** OUT MESSAGES GENERATION ***************************/
static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
{
*data = 0x0B;
*len = 1;
data++;
memset(data, 0, 39);
*len += 38;
data[20] = subtype;
switch (subtype) {
case 0x04:
data[34] = 0x9F;
break;
case 0x05:
data[34] = 0xAB;
len++;
break;
default:
assert(!"unsupported");
break;
}
}
#define HEX_TO_INT(c) \
(((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'A' + 10))
static void translate_str(const char **srcL, unsigned char *data, int *len)
{
const char *src;
unsigned char *dataOrig = data;
while (*srcL != NULL) {
src = *srcL;
while (*src != '\0') {
assert(*src != '\0');
assert(*(src +1) != '\0');
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
data++;
src += 2;
}
srcL++;
}
*len = data - dataOrig;
}
static void vfs301_proto_generate(int type, int subtype, unsigned char *data, int *len)
{
switch (type) {
case 0x01:
case 0x04:
/* After cmd 0x04 is sent, a data is received on VALIDITY_RECEIVE_ENDPOINT_CTRL.
* If it is 0x0000:
* additional 64B and 224B are read from _DATA, then vfs301_next_scan_FA00 is
* sent, 0000 received from _CTRL, and then continue with wait loop
* If it is 0x1204:
* => reinit?
*/
case 0x17:
case 0x19:
case 0x1A:
*data = type;
*len = 1;
break;
case 0x0B:
vfs301_proto_generate_0B(subtype, data, len);
break;
case 0x02D0:
{
const char **dataLs[] = {
vfs301_02D0_01,
vfs301_02D0_02,
vfs301_02D0_03,
vfs301_02D0_04,
vfs301_02D0_05,
vfs301_02D0_06,
vfs301_02D0_07,
};
assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
translate_str(dataLs[subtype - 1], data, len);
}
break;
case 0x0220:
switch (subtype) {
case 1:
translate_str(vfs301_0220_01, data, len);
break;
case 2:
translate_str(vfs301_0220_02, data, len);
break;
case 3:
translate_str(vfs301_0220_03, data, len);
break;
case 0xFA00:
case 0x2C01:
case 0x5E01:
translate_str(vfs301_next_scan_template, data, len);
unsigned char *field = data + *len - (sizeof(S4_TAIL) - 1) / 2 - 4;
assert(*field == 0xDE);
assert(*(field + 1) == 0xAD);
assert(*(field + 2) == 0xDE);
assert(*(field + 3) == 0xAD);
*field = (unsigned char)((subtype >> 8) & 0xFF);
*(field + 1) = (unsigned char)(subtype & 0xFF);
*(field + 2) = *field;
*(field + 3) = *(field + 1);
break;
default:
assert(0);
break;
}
break;
case 0x06:
assert(!"Not generated");
break;
default:
assert(!"Unknown message type");
break;
}
}
/************************** SCAN IMAGE PROCESSING *****************************/
#ifdef SCAN_FINISH_DETECTION
static int img_is_finished_scan(fp_line_t *lines, int no_lines)
{
int i;
int j;
int rv = 1;
for (i = no_lines - VFS301_FP_SUM_LINES; i < no_lines; i++) {
/* check the line for fingerprint data */
for (j = 0; j < sizeof(lines[i].sum2); j++) {
if (lines[i].sum2[j] > (VFS301_FP_SUM_MEDIAN + VFS301_FP_SUM_EMPTY_RANGE))
rv = 0;
}
}
return rv;
}
#endif
static int scanline_diff(const unsigned char *scanlines, int prev, int cur)
{
const unsigned char *line1 = scanlines + prev * VFS301_FP_OUTPUT_WIDTH;
const unsigned char *line2 = scanlines + cur * VFS301_FP_OUTPUT_WIDTH;
int i;
int diff;
#ifdef OUTPUT_RAW
/* We only need the image, not the surrounding stuff. */
line1 = ((vfs301_line_t*)line1)->scan;
line2 = ((vfs301_line_t*)line2)->scan;
#endif
/* TODO: This doesn't work too well when there are parallel lines in the
* fingerprint. */
for (diff = 0, i = 0; i < VFS301_FP_WIDTH; i++) {
if (*line1 > *line2)
diff += *line1 - *line2;
else
diff += *line2 - *line1;
line1++;
line2++;
}
return ((diff / VFS301_FP_WIDTH) > VFS301_FP_LINE_DIFF_THRESHOLD);
}
/** Transform the input data to a normalized fingerprint scan */
void vfs301_extract_image(
vfs301_dev_t *vfs, unsigned char *output, int *output_height
)
{
const unsigned char *scanlines = vfs->scanline_buf;
int last_line;
int i;
assert(vfs->scanline_count >= 1);
*output_height = 1;
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
last_line = 0;
/* The following algorithm is quite trivial - it just picks lines that
* differ more than VFS301_FP_LINE_DIFF_THRESHOLD.
* TODO: A nicer approach would be to pick those lines and then do some kind
* of bi/tri-linear resampling to get the output (so that we don't get so
* many false edges etc.).
*/
for (i = 1; i < vfs->scanline_count; i++) {
if (scanline_diff(scanlines, last_line, i)) {
memcpy(
output + VFS301_FP_OUTPUT_WIDTH * (*output_height),
scanlines + VFS301_FP_OUTPUT_WIDTH * i,
VFS301_FP_OUTPUT_WIDTH
);
last_line = i;
(*output_height)++;
}
}
}
static int img_process_data(
int first_block, vfs301_dev_t *dev, const unsigned char *buf, int len
)
{
vfs301_line_t *lines = (vfs301_line_t*)buf;
int no_lines = len / sizeof(vfs301_line_t);
int i;
/*int no_nonempty;*/
unsigned char *cur_line;
int last_img_height;
#ifdef SCAN_FINISH_DETECTION
int finished_scan;
#endif
if (first_block) {
last_img_height = 0;
dev->scanline_count = no_lines;
} else {
last_img_height = dev->scanline_count;
dev->scanline_count += no_lines;
}
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
assert(dev->scanline_buf != NULL);
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
i < no_lines;
i++, cur_line += VFS301_FP_OUTPUT_WIDTH
) {
#ifndef OUTPUT_RAW
memcpy(cur_line, lines[i].scan, VFS301_FP_OUTPUT_WIDTH);
#else
memcpy(cur_line, &lines[i], VFS301_FP_OUTPUT_WIDTH);
#endif
}
#ifdef SCAN_FINISH_DETECTION
finished_scan = img_is_finished_scan(lines, no_lines);
return !finished_scan;
#else /* SCAN_FINISH_DETECTION */
return 1; /* Just continue until data is coming */
#endif
}
/************************** PROTOCOL STUFF ************************************/
static unsigned char usb_send_buf[0x2000];
#define USB_RECV(from, len) \
usb_recv(dev, devh, from, len)
#define USB_SEND(type, subtype) \
{ \
int len; \
vfs301_proto_generate(type, subtype, usb_send_buf, &len); \
usb_send(devh, usb_send_buf, len); \
}
#define RAW_DATA(x) x, sizeof(x)
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
{
int i;
const unsigned char *buf = dev->recv_buf;
int len = dev->recv_len;
if (first_block) {
assert(len >= VFS301_FP_FRAME_SIZE);
/* Skip bytes until start_sequence is found */
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--) {
if (IS_VFS301_FP_SEQ_START(buf))
break;
}
}
return img_process_data(first_block, dev, buf, len);
}
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x0220, 0xFA00);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 000000000000 */
}
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
USB_SEND(0x17, -1);
assert(USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7) == 0);
if (memcmp(dev->recv_buf, no_event, sizeof(no_event)) == 0) {
return 0;
} else if (memcmp(dev->recv_buf, got_event, sizeof(no_event)) == 0) {
return 1;
} else {
assert(!"unexpected reply to wait");
}
}
#define VARIABLE_ORDER(a, b) \
{ \
int _rv = a;\
b; \
if (_rv == -7) \
a; \
}
static void vfs301_proto_process_event_cb(struct libusb_transfer *transfer)
{
vfs301_dev_t *dev = transfer->user_data;
struct libusb_device_handle *devh = transfer->dev_handle;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
dev->recv_progress = VFS301_FAILURE;
goto end;
} else if (transfer->actual_length < dev->recv_exp_amt) {
/* TODO: process the data anyway? */
dev->recv_progress = VFS301_ENDED;
goto end;
} else {
dev->recv_len = transfer->actual_length;
if (!vfs301_proto_process_data(dev->recv_exp_amt == VFS301_FP_RECV_LEN_1, dev)) {
dev->recv_progress = VFS301_ENDED;
goto end;
}
dev->recv_exp_amt = VFS301_FP_RECV_LEN_2;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
printf("cb::continue fail\n");
dev->recv_progress = VFS301_FAILURE;
goto end;
}
return;
}
end:
libusb_free_transfer(transfer);
}
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
struct libusb_transfer *transfer;
/*
* Notes:
*
* seen next_scan order:
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o 5E01 !?
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
*/
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 64);
/* now read the fingerprint data, while there are some */
transfer = libusb_alloc_transfer(0);
if (!transfer) {
dev->recv_progress = VFS301_FAILURE;
return;
}
dev->recv_progress = VFS301_ONGOING;
dev->recv_exp_amt = VFS301_FP_RECV_LEN_1;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
libusb_free_transfer(transfer);
dev->recv_progress = VFS301_FAILURE;
return;
}
}
int /* vfs301_dev_t::recv_progress */ vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
if (dev->recv_progress != VFS301_ENDED)
return dev->recv_progress;
/* Finish the scan process... */
USB_SEND(0x04, -1);
/* the following may come in random order, data may not come at all, don't
* try for too long... */
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 1204 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 16384)
);
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760), /* seems to always come */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2) /* 0000 */
);
return dev->recv_progress;
}
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0B, 0x04);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 6); /* 000000000000 */
USB_SEND(0x0B, 0x05);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7); /* 00000000000000 */
USB_SEND(0x19, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 64);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 4); /* 6BB4D0BC */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 256);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 32);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_3));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x02D0, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 11648); /* 56 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 2);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 53248); /* 2 * 128 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 19968); /* 96 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 4);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5824); /* 28 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 5);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 6);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 7);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 832);
usb_send(devh, RAW_DATA(vfs301_12));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760)
);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_4));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_24)); /* turns on white */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0220, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2368);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 36);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760);
}
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
}
+137
View File
@@ -0,0 +1,137 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 <libusb-1.0/libusb.h>
enum {
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
VFS301_SEND_ENDPOINT = 0x01,
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
};
#define VFS301_FP_RECV_LEN_1 (84032)
#define VFS301_FP_RECV_LEN_2 (84096)
typedef struct {
/* buffer for received data */
unsigned char recv_buf[0x20000];
int recv_len;
/* buffer to hold raw scanlines */
unsigned char *scanline_buf;
int scanline_count;
enum {
VFS301_ONGOING = 0,
VFS301_ENDED = 1,
VFS301_FAILURE = -1
} recv_progress;
int recv_exp_amt;
} vfs301_dev_t;
enum {
/* Width of the scanned data in px */
VFS301_FP_WIDTH = 200,
/* sizeof(fp_line_t) */
VFS301_FP_FRAME_SIZE = 288,
/* Width of output line */
#ifndef OUTPUT_RAW
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
#else
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
#endif
VFS301_FP_SUM_LINES = 3,
#ifdef SCAN_FINISH_DETECTION
/* TODO: The following changes (seen ~60 and ~80) In that
* case we'll need to calibrate this from empty data somehow... */
VFS301_FP_SUM_MEDIAN = 60,
VFS301_FP_SUM_EMPTY_RANGE = 5,
#endif
/* Minimum average difference between returned lines */
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
/* Maximum waiting time for a single fingerprint frame */
VFS301_FP_RECV_TIMEOUT = 2000
};
/* Arrays of this structure is returned during the initialization as a response
* to the 0x02D0 messages.
* It seems to be always the same - what is it for? Some kind of confirmation?
*/
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi; /* FIXME ? */
unsigned char flags[3];
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
} vfs301_init_line_t;
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi;
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
unsigned char flag_1;
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
/* A offseted, stretched, inverted copy of scan... probably could
* serve finger motion speed detection?
* Seems to be subdivided to some 10B + 53B + 1B blocks */
unsigned char mirror[64];
/* Some kind of sum of the scan, very low contrast */
unsigned char sum1[2];
unsigned char sum2[11];
unsigned char sum3[3];
} vfs301_line_t;
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
/** returns 0 if no event is ready, or 1 if there is one... */
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
int vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);
File diff suppressed because it is too large Load Diff
+26 -12
View File
@@ -28,15 +28,17 @@
#include <fprint.h>
#define array_n_elements(array) (sizeof(array) / sizeof(array[0]))
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
enum fpi_log_level {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
FPRINT_LOG_LEVEL_DEBUG,
FPRINT_LOG_LEVEL_INFO,
FPRINT_LOG_LEVEL_WARNING,
FPRINT_LOG_LEVEL_ERROR,
};
void fpi_log(enum fpi_log_level, const char *component, const char *function,
@@ -53,14 +55,14 @@ void fpi_log(enum fpi_log_level, const char *component, const char *function,
#endif
#ifdef ENABLE_DEBUG_LOGGING
#define fp_dbg(fmt...) _fpi_log(LOG_LEVEL_DEBUG, fmt)
#define fp_dbg(fmt...) _fpi_log(FPRINT_LOG_LEVEL_DEBUG, fmt)
#else
#define fp_dbg(fmt...)
#endif
#define fp_info(fmt...) _fpi_log(LOG_LEVEL_INFO, fmt)
#define fp_warn(fmt...) _fpi_log(LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(LOG_LEVEL_ERROR, fmt)
#define fp_info(fmt...) _fpi_log(FPRINT_LOG_LEVEL_INFO, fmt)
#define fp_warn(fmt...) _fpi_log(FPRINT_LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(FPRINT_LOG_LEVEL_ERROR, fmt)
#ifndef NDEBUG
#define BUG_ON(condition) \
@@ -158,7 +160,7 @@ enum fp_imgdev_enroll_state {
enum fp_imgdev_verify_state {
IMG_VERIFY_STATE_NONE = 0,
IMG_VERIFY_STATE_ACTIVATING
IMG_VERIFY_STATE_ACTIVATING
};
struct fp_img_dev {
@@ -253,9 +255,18 @@ extern struct fp_img_driver uru4000_driver;
#ifdef ENABLE_AES1610
extern struct fp_img_driver aes1610_driver;
#endif
#ifdef ENABLE_AES1660
extern struct fp_img_driver aes1660_driver;
#endif
#ifdef ENABLE_AES2501
extern struct fp_img_driver aes2501_driver;
#endif
#ifdef ENABLE_AES2550
extern struct fp_img_driver aes2550_driver;
#endif
#ifdef ENABLE_AES2660
extern struct fp_img_driver aes2660_driver;
#endif
#ifdef ENABLE_AES4000
extern struct fp_img_driver aes4000_driver;
#endif
@@ -268,6 +279,9 @@ extern struct fp_img_driver vcom5s_driver;
#ifdef ENABLE_VFS101
extern struct fp_img_driver vfs101_driver;
#endif
#ifdef ENABLE_VFS301
extern struct fp_img_driver vfs301_driver;
#endif
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
@@ -325,8 +339,8 @@ struct fp_minutiae {
};
/* bit values for fp_img.flags */
#define FP_IMG_V_FLIPPED (1<<0)
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_V_FLIPPED (1<<0)
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
@@ -354,7 +368,7 @@ int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print);
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 fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor);
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor);
/* polling and timeouts */
+11 -1
View File
@@ -35,13 +35,16 @@ static const struct usb_id blacklist_id_table[] = {
struct fp_driver whitelist = {
.id_table = whitelist_id_table,
.full_name = "Hardcoded whitelist"
};
GHashTable *printed = NULL;
static void print_driver (struct fp_driver *driver)
{
int i, j, blacklist;
int i, j, blacklist, num_printed;
num_printed = 0;
for (i = 0; driver->id_table[i].vendor != 0; i++) {
char *key;
@@ -66,8 +69,15 @@ static void print_driver (struct fp_driver *driver)
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
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);
num_printed++;
}
if (num_printed > 0)
printf ("\n");
}
int main (int argc, char **argv)
+3 -3
View File
@@ -22,10 +22,10 @@
#include "fp_internal.h"
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor)
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
{
int new_width = img->width * factor;
int new_height = img->height * factor;
int new_width = img->width * w_factor;
int new_height = img->height * h_factor;
GdkPixbuf *orig, *resized;
struct fp_img *newimg;
guchar *pixels;
+3 -3
View File
@@ -22,14 +22,14 @@
#include "fp_internal.h"
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor)
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 * factor;
int new_height = img->height * factor;
int new_width = img->width * w_factor;
int new_height = img->height * h_factor;
struct fp_img *newimg;
/* It is possible to implement resizing using a simple algorithm, however
+1 -1
View File
@@ -290,7 +290,7 @@ int fpi_img_detect_minutiae(struct fp_img *img)
&low_contrast_map, &low_flow_map, &high_curve_map,
&map_w, &map_h, &bdata, &bw, &bh, &bd,
img->data, img->width, img->height, 8,
DEFAULT_PPI / (double)25.4, &lfsparms_V2);
DEFAULT_PPI / (double)25.4, &g_lfsparms_V2);
g_timer_stop(timer);
fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
+7 -7
View File
@@ -1021,12 +1021,12 @@ extern int closest_dir_dist(const int, const int, const int);
/*************************************************************************/
/* EXTERNAL GLOBAL VARIABLE DEFINITIONS */
/*************************************************************************/
extern double dft_coefs[];
extern LFSPARMS lfsparms;
extern LFSPARMS lfsparms_V2;
extern int nbr8_dx[];
extern int nbr8_dy[];
extern int chaincodes_nbr8[];
extern FEATURE_PATTERN feature_patterns[];
extern double g_dft_coefs[];
extern LFSPARMS g_lfsparms;
extern LFSPARMS g_lfsparms_V2;
extern int g_nbr8_dx[];
extern int g_nbr8_dy[];
extern int g_chaincodes_nbr8[];
extern FEATURE_PATTERN g_feature_patterns[];
#endif
+4 -4
View File
@@ -36,10 +36,10 @@ identified are necessarily the best available for the purpose.
#define LOG_FILE "log.txt"
#endif
extern FILE *logfp;
extern int avrdir;
extern float dir_strength;
extern int nvalid;
extern FILE *g_logfp;
extern int g_avrdir;
extern float g_dir_strength;
extern int g_nvalid;
extern int open_logfile(void);
extern int close_logfile(void);
+1 -2
View File
@@ -86,7 +86,7 @@ int block_offsets(int **optr, int *ow, int *oh,
int *blkoffs, bx, by, bw, bh, bi, bsize;
int blkrow_start, blkrow_size, offset;
int lastbw, lastbh;
int pad2, pw, ph;
int pad2, pw;
/* Test if unpadded image is smaller than a single block */
if((iw < blocksize) || (ih < blocksize)){
@@ -99,7 +99,6 @@ int block_offsets(int **optr, int *ow, int *oh,
/* Compute padded width and height of image */
pad2 = pad<<1;
pw = iw + pad2;
ph = ih + pad2;
/* Compute the number of columns and rows of blocks in the image. */
/* Take the ceiling to account for "leftovers" at the right and */
+4 -4
View File
@@ -742,8 +742,8 @@ static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
/* Set current scan pixel to the new neighbor. */
/* REMEMBER: the neighbors are being scanned around the original */
/* feature point. */
cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
cur_nbr_x = cur_x_loc + g_nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + g_nbr8_dy[nbr_i];
/* If new neighbor is not within image boundaries... */
if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
@@ -766,8 +766,8 @@ static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
if(nbr_i % 2){
/* To do this, look ahead one more neighbor pixel. */
ni = next_scan_nbr(nbr_i, scan_clock);
nx = cur_x_loc + nbr8_dx[ni];
ny = cur_y_loc + nbr8_dy[ni];
nx = cur_x_loc + g_nbr8_dx[ni];
ny = cur_y_loc + g_nbr8_dy[ni];
/* If new neighbor is not within image boundaries... */
if((nx < 0) || (nx >= iw) ||
(ny < 0) || (ny >= ih))
+7 -7
View File
@@ -122,7 +122,7 @@ static int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
/* Initialize wave form lookup tables for DFT analyses. */
/* used for direction binarization. */
if((ret = init_dftwaves(&dftwaves, dft_coefs, lfsparms->num_dft_waves,
if((ret = init_dftwaves(&dftwaves, g_dft_coefs, lfsparms->num_dft_waves,
lfsparms->windowsize))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
@@ -385,12 +385,12 @@ int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
const int id, const double ppmm, const LFSPARMS *lfsparms)
{
int ret;
MINUTIAE *minutiae;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh;
MINUTIAE *minutiae = NULL;
int *direction_map = NULL, *low_contrast_map = NULL, *low_flow_map = NULL;
int *high_curve_map = NULL, *quality_map = NULL;
int map_w = 0, map_h = 0;
unsigned char *bdata = NULL;
int bw = 0, bh = 0;
/* If input image is not 8-bit grayscale ... */
if(id != 8){
+8 -8
View File
@@ -40,7 +40,7 @@ identified are necessarily the best available for the purpose.
/*************************************************************************/
#ifdef LOG_REPORT
FILE *logfp;
FILE *g_logfp;
#endif
/* Constants (C) for defining 4 DFT frequencies, where */
@@ -50,10 +50,10 @@ FILE *logfp;
/* 2 = twice the frequency in range X. */
/* 3 = three times the frequency in reange X. */
/* 4 = four times the frequency in ranage X. */
double dft_coefs[NUM_DFT_WAVES] = { 1,2,3,4 };
double g_dft_coefs[NUM_DFT_WAVES] = { 1,2,3,4 };
/* Allocate and initialize a global LFS parameters structure. */
LFSPARMS lfsparms = {
LFSPARMS g_lfsparms = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
@@ -137,7 +137,7 @@ LFSPARMS lfsparms = {
/* Allocate and initialize VERSION 2 global LFS parameters structure. */
LFSPARMS lfsparms_V2 = {
LFSPARMS g_lfsparms_V2 = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
@@ -221,17 +221,17 @@ LFSPARMS lfsparms_V2 = {
/* Variables for conducting 8-connected neighbor analyses. */
/* Pixel neighbor offsets: 0 1 2 3 4 5 6 7 */ /* 7 0 1 */
int nbr8_dx[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; /* 6 C 2 */
int nbr8_dy[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; /* 5 4 3 */
int g_nbr8_dx[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; /* 6 C 2 */
int g_nbr8_dy[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; /* 5 4 3 */
/* The chain code lookup matrix for 8-connected neighbors. */
/* Should put this in globals. */
int chaincodes_nbr8[]={ 3, 2, 1,
int g_chaincodes_nbr8[]={ 3, 2, 1,
4,-1, 0,
5, 6, 7};
/* Global array of feature pixel pairs. */
FEATURE_PATTERN feature_patterns[]=
FEATURE_PATTERN g_feature_patterns[]=
{{RIDGE_ENDING, /* a. Ridge Ending (appearing) */
APPEARING,
{0,0},
+2 -10
View File
@@ -110,14 +110,14 @@ static int chain_code_loop(int **ochain, int *onchain,
/* Derive chain code index from neighbor deltas. */
/* The deltas are on the range [-1..1], so to use them as indices */
/* into the code list, they must first be incremented by one. */
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
chain[i] = *(g_chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
}
/* Now derive chain code between last and first points in the */
/* contour list. */
dx = contour_x[0] - contour_x[i];
dy = contour_y[0] - contour_y[i];
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
chain[i] = *(g_chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
/* Store results to the output pointers. */
*ochain = chain;
@@ -782,7 +782,6 @@ int process_loop(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int halfway;
int idir, type, appearing;
double min_dist, max_dist;
int min_fr, max_fr, min_to, max_to;
@@ -800,9 +799,6 @@ int process_loop(MINUTIAE *minutiae,
/* Get pixel value of feature's interior. */
feature_pix = *(bdata + (contour_y[0] * iw) + contour_x[0]);
/* Compute half the perimeter of the loop. */
halfway = ncontour>>1;
/* Get the aspect dimensions of the loop in units of */
/* squared distance. */
get_loop_aspect(&min_fr, &min_to, &min_dist,
@@ -940,7 +936,6 @@ int process_loop_V2(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
int *plow_flow_map, const LFSPARMS *lfsparms)
{
int halfway;
int idir, type, appearing;
double min_dist, max_dist;
int min_fr, max_fr, min_to, max_to;
@@ -960,9 +955,6 @@ int process_loop_V2(MINUTIAE *minutiae,
/* Get pixel value of feature's interior. */
feature_pix = *(bdata + (contour_y[0] * iw) + contour_x[0]);
/* Compute half the perimeter of the loop. */
halfway = ncontour>>1;
/* Get the aspect dimensions of the loop in units of */
/* squared distance. */
get_loop_aspect(&min_fr, &min_to, &min_dist,
+4
View File
@@ -307,6 +307,10 @@ int gen_initial_maps(int **odmap, int **olcmap, int **olfmap,
xmaxlimit = pw - dftgrids->pad - lfsparms->windowsize - 1;
ymaxlimit = ph - dftgrids->pad - lfsparms->windowsize - 1;
/* max limits should not be negative */
xmaxlimit = MAX(xmaxlimit, 0);
ymaxlimit = MAX(ymaxlimit, 0);
/* Foreach block in image ... */
for(bi = 0; bi < bsize; bi++){
/* Adjust block offset from pointing to block origin to pointing */
+6 -6
View File
@@ -69,8 +69,8 @@ int match_1st_pair(unsigned char p1, unsigned char p2,
/* Foreach set of feature pairs ... */
for(i = 0; i < NFEATURES; i++){
/* If current scan pair matches first pair for feature ... */
if((p1==feature_patterns[i].first[0]) &&
(p2==feature_patterns[i].first[1])){
if((p1==g_feature_patterns[i].first[0]) &&
(p2==g_feature_patterns[i].first[1])){
/* Store feature as a possible match. */
possible[*nposs] = i;
/* Bump number of stored possibilities. */
@@ -117,8 +117,8 @@ int match_2nd_pair(unsigned char p1, unsigned char p2,
/* Foreach possible match based on first pair ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches second pair for feature ... */
if((p1==feature_patterns[possible[i]].second[0]) &&
(p2==feature_patterns[possible[i]].second[1])){
if((p1==g_feature_patterns[possible[i]].second[0]) &&
(p2==g_feature_patterns[possible[i]].second[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */
@@ -160,8 +160,8 @@ int match_3rd_pair(unsigned char p1, unsigned char p2,
/* Foreach possible match based on first and second pairs ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches third pair for feature ... */
if((p1==feature_patterns[possible[i]].third[0]) &&
(p2==feature_patterns[possible[i]].third[1])){
if((p1==g_feature_patterns[possible[i]].third[0]) &&
(p2==g_feature_patterns[possible[i]].third[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */
+16 -16
View File
@@ -2521,7 +2521,7 @@ int process_horizontal_scan_minutia(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set y location to second scan row. */
y_loc = cy+1;
/* Set y location of neighboring edge pixel to the first scan row. */
@@ -2550,15 +2550,15 @@ int process_horizontal_scan_minutia(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current IMAP value. */
idir = get_low_curvature_direction(SCAN_HORIZONTAL,
feature_patterns[feature_id].appearing,
g_feature_patterns[feature_id].appearing,
imapval, lfsparms->num_directions);
}
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
DEFAULT_RELIABILITY,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2627,7 +2627,7 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set y location to second scan row. */
y_loc = cy+1;
/* Set y location of neighboring edge pixel to the first scan row. */
@@ -2665,7 +2665,7 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current block's direction. */
idir = get_low_curvature_direction(SCAN_HORIZONTAL,
feature_patterns[feature_id].appearing, dmapval,
g_feature_patterns[feature_id].appearing, dmapval,
lfsparms->num_directions);
}
@@ -2680,8 +2680,8 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
reliability,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2740,7 +2740,7 @@ int process_vertical_scan_minutia(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set x location to second scan column. */
x_loc = cx+1;
/* Set x location of neighboring edge pixel to the first scan column. */
@@ -2776,15 +2776,15 @@ int process_vertical_scan_minutia(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current IMAP value. */
idir = get_low_curvature_direction(SCAN_VERTICAL,
feature_patterns[feature_id].appearing,
g_feature_patterns[feature_id].appearing,
imapval, lfsparms->num_directions);
}
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
DEFAULT_RELIABILITY,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2845,7 +2845,7 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set x location to second scan column. */
x_loc = cx+1;
/* Set x location of neighboring edge pixel to the first scan column. */
@@ -2890,7 +2890,7 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current block's direction. */
idir = get_low_curvature_direction(SCAN_VERTICAL,
feature_patterns[feature_id].appearing, dmapval,
g_feature_patterns[feature_id].appearing, dmapval,
lfsparms->num_directions);
}
@@ -2905,8 +2905,8 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
reliability,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
+7 -7
View File
@@ -565,7 +565,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
MINUTIA *minutia1, *minutia2;
int i, ret, found;
int *xlist, *ylist, num;
int ridge_count, ridge_start, ridge_end;
int ridge_cnt, ridge_start, ridge_end;
int prevpix, curpix;
minutia1 = minutiae->list[first];
@@ -614,7 +614,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
}
/* Ready to count ridges, so initialize counter to 0. */
ridge_count = 0;
ridge_cnt = 0;
print2log("RIDGE COUNT: %d,%d to %d,%d ", minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
@@ -630,7 +630,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
return(ridge_cnt);
}
/* Otherwise, we found a new ridge start transition, so store */
/* its location (the location of the 1 in 0-to-1 transition). */
@@ -647,7 +647,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
return(ridge_cnt);
}
/* Otherwise, we found a new ridge end transition, so store */
/* its location (the location of the 0 in 1-to-0 transition). */
@@ -680,7 +680,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
if(ret){
/* Then assume we have found a valid ridge crossing and bump */
/* the ridge counter. */
ridge_count++;
ridge_cnt++;
}
/* Otherwise, ignore the current ridge start and end transitions */
@@ -694,7 +694,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return the number of ridges counted. */
return(ridge_count);
return(ridge_cnt);
}
/*************************************************************************
@@ -719,7 +719,7 @@ static int count_minutia_ridges(const int first, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, ret, *nbr_list, *nbr_nridges, nnbrs;
int i, ret, *nbr_list = NULL, *nbr_nridges, nnbrs;
/* Find up to the maximum number of qualifying neighbors. */
if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,
+2 -4
View File
@@ -320,7 +320,6 @@ static void verify_stop_cb(struct fp_dev *dev, void *user_data)
API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
struct fp_print_data *enrolled_print, struct fp_img **img)
{
struct fp_driver *drv = dev->drv;
struct sync_verify_data *vdata;
gboolean stopped = FALSE;
int r;
@@ -335,7 +334,7 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
return -EINVAL;
}
fp_dbg("to be handled by %s", drv->name);
fp_dbg("to be handled by %s", dev->drv->name);
vdata = g_malloc0(sizeof(struct sync_verify_data));
r = fp_async_verify_start(dev, enrolled_print, sync_verify_cb, vdata);
if (r < 0) {
@@ -452,13 +451,12 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset,
struct fp_img **img)
{
struct fp_driver *drv = dev->drv;
gboolean stopped = FALSE;
struct sync_identify_data *idata
= g_malloc0(sizeof(struct sync_identify_data));
int r;
fp_dbg("to be handled by %s", drv->name);
fp_dbg("to be handled by %s", dev->drv->name);
r = fp_async_identify_start(dev, print_gallery, sync_identify_cb, idata);
if (r < 0) {