diff --git a/configure.ac b/configure.ac
index d4536f6..cc758e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_INIT([libfprint], [0.5.1])
-AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news])
+AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news subdir-objects])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
AC_CONFIG_HEADERS([config.h])
@@ -23,11 +23,12 @@ AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
-all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes4000 vfs101 vfs301"
+all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 upektc_img etes603 vfs0050"
require_imaging='no'
require_aeslib='no'
require_aesX660='no'
+require_aes3k='no'
enable_upeke2='no'
enable_upekts='no'
enable_upektc='no'
@@ -40,9 +41,13 @@ enable_aes1660='no'
enable_aes2501='no'
enable_aes2550='no'
enable_aes2660='no'
+enable_aes3500='no'
enable_aes4000='no'
enable_vfs101='no'
enable_vfs301='no'
+enable_upektc_img='no'
+enable_etes603='no'
+enable_vfs0050='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
@@ -55,6 +60,10 @@ fi
for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
case ${driver} in
+ vfs0050)
+ AC_DEFINE([ENABLE_VFS0050], [], [Build Validity 0050 driver])
+ enable_vfs0050="yes"
+ ;;
upekts)
AC_DEFINE([ENABLE_UPEKTS], [], [Build UPEK TouchStrip driver])
enable_upekts="yes"
@@ -111,10 +120,18 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
require_aesX660="yes"
enable_aes2660="yes"
;;
+ aes3500)
+ AC_DEFINE([ENABLE_AES3500], [], [Build AuthenTec AES3500 driver])
+ require_aeslib="yes"
+ require_imaging="yes"
+ require_aes3k="yes"
+ enable_aes3500="yes"
+ ;;
aes4000)
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
require_aeslib="yes"
require_imaging="yes"
+ require_aes3k="yes"
enable_aes4000="yes"
;;
vfs101)
@@ -125,6 +142,14 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
enable_vfs301="yes"
;;
+ upektc_img)
+ AC_DEFINE([ENABLE_UPEKTC_IMG], [], [Build Upek TouchChip Fingerprint Coprocessor driver])
+ enable_upektc_img="yes"
+ ;;
+ etes603)
+ AC_DEFINE([ENABLE_ETES603], [], [Build EgisTec ES603 driver])
+ enable_etes603="yes"
+ ;;
esac
done
@@ -140,11 +165,16 @@ AM_CONDITIONAL([ENABLE_AES1660], [test "$enable_aes1660" = "yes"])
AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
AM_CONDITIONAL([ENABLE_AES2550], [test "$enable_aes2550" = "yes"])
AM_CONDITIONAL([ENABLE_AES2660], [test "$enable_aes2660" = "yes"])
+AM_CONDITIONAL([ENABLE_AES3500], [test "$enable_aes3500" = "yes"])
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
+AM_CONDITIONAL([REQUIRE_AES3K], [test "$require_aes3k" = "yes"])
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
+AM_CONDITIONAL([ENABLE_UPEKTC_IMG], [test "$enable_upektc_img" = "yes"])
+AM_CONDITIONAL([ENABLE_ETES603], [test "$enable_etes603" = "yes"])
+AM_CONDITIONAL([ENABLE_VFS0050], [test "$enable_vfs0050" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
@@ -160,8 +190,7 @@ PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28])
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
-imagemagick_found=no
-gdkpixbuf_found=no
+pixman_found=no
AC_ARG_ENABLE(udev-rules,
AC_HELP_STRING([--enable-udev-rules],[Update the udev rules]),
@@ -189,20 +218,13 @@ AC_MSG_NOTICE([installing udev rules in ${ac_with_udev_rules_dir}])
AC_SUBST([udev_rulesdir],[${ac_with_udev_rules_dir}])
if test "$require_imaging" = "yes"; then
- PKG_CHECK_MODULES(IMAGING, gthread-2.0 gdk-pixbuf-2.0, [gdkpixbuf_found=yes], [gdkpixbuf_found=no])
- if test "$gdkpixbuf_found" != "yes"; then
- PKG_CHECK_MODULES(IMAGING, ImageMagick, [imagemagick_found=yes], [imagemagick_found=no])
+ PKG_CHECK_MODULES(IMAGING, pixman-1, [pixman_found=yes], [pixman_found=no])
+ if test "$pixman_found" != "yes"; then
+ AC_MSG_ERROR([pixman is required for imaging support])
fi
fi
-if test "$require_imaging" = "yes"; then
- if test "$gdkpixbuf_found" != "yes" && test "$imagemagick_found" != "yes"; then
- AC_MSG_ERROR([gdk-pixbuf or ImageMagick is required for imaging support])
- fi
-fi
-
-AM_CONDITIONAL([REQUIRE_GDKPIXBUF], [test "$gdkpixbuf_found" = "yes"])
-AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$imagemagick_found" = "yes"])
+AM_CONDITIONAL([REQUIRE_PIXMAN], [test "$pixman_found" = "yes"])
AC_SUBST(IMAGING_CFLAGS)
AC_SUBST(IMAGING_LIBS)
@@ -272,15 +294,18 @@ AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes
AC_SUBST(AM_CFLAGS)
if test "$require_imaging" = "yes"; then
- if test x$gdkpixbuf_found != no; then
- AC_MSG_NOTICE([** Using gdk-pixbuf for imaging])
- else
- AC_MSG_NOTICE([** Using ImageMagick for imaging])
+ if test x$pixman_found != no; then
+ AC_MSG_NOTICE([** Using pixman for imaging])
fi
else
AC_MSG_NOTICE([ Imaging support disabled])
fi
+if test x$enable_vfs0050 != xno ; then
+ AC_MSG_NOTICE([** vfs0050 driver enabled])
+else
+ AC_MSG_NOTICE([ vfs0050 driver disabled])
+fi
if test x$enable_upekts != xno ; then
AC_MSG_NOTICE([** upekts driver enabled])
else
@@ -341,6 +366,11 @@ if test x$enable_aes2660 != xno ; then
else
AC_MSG_NOTICE([ aes2660 driver disabled])
fi
+if test x$enable_aes3500 != xno ; then
+ AC_MSG_NOTICE([** aes3500 driver enabled])
+else
+ AC_MSG_NOTICE([ aes3500 driver disabled])
+fi
if test x$enable_aes4000 != xno ; then
AC_MSG_NOTICE([** aes4000 driver enabled])
else
@@ -356,6 +386,16 @@ if test x$enable_vfs301 != xno ; then
else
AC_MSG_NOTICE([ vfs301 driver disabled])
fi
+if test x$enable_upektc_img != xno ; then
+ AC_MSG_NOTICE([** upektc_img driver enabled])
+else
+ AC_MSG_NOTICE([ upektc_img driver disabled])
+fi
+if test x$enable_etes603 != xno ; then
+ AC_MSG_NOTICE([** etes603 driver enabled])
+else
+ AC_MSG_NOTICE([ etes603 driver disabled])
+fi
if test x$require_aeslib != xno ; then
AC_MSG_NOTICE([** aeslib helper functions enabled])
else
@@ -366,6 +406,11 @@ if test x$require_aesX660 != xno ; then
else
AC_MSG_NOTICE([ aesX660 common routines disabled])
fi
+if test x$require_aes3k != xno ; then
+ AC_MSG_NOTICE([** aes3k common routines enabled])
+else
+ AC_MSG_NOTICE([ aes3k common routines disabled])
+fi
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT
diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am
index 4dee301..5c8c9bc 100644
--- a/libfprint/Makefile.am
+++ b/libfprint/Makefile.am
@@ -12,11 +12,15 @@ AES1660_SRC = drivers/aes1660.c drivers/aes1660.h
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
AES2550_SRC = drivers/aes2550.c drivers/aes2550.h
AES2660_SRC = drivers/aes2660.c drivers/aes2660.h
+AES3500_SRC = drivers/aes3500.c
AES4000_SRC = drivers/aes4000.c
FDU2000_SRC = drivers/fdu2000.c
VCOM5S_SRC = drivers/vcom5s.c
VFS101_SRC = drivers/vfs101.c
VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h drivers/vfs301_proto_fragments.h
+UPEKTC_IMG_SRC = drivers/upektc_img.c drivers/upektc_img.h
+ETES603_SRC = drivers/etes603.c
+VFS0050_SRC = drivers/vfs0050.c drivers/vfs0050.h
EXTRA_DIST = \
$(UPEKE2_SRC) \
@@ -29,17 +33,22 @@ EXTRA_DIST = \
$(AES2501_SRC) \
$(AES2550_SRC) \
$(AES2660_SRC) \
+ $(AES3500_SRC) \
$(AES4000_SRC) \
$(FDU2000_SRC) \
$(VCOM5S_SRC) \
$(VFS101_SRC) \
$(VFS301_SRC) \
+ $(UPEKTC_IMG_SRC) \
+ $(ETES603_SRC) \
+ $(VFS0050_SRC) \
drivers/aesx660.c \
drivers/aesx660.h \
+ drivers/aes3k.c \
+ drivers/aes3k.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
- imagemagick.c \
- gdkpixbuf.c \
+ pixman.c \
60-fprint-autosuspend.rules
DRIVER_SRC =
@@ -87,7 +96,7 @@ libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
fprint_list_udev_rules_SOURCES = fprint-list-udev-rules.c
-fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
+fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la $(GLIB_LIBS)
udev_rules_DATA = 60-fprint-autosuspend.rules
@@ -145,6 +154,10 @@ if ENABLE_AES2660
DRIVER_SRC += $(AES2660_SRC)
endif
+if ENABLE_AES3500
+DRIVER_SRC += $(AES3500_SRC)
+endif
+
if ENABLE_AES4000
DRIVER_SRC += $(AES4000_SRC)
endif
@@ -157,14 +170,20 @@ if ENABLE_VFS301
DRIVER_SRC += $(VFS301_SRC)
endif
-if REQUIRE_IMAGEMAGICK
-OTHER_SRC += imagemagick.c
-libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
-libfprint_la_LIBADD += $(IMAGING_LIBS)
+if ENABLE_UPEKTC_IMG
+DRIVER_SRC += $(UPEKTC_IMG_SRC)
+endif
+
+if ENABLE_ETES603
+DRIVER_SRC += $(ETES603_SRC)
+endif
+
+if ENABLE_VFS0050
+DRIVER_SRC += $(VFS0050_SRC)
endif
-if REQUIRE_GDKPIXBUF
-OTHER_SRC += gdkpixbuf.c
+if REQUIRE_PIXMAN
+OTHER_SRC += pixman.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
@@ -177,6 +196,10 @@ if REQUIRE_AESX660
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
endif
+if REQUIRE_AES3K
+OTHER_SRC += drivers/aes3k.c drivers/aes3k.h
+endif
+
libfprint_la_SOURCES = \
fp_internal.h \
async.c \
diff --git a/libfprint/async.c b/libfprint/async.c
index 67e3481..128b7e9 100644
--- a/libfprint/async.c
+++ b/libfprint/async.c
@@ -412,3 +412,101 @@ void fpi_drvcb_identify_stopped(struct fp_dev *dev)
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
}
+API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
+ fp_capture_cb callback, void *user_data)
+{
+ struct fp_driver *drv = dev->drv;
+ int r;
+
+ fp_dbg("");
+ if (!drv->capture_start)
+ return -ENOTSUP;
+
+ dev->state = DEV_STATE_CAPTURE_STARTING;
+ dev->capture_cb = callback;
+ dev->capture_cb_data = user_data;
+ dev->unconditional_capture = unconditional;
+
+ r = drv->capture_start(dev);
+ if (r < 0) {
+ dev->capture_cb = NULL;
+ dev->state = DEV_STATE_ERROR;
+ fp_err("failed to start verification, error %d", r);
+ }
+ return r;
+}
+
+/* Drivers call this when capture has started */
+void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
+{
+ fp_dbg("");
+ BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
+ if (status) {
+ if (status > 0) {
+ status = -status;
+ fp_dbg("adjusted to %d", status);
+ }
+ dev->state = DEV_STATE_ERROR;
+ if (dev->capture_cb)
+ dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
+ } else {
+ dev->state = DEV_STATE_CAPTURING;
+ }
+}
+
+/* Drivers call this to report a capture result (which might mark completion) */
+void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
+ struct fp_img *img)
+{
+ fp_dbg("result %d", result);
+ BUG_ON(dev->state != DEV_STATE_CAPTURING);
+ if (result < 0 || result == FP_CAPTURE_COMPLETE)
+ dev->state = DEV_STATE_CAPTURE_DONE;
+
+ if (dev->capture_cb)
+ dev->capture_cb(dev, result, img, dev->capture_cb_data);
+ else
+ fp_dbg("ignoring capture result as no callback is subscribed");
+}
+
+/* Drivers call this when capture has stopped */
+void fpi_drvcb_capture_stopped(struct fp_dev *dev)
+{
+ fp_dbg("");
+ BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
+ dev->state = DEV_STATE_INITIALIZED;
+ if (dev->capture_stop_cb)
+ dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
+}
+
+API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
+ fp_capture_stop_cb callback, void *user_data)
+{
+ struct fp_driver *drv = dev->drv;
+ int r;
+
+ fp_dbg("");
+ BUG_ON(dev->state != DEV_STATE_ERROR
+ && dev->state != DEV_STATE_CAPTURING
+ && dev->state != DEV_STATE_CAPTURE_DONE);
+
+ dev->capture_cb = NULL;
+ dev->capture_stop_cb = callback;
+ dev->capture_stop_cb_data = user_data;
+ dev->state = DEV_STATE_CAPTURE_STOPPING;
+
+ if (!drv->capture_start)
+ return -ENOTSUP;
+ if (!drv->capture_stop) {
+ dev->state = DEV_STATE_INITIALIZED;
+ fpi_drvcb_capture_stopped(dev);
+ return 0;
+ }
+
+ r = drv->capture_stop(dev);
+ if (r < 0) {
+ fp_err("failed to stop verification");
+ dev->capture_stop_cb = NULL;
+ }
+ return r;
+}
diff --git a/libfprint/core.c b/libfprint/core.c
index 5315fdc..4cf00ec 100644
--- a/libfprint/core.c
+++ b/libfprint/core.c
@@ -349,6 +349,9 @@ static struct fp_driver * const primitive_drivers[] = {
};
static struct fp_img_driver * const img_drivers[] = {
+#ifdef ENABLE_AES3500
+ &aes3500_driver,
+#endif
#ifdef ENABLE_AES4000
&aes4000_driver,
#endif
@@ -386,6 +389,15 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_UPEKTC
&upektc_driver,
#endif
+#ifdef ENABLE_UPEKTC_IMG
+ &upektc_img_driver,
+#endif
+#ifdef ENABLE_ETES603
+ &etes603_driver,
+#endif
+#ifdef ENABLE_VFS0050
+ &vfs0050_driver,
+#endif
/*#ifdef ENABLE_FDU2000
&fdu2000_driver,
#endif
@@ -801,7 +813,7 @@ static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
*/
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
{
- return dev->drv->type == DRIVER_IMAGING;
+ return dev->drv->capture_start != NULL;
}
/** \ingroup dev
@@ -817,38 +829,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
}
/** \ingroup dev
- * Captures an \ref img "image" from a device. The returned image is the raw
- * image provided by the device, you may wish to \ref img_std "standardize" it.
- *
- * If set, the unconditional flag indicates that the device should
- * capture an image unconditionally, regardless of whether a finger is there
- * or not. If unset, this function will block until a finger is detected on
- * the sensor.
- *
- * \param dev the device
- * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
- * \param image a location to return the captured image. Must be freed with
- * fp_img_free() after use.
- * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
- * unconditional flag was set but the device does not support this, or that the
- * device does not support imaging.
- * \sa fp_dev_supports_imaging()
- */
-API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
- struct fp_img **image)
-{
- struct fp_img_dev *imgdev = dev_to_img_dev(dev);
- if (!imgdev) {
- fp_dbg("image capture on non-imaging device");
- return -ENOTSUP;
- }
-
- //return fpi_imgdev_capture(imgdev, unconditional, image);
- /* FIXME reimplement async */
- return -ENOTSUP;
-}
-
-/** \ingroup dev
* Gets the expected width of images that will be captured from the device.
* This function will return -1 for devices that are not
* \ref imaging "imaging devices". If the width of images from this device
diff --git a/libfprint/data.c b/libfprint/data.c
index bfbf8fb..3c138c3 100644
--- a/libfprint/data.c
+++ b/libfprint/data.c
@@ -95,21 +95,33 @@ static const char *finger_num_to_str(enum fp_finger finger)
#endif
static struct fp_print_data *print_data_new(uint16_t driver_id,
- uint32_t devtype, enum fp_print_data_type type, size_t length)
+ uint32_t devtype, enum fp_print_data_type type)
{
- struct fp_print_data *data = g_malloc0(sizeof(*data) + length);
- fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
+ struct fp_print_data *data = g_malloc0(sizeof(*data));
+ fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
data->driver_id = driver_id;
data->devtype = devtype;
data->type = type;
- data->length = length;
return data;
}
-struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
+void fpi_print_data_item_free(struct fp_print_data_item *item)
+{
+ g_free(item);
+}
+
+struct fp_print_data_item *fpi_print_data_item_new(size_t length)
+{
+ struct fp_print_data_item *item = g_malloc(sizeof(*item) + length);
+ item->length = length;
+
+ return item;
+}
+
+struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
{
return print_data_new(dev->drv->id, dev->devtype,
- fpi_driver_get_data_type(dev->drv), length);
+ fpi_driver_get_data_type(dev->drv));
}
/** \ingroup print_data
@@ -124,27 +136,115 @@ struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
unsigned char **ret)
{
- struct fpi_print_data_fp1 *buf;
- size_t buflen;
+ struct fpi_print_data_fp2 *out_data;
+ struct fpi_print_data_item_fp2 *out_item;
+ struct fp_print_data_item *item;
+ size_t buflen = 0;
+ GSList *list_item;
+ unsigned char *buf;
fp_dbg("");
- buflen = sizeof(*buf) + data->length;
- buf = malloc(buflen);
- if (!buf)
- return 0;
-
- *ret = (unsigned char *) buf;
- buf->prefix[0] = 'F';
- buf->prefix[1] = 'P';
- buf->prefix[2] = '1';
- buf->driver_id = GUINT16_TO_LE(data->driver_id);
- buf->devtype = GUINT32_TO_LE(data->devtype);
- buf->data_type = data->type;
- memcpy(buf->data, data->data, data->length);
+ list_item = data->prints;
+ while (list_item) {
+ item = list_item->data;
+ buflen += sizeof(*out_item);
+ buflen += item->length;
+ list_item = g_slist_next(list_item);
+ }
+
+ buflen += sizeof(*out_data);
+ out_data = g_malloc(buflen);
+
+ *ret = (unsigned char *) out_data;
+ buf = out_data->data;
+ out_data->prefix[0] = 'F';
+ out_data->prefix[1] = 'P';
+ out_data->prefix[2] = '2';
+ out_data->driver_id = GUINT16_TO_LE(data->driver_id);
+ out_data->devtype = GUINT32_TO_LE(data->devtype);
+ out_data->data_type = data->type;
+
+ list_item = data->prints;
+ while (list_item) {
+ item = list_item->data;
+ out_item = (struct fpi_print_data_item_fp2 *)buf;
+ out_item->length = GUINT32_TO_LE(item->length);
+ /* FIXME: fp_print_data_item->data content is not endianess agnostic */
+ memcpy(out_item->data, item->data, item->length);
+ buf += sizeof(*out_item);
+ buf += item->length;
+ list_item = g_slist_next(list_item);
+ }
+
return buflen;
}
+static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
+ size_t buflen)
+{
+ size_t print_data_len;
+ struct fp_print_data *data;
+ struct fp_print_data_item *item;
+ struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
+
+ print_data_len = buflen - sizeof(*raw);
+ data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
+ GUINT32_FROM_LE(raw->devtype), raw->data_type);
+ item = fpi_print_data_item_new(print_data_len);
+ /* FIXME: fp_print_data->data content is not endianess agnostic */
+ memcpy(item->data, raw->data, print_data_len);
+ data->prints = g_slist_prepend(data->prints, item);
+
+ return data;
+}
+
+static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
+ size_t buflen)
+{
+ size_t total_data_len, item_len;
+ struct fp_print_data *data;
+ struct fp_print_data_item *item;
+ struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
+ unsigned char *raw_buf;
+ struct fpi_print_data_item_fp2 *raw_item;
+
+ total_data_len = buflen - sizeof(*raw);
+ data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
+ GUINT32_FROM_LE(raw->devtype), raw->data_type);
+ raw_buf = raw->data;
+ while (total_data_len) {
+ if (total_data_len < sizeof(*raw_item))
+ break;
+ total_data_len -= sizeof(*raw_item);
+
+ raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
+ item_len = GUINT32_FROM_LE(raw_item->length);
+ fp_dbg("item len %d, total_data_len %d", item_len, total_data_len);
+ if (total_data_len < item_len) {
+ fp_err("corrupted fingerprint data");
+ break;
+ }
+ total_data_len -= item_len;
+
+ item = fpi_print_data_item_new(item_len);
+ /* FIXME: fp_print_data->data content is not endianess agnostic */
+ memcpy(item->data, raw_item->data, item_len);
+ data->prints = g_slist_prepend(data->prints, item);
+
+ raw_buf += sizeof(*raw_item);
+ raw_buf += item_len;
+ }
+
+ if (g_slist_length(data->prints) == 0) {
+ fp_print_data_free(data);
+ data = NULL;
+ }
+
+ return data;
+
+}
+
/** \ingroup print_data
* Load a stored print from a data buffer. The contents of said buffer must
* be the untouched contents of a buffer previously supplied to you by the
@@ -157,24 +257,21 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
size_t buflen)
{
- struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf;
- size_t print_data_len;
- struct fp_print_data *data;
+ struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
fp_dbg("buffer size %zd", buflen);
if (buflen < sizeof(*raw))
return NULL;
- if (strncmp(raw->prefix, "FP1", 3) != 0) {
+ if (strncmp(raw->prefix, "FP1", 3) == 0) {
+ return fpi_print_data_from_fp1_data(buf, buflen);
+ } else if (strncmp(raw->prefix, "FP2", 3) == 0) {
+ return fpi_print_data_from_fp2_data(buf, buflen);
+ } else {
fp_dbg("bad header prefix");
- return NULL;
}
- print_data_len = buflen - sizeof(*raw);
- data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
- GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len);
- memcpy(data->data, raw->data, print_data_len);
- return data;
+ return NULL;
}
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
@@ -405,6 +502,8 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
*/
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
{
+ if (data)
+ g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
g_free(data);
}
diff --git a/libfprint/drivers/aes1660.c b/libfprint/drivers/aes1660.c
index 250ca52..d9ddc80 100644
--- a/libfprint/drivers/aes1660.c
+++ b/libfprint/drivers/aes1660.c
@@ -104,12 +104,6 @@ struct fp_img_driver aes1660_driver = {
.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,
diff --git a/libfprint/drivers/aes3500.c b/libfprint/drivers/aes3500.c
new file mode 100644
index 0000000..6f4d6b0
--- /dev/null
+++ b/libfprint/drivers/aes3500.c
@@ -0,0 +1,188 @@
+/*
+ * AuthenTec AES3500 driver for libfprint
+ *
+ * AES3500 is a press-typed sensor, which captures image in 128x128
+ * pixels.
+ *
+ * Thanks Rafael Toledo for the Windows driver and the help.
+ *
+ * This work is derived from Daniel Drake's AES4000 driver.
+ *
+ * Copyright (C) 2011-2013 Juvenn Woo
+ * Copyright (C) 2007-2008 Daniel Drake
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#define FP_COMPONENT "aes3500"
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include "aes3k.h"
+#include "driver_ids.h"
+
+#define DATA_BUFLEN 0x2089
+
+/* image size = FRAME_WIDTH x FRAME_WIDTH */
+#define FRAME_WIDTH 128
+#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
+#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
+#define ENLARGE_FACTOR 2
+
+
+static struct aes_regwrite init_reqs[] = {
+ /* master reset */
+ { 0x80, 0x01 },
+ { 0, 0 },
+ { 0x80, 0x00 },
+ { 0, 0 },
+
+ { 0x81, 0x00 },
+ { 0x80, 0x00 },
+ { 0, 0 },
+
+ /* scan reset */
+ { 0x80, 0x02 },
+ { 0, 0 },
+ { 0x80, 0x00 },
+ { 0, 0 },
+
+ /* disable register buffering */
+ { 0x80, 0x04 },
+ { 0, 0 },
+ { 0x80, 0x00 },
+ { 0, 0 },
+
+ { 0x81, 0x00 },
+ { 0, 0 },
+ /* windows driver reads registers now (81 02) */
+ { 0x80, 0x00 },
+ { 0x81, 0x00 },
+
+ /* set excitation bias current: 2mhz drive ring frequency,
+ * 4V drive ring voltage, 16.5mA excitation bias */
+ { 0x82, 0x04 },
+
+ /* continuously sample drive ring for finger detection,
+ * 62.50ms debounce delay */
+ { 0x83, 0x13 },
+
+ { 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */
+ { 0x85, 0x3d }, /* set calibration capacitance */
+ { 0x86, 0x03 }, /* detect drive voltage */
+ { 0x87, 0x01 }, /* set detection frequency to 125khz */
+ { 0x88, 0x02 }, /* set column scan period */
+ { 0x89, 0x02 }, /* set measure drive */
+ { 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */
+ { 0x8b, 0x33 }, /* set matrix pattern */
+ { 0x8c, 0x0f }, /* set demodulation phase 1 */
+ { 0x8d, 0x04 }, /* set demodulation phase 2 */
+ { 0x8e, 0x23 }, /* set sensor gain */
+ { 0x8f, 0x07 }, /* set image parameters */
+ { 0x90, 0x00 }, /* carrier offset null */
+ { 0x91, 0x1c }, /* set A/D reference high */
+ { 0x92, 0x08 }, /* set A/D reference low */
+ { 0x93, 0x00 }, /* set start row to 0 */
+ { 0x94, 0x07 }, /* set end row */
+ { 0x95, 0x00 }, /* set start column to 0 */
+ { 0x96, 0x1f }, /* set end column */
+ { 0x97, 0x04 }, /* data format and thresholds */
+ { 0x98, 0x28 }, /* image data control */
+ { 0x99, 0x00 }, /* disable general purpose outputs */
+ { 0x9a, 0x0b }, /* set initial scan state */
+ { 0x9b, 0x00 }, /* clear challenge word bits */
+ { 0x9c, 0x00 }, /* clear challenge word bits */
+ { 0x9d, 0x09 }, /* set some challenge word bits */
+ { 0x9e, 0x53 }, /* clear challenge word bits */
+ { 0x9f, 0x6b }, /* set some challenge word bits */
+ { 0, 0 },
+
+ { 0x80, 0x00 },
+ { 0x81, 0x00 },
+ { 0, 0 },
+ { 0x81, 0x04 },
+ { 0, 0 },
+ { 0x81, 0x00 },
+};
+
+static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
+{
+ int r;
+ struct aes3k_dev *aesdev;
+
+ r = libusb_claim_interface(dev->udev, 0);
+ if (r < 0)
+ fp_err("could not claim interface 0");
+
+ aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
+
+ if (!aesdev)
+ return -ENOMEM;
+
+ if (r == 0)
+ aesdev->data_buflen = DATA_BUFLEN;
+ aesdev->frame_width = FRAME_WIDTH;
+ aesdev->frame_size = FRAME_SIZE;
+ aesdev->frame_number = FRAME_NUMBER;
+ aesdev->enlarge_factor = ENLARGE_FACTOR;
+ aesdev->init_reqs = init_reqs;
+ aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
+ fpi_imgdev_open_complete(dev, 0);
+
+ return r;
+}
+
+static void dev_deinit(struct fp_img_dev *dev)
+{
+ struct aes3k_dev *aesdev = dev->priv;
+ g_free(aesdev);
+ libusb_release_interface(dev->udev, 0);
+ fpi_imgdev_close_complete(dev);
+}
+
+
+static const struct usb_id id_table[] = {
+ { .vendor = 0x08ff, .product = 0x5731 },
+ { 0, 0, 0, },
+};
+
+struct fp_img_driver aes3500_driver = {
+ .driver = {
+ .id = AES3500_ID,
+ .name = FP_COMPONENT,
+ .full_name = "AuthenTec AES3500",
+ .id_table = id_table,
+ .scan_type = FP_SCAN_TYPE_PRESS,
+ },
+ .flags = 0,
+ .img_height = FRAME_WIDTH * ENLARGE_FACTOR,
+ .img_width = FRAME_WIDTH * ENLARGE_FACTOR,
+
+ /* temporarily lowered until image quality improves */
+ .bz3_threshold = 9,
+
+ .open = dev_init,
+ .close = dev_deinit,
+ .activate = aes3k_dev_activate,
+ .deactivate = aes3k_dev_deactivate,
+};
+
diff --git a/libfprint/drivers/aes3k.c b/libfprint/drivers/aes3k.c
new file mode 100644
index 0000000..fefba3c
--- /dev/null
+++ b/libfprint/drivers/aes3k.c
@@ -0,0 +1,155 @@
+/*
+ * AuthenTec AES3500/AES4000 common routines
+ *
+ * The AES3500 and AES4000 sensors are press-typed, and could capture
+ * fingerprint images in 128x128 and 96x96 pixels respectively. They
+ * share a same communication interface: a number of frames are
+ * transferred and captured, from which a final image could be
+ * assembled. Each frame has fixed height of 16 pixels.
+ *
+ * As the imaging area is a bit small, only a part of finger could be
+ * captured, the detected minutiae are not so many that the NBIS
+ * matching works not so good. The verification rate is very low at the
+ * moment.
+ *
+ * This work is derived from Daniel Drake's AES4000 driver.
+ *
+ * Copyright (C) 2013 Juvenn Woo
+ * Copyright (C) 2007-2008 Daniel Drake
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#define FP_COMPONENT "aes3k"
+
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include "aes3k.h"
+
+#define CTRL_TIMEOUT 1000
+#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
+#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
+
+static void do_capture(struct fp_img_dev *dev);
+
+static void img_cb(struct libusb_transfer *transfer)
+{
+ struct fp_img_dev *dev = transfer->user_data;
+ struct aes3k_dev *aesdev = dev->priv;
+ unsigned char *ptr = transfer->buffer;
+ struct fp_img *tmp;
+ struct fp_img *img;
+ int i;
+
+ if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
+ goto err;
+ } else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ fpi_imgdev_session_error(dev, -EIO);
+ goto err;
+ } else if (transfer->length != transfer->actual_length) {
+ fpi_imgdev_session_error(dev, -EPROTO);
+ goto err;
+ }
+
+ fpi_imgdev_report_finger_status(dev, TRUE);
+
+ tmp = fpi_img_new(aesdev->frame_width * aesdev->frame_width);
+ tmp->width = aesdev->frame_width;
+ tmp->height = aesdev->frame_width;
+ tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
+ for (i = 0; i < aesdev->frame_number; i++) {
+ fp_dbg("frame header byte %02x", *ptr);
+ ptr++;
+ aes_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
+ ptr += aesdev->frame_size;
+ }
+
+ /* FIXME: this is an ugly hack to make the image big enough for NBIS
+ * to process reliably */
+ img = fpi_im_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
+ fp_img_free(tmp);
+ fpi_imgdev_image_captured(dev, img);
+
+ /* FIXME: rather than assuming finger has gone, we should poll regs until
+ * it really has, then restart the capture */
+ fpi_imgdev_report_finger_status(dev, FALSE);
+
+ do_capture(dev);
+
+err:
+ g_free(transfer->buffer);
+ aesdev->img_trf = NULL;
+ libusb_free_transfer(transfer);
+}
+
+static void do_capture(struct fp_img_dev *dev)
+{
+ struct aes3k_dev *aesdev = dev->priv;
+ unsigned char *data;
+ int r;
+
+ aesdev->img_trf = libusb_alloc_transfer(0);
+ if (!aesdev->img_trf) {
+ fpi_imgdev_session_error(dev, -EIO);
+ return;
+ }
+
+ data = g_malloc(aesdev->data_buflen);
+ libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
+ aesdev->data_buflen, img_cb, dev, 0);
+
+ r = libusb_submit_transfer(aesdev->img_trf);
+ if (r < 0) {
+ g_free(data);
+ libusb_free_transfer(aesdev->img_trf);
+ aesdev->img_trf = NULL;
+ fpi_imgdev_session_error(dev, r);
+ }
+}
+
+static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
+{
+ fpi_imgdev_activate_complete(dev, result);
+ if (result == 0)
+ do_capture(dev);
+}
+
+int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
+{
+ struct aes3k_dev *aesdev = dev->priv;
+ aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
+ return 0;
+}
+
+void aes3k_dev_deactivate(struct fp_img_dev *dev)
+{
+ struct aes3k_dev *aesdev = dev->priv;
+
+ /* FIXME: should wait for cancellation to complete before returning
+ * from deactivation, otherwise app may legally exit before we've
+ * cleaned up */
+ if (aesdev->img_trf)
+ libusb_cancel_transfer(aesdev->img_trf);
+ fpi_imgdev_deactivate_complete(dev);
+}
+
diff --git a/libfprint/drivers/aes3k.h b/libfprint/drivers/aes3k.h
new file mode 100644
index 0000000..98750ed
--- /dev/null
+++ b/libfprint/drivers/aes3k.h
@@ -0,0 +1,58 @@
+/*
+ * AuthenTec AES3500/AES4000 common routines
+ *
+ * The AES3500 and AES4000 sensors are press-typed, and could capture
+ * fingerprint images in 128x128 and 96x96 pixels respectively. They
+ * share a same communication interface: a number of frames are
+ * transferred and captured, from which a final image could be
+ * assembled. Each frame has fixed height of 16 pixels.
+ *
+ * As the imaging area is a bit small, only a part of finger could be
+ * captured, the detected minutiae are not so many that the NBIS
+ * matching works not so good. The verification rate is very low at the
+ * moment.
+ *
+ * This work is derived from Daniel Drake's AES4000 driver.
+ *
+ * Copyright (C) 2013 Juvenn Woo
+ * Copyright (C) 2007-2008 Daniel Drake
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __AES3K_H
+#define __AES3K_H
+
+#define AES3K_FRAME_HEIGHT 16
+
+struct aes3k_dev {
+ struct libusb_transfer *img_trf;
+ size_t frame_width; /* image size = frame_width x frame_width */
+ size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
+ size_t frame_number; /* number of frames */
+ size_t enlarge_factor;
+
+ size_t data_buflen; /* buffer length of usb bulk transfer */
+ struct aes_regwrite *init_reqs; /* initial values sent to device */
+ size_t init_reqs_len;
+};
+
+
+int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
+void aes3k_dev_deactivate(struct fp_img_dev *dev);
+
+#endif
diff --git a/libfprint/drivers/aes4000.c b/libfprint/drivers/aes4000.c
index 6ff32a2..b849651 100644
--- a/libfprint/drivers/aes4000.c
+++ b/libfprint/drivers/aes4000.c
@@ -1,5 +1,12 @@
/*
* AuthenTec AES4000 driver for libfprint
+ *
+ * AES4000 is a press-typed sensor, which captures image in 96x96
+ * pixels.
+ *
+ * This work is derived from Daniel Drake's AES4000 driver.
+ *
+ * Copyright (C) 2013 Juvenn Woo
* Copyright (C) 2007-2008 Daniel Drake
*
* This library is free software; you can redistribute it and/or
@@ -27,24 +34,19 @@
#include
#include
+#include "aes3k.h"
#include "driver_ids.h"
-#define CTRL_TIMEOUT 1000
-#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
-#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
-#define DATA_BUFLEN 0x1259
-#define NR_SUBARRAYS 6
-#define SUBARRAY_LEN 768
+#define DATA_BUFLEN 0x1259
-#define IMG_HEIGHT 96
-#define IMG_WIDTH 96
-#define ENLARGE_FACTOR 3
+/* image size = FRAME_WIDTH x FRAME_WIDTH */
+#define FRAME_WIDTH 96
+#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
+#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
+#define ENLARGE_FACTOR 3
-struct aes4k_dev {
- struct libusb_transfer *img_trf;
-};
-static const struct aes_regwrite init_reqs[] = {
+static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
@@ -119,119 +121,28 @@ static const struct aes_regwrite init_reqs[] = {
{ 0x81, 0x00 },
};
-static void do_capture(struct fp_img_dev *dev);
-
-static void img_cb(struct libusb_transfer *transfer)
-{
- struct fp_img_dev *dev = transfer->user_data;
- struct aes4k_dev *aesdev = dev->priv;
- unsigned char *ptr = transfer->buffer;
- struct fp_img *tmp;
- struct fp_img *img;
- int i;
-
- if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
- goto err;
- } else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
- fpi_imgdev_session_error(dev, -EIO);
- goto err;
- } else if (transfer->length != transfer->actual_length) {
- fpi_imgdev_session_error(dev, -EPROTO);
- goto err;
- }
-
- fpi_imgdev_report_finger_status(dev, TRUE);
-
- tmp = fpi_img_new(IMG_WIDTH * IMG_HEIGHT);
- tmp->width = IMG_WIDTH;
- tmp->height = IMG_HEIGHT;
- tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
- for (i = 0; i < NR_SUBARRAYS; i++) {
- fp_dbg("subarray header byte %02x", *ptr);
- ptr++;
- aes_assemble_image(ptr, 96, 16, tmp->data + (i * 96 * 16));
- ptr += SUBARRAY_LEN;
- }
-
- /* FIXME: this is an ugly hack to make the image big enough for NBIS
- * to process reliably */
- img = fpi_im_resize(tmp, ENLARGE_FACTOR, ENLARGE_FACTOR);
- fp_img_free(tmp);
- fpi_imgdev_image_captured(dev, img);
-
- /* FIXME: rather than assuming finger has gone, we should poll regs until
- * it really has, then restart the capture */
- fpi_imgdev_report_finger_status(dev, FALSE);
-
- do_capture(dev);
-
-err:
- g_free(transfer->buffer);
- aesdev->img_trf = NULL;
- libusb_free_transfer(transfer);
-}
-
-static void do_capture(struct fp_img_dev *dev)
-{
- struct aes4k_dev *aesdev = dev->priv;
- unsigned char *data;
- int r;
-
- aesdev->img_trf = libusb_alloc_transfer(0);
- if (!aesdev->img_trf) {
- fpi_imgdev_session_error(dev, -EIO);
- return;
- }
-
- data = g_malloc(DATA_BUFLEN);
- libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
- DATA_BUFLEN, img_cb, dev, 0);
-
- r = libusb_submit_transfer(aesdev->img_trf);
- if (r < 0) {
- g_free(data);
- libusb_free_transfer(aesdev->img_trf);
- aesdev->img_trf = NULL;
- fpi_imgdev_session_error(dev, r);
- }
-}
-
-static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
-{
- fpi_imgdev_activate_complete(dev, result);
- if (result == 0)
- do_capture(dev);
-}
-
-static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
-{
- aes_write_regv(dev, init_reqs, G_N_ELEMENTS(init_reqs), init_reqs_cb, NULL);
- return 0;
-}
-
-static void dev_deactivate(struct fp_img_dev *dev)
-{
- struct aes4k_dev *aesdev = dev->priv;
-
- /* FIXME: should wait for cancellation to complete before returning
- * from deactivation, otherwise app may legally exit before we've
- * cleaned up */
- if (aesdev->img_trf)
- libusb_cancel_transfer(aesdev->img_trf);
- fpi_imgdev_deactivate_complete(dev);
-}
-
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
+ struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
- dev->priv = g_malloc0(sizeof(struct aes4k_dev));
+ aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
+
+ if (!aesdev)
+ return -ENOMEM;
if (r == 0)
+ aesdev->data_buflen = DATA_BUFLEN;
+ aesdev->frame_width = FRAME_WIDTH;
+ aesdev->frame_size = FRAME_SIZE;
+ aesdev->frame_number = FRAME_NUMBER;
+ aesdev->enlarge_factor = ENLARGE_FACTOR;
+ aesdev->init_reqs = init_reqs;
+ aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
@@ -239,11 +150,13 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_deinit(struct fp_img_dev *dev)
{
- g_free(dev->priv);
+ struct aes3k_dev *aesdev = dev->priv;
+ g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
+
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5501 },
{ 0, 0, 0, },
@@ -258,15 +171,15 @@ struct fp_img_driver aes4000_driver = {
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
- .img_height = IMG_HEIGHT * ENLARGE_FACTOR,
- .img_width = IMG_WIDTH * ENLARGE_FACTOR,
+ .img_height = FRAME_WIDTH * ENLARGE_FACTOR,
+ .img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
- .activate = dev_activate,
- .deactivate = dev_deactivate,
+ .activate = aes3k_dev_activate,
+ .deactivate = aes3k_dev_deactivate,
};
diff --git a/libfprint/drivers/driver_ids.h b/libfprint/drivers/driver_ids.h
index 0f45beb..8333075 100644
--- a/libfprint/drivers/driver_ids.h
+++ b/libfprint/drivers/driver_ids.h
@@ -36,6 +36,10 @@ enum {
UPEKE2_ID = 13,
AES1660_ID = 14,
AES2660_ID = 15,
+ AES3500_ID = 16,
+ UPEKTC_IMG_ID = 17,
+ ETES603_ID = 18,
+ VFS0050_ID = 19,
};
#endif
diff --git a/libfprint/drivers/etes603.c b/libfprint/drivers/etes603.c
new file mode 100644
index 0000000..aae0f0f
--- /dev/null
+++ b/libfprint/drivers/etes603.c
@@ -0,0 +1,1513 @@
+/*
+ * EgisTec ES603 driver for libfprint
+ * Copyright (C) 2012 Patrick Marlier
+ *
+ * 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
+ */
+
+/* EgisTec ES603 device information
+ * Sensor area: 192 x 4 pixels
+ * Sensor gray: 16 gray levels/sensor pixel
+ * Sensor resolution: 508 dpi
+ * USB Manufacturer ID: 1C7A
+ * USB Product ID: 0603
+ *
+ * Possible compatibility LTT-SS500/SS501
+ *
+ * Extra features not present in this driver (see https://code.google.com/p/etes603):
+ * Tuning of DTVRT for contact detection
+ * Contact detection via capacitance
+ * Capture mode using assembled frames (usually better quality)
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define FP_COMPONENT "etes603"
+#include
+#include "driver_ids.h"
+
+/* libusb defines */
+#define EP_IN 0x81
+#define EP_OUT 0x02
+/* Note that 1000 ms is usually enough but with CMD_READ_FE could be longer
+ * since the sensor is waiting motion. */
+#define BULK_TIMEOUT 1000
+
+/* es603 defines */
+#define FRAME_WIDTH 192 /* pixels per row */
+#define FRAME_HEIGHT 4 /* number of rows */
+#define FRAME_SIZE 384 /* size in bytes (4 bits per pixels) */
+#define FE_WIDTH 256 /* pixels per row for Fly-Estimation */
+#define FE_HEIGHT 500 /* number of rows for Fly-Estimation */
+#define FE_SIZE 64000 /* size in bytes (4 bits per pixels) */
+
+#define GAIN_SMALL_INIT 0x23 /* Initial small gain */
+#define VRT_MAX 0x3F /* Maximum value for VRT */
+#define VRB_MAX 0x3A /* Maximum value for VRB */
+#define DTVRT_MAX 0x3A /* Maximum value for DTVRT */
+#define DCOFFSET_MIN 0x00 /* Minimum value for DCoffset */
+#define DCOFFSET_MAX 0x35 /* Maximum value for DCoffset */
+
+/* es603 commands */
+#define CMD_READ_REG 0x01
+#define CMD_WRITE_REG 0x02
+#define CMD_READ_FRAME 0x03 /* Read the sensor area */
+#define CMD_READ_FE 0x06 /* Read a fingerprint using Fly-Estimation */
+#define CMD_20 0x20 /* ? */
+#define CMD_25 0x25 /* ? */
+#define CMD_60 0x60 /* ? */
+
+#define CMD_OK 0x01 /* Command successfully executed */
+
+/* es603 registers */
+#define REG_MAX 0x18 /* Maximum number of registers in one message */
+#define REG_MODE_CONTROL 0x02 /* Mode control */
+#define REG_03 0x03 /* Contact register? */
+#define REG_04 0x04 /* ? */
+#define REG_10 0x10 /* MVS FRMBUF control */
+#define REG_1A 0x1A /* ? */
+/* BEGIN init sensor */
+#define REG_20 0x20 /* (def: 0x00) */
+#define REG_21 0x21 /* Small gain (def: 0x23) */
+#define REG_22 0x22 /* Normal gain (def: 0x21) */
+#define REG_23 0x23 /* Large gain (def: 0x20) */
+#define REG_24 0x24 /* (def: 0x14) */
+#define REG_25 0x25 /* (def: 0x6A) */
+#define REG_26 0x26 /* VRB again? (def: 0x00) */
+#define REG_27 0x27 /* VRT again? (def: 0x00) */
+#define REG_28 0x28 /* (def: 0x00) */
+#define REG_29 0x29 /* (def: 0xC0) */
+#define REG_2A 0x2A /* (def: 0x50) */
+#define REG_2B 0x2B /* (def: 0x50) */
+#define REG_2C 0x2C /* (def: 0x4D) */
+#define REG_2D 0x2D /* (def: 0x03) */
+#define REG_2E 0x2E /* (def: 0x06) */
+#define REG_2F 0x2F /* (def: 0x06) */
+#define REG_30 0x30 /* (def: 0x10) */
+#define REG_31 0x31 /* (def: 0x02) */
+#define REG_32 0x32 /* (def: 0x14) */
+#define REG_33 0x33 /* (def: 0x34) */
+#define REG_34 0x34 /* (def: 0x01) */
+#define REG_35 0x35 /* (def: 0x08) */
+#define REG_36 0x36 /* (def: 0x03) */
+#define REG_37 0x37 /* (def: 0x21) */
+/* END init sensor */
+
+#define REG_ENC1 0x41 /* Encryption 1 */
+#define REG_ENC2 0x42
+#define REG_ENC3 0x43
+#define REG_ENC4 0x44
+#define REG_ENC5 0x45
+#define REG_ENC6 0x46
+#define REG_ENC7 0x47
+#define REG_ENC8 0x48 /* Encryption 8 */
+
+#define REG_50 0x50 /* ? For contact detection */
+#define REG_51 0x51 /* ? */
+#define REG_59 0x59 /* ? */
+#define REG_5A 0x5A /* ? */
+#define REG_5B 0x5B /* ? */
+
+#define REG_INFO0 0x70 /* Sensor model byte0 */
+#define REG_INFO1 0x71 /* Sensor model byte1 */
+#define REG_INFO2 0x72 /* Sensor model byte2 */
+#define REG_INFO3 0x73 /* Sensor model byte3 */
+
+#define REG_GAIN 0xE0
+#define REG_VRT 0xE1
+#define REG_VRB 0xE2
+#define REG_DTVRT 0xE3 /* used for contact detection */
+#define REG_VCO_CONTROL 0xE5 /* 0x13 (IDLE?), 0x14 (REALTIME) */
+#define REG_DCOFFSET 0xE6
+
+#define REG_F0 0xF0 /* ? init:0x00 close:0x01 */
+#define REG_F2 0xF2 /* ? init:0x00 close:0x4E */
+
+#define REG_MODE_SLEEP 0x30 /* Sleep mode */
+#define REG_MODE_CONTACT 0x31 /* Contact mode */
+#define REG_MODE_SENSOR 0x33 /* Sensor mode */
+#define REG_MODE_FP 0x34 /* FingerPrint mode (Fly-Estimation®) */
+
+#define REG_VCO_IDLE 0x13
+#define REG_VCO_RT 0x14 /* Realtime */
+
+/* The size of the message header is 5 plus 1 for the command. */
+#define MSG_HDR_SIZE 6
+
+/* This structure must be packed because it is a the raw message sent. */
+struct egis_msg {
+ uint8_t magic[5]; /* out: 'EGIS' 0x09 / in: 'SIGE' 0x0A */
+ uint8_t cmd;
+ union {
+ struct {
+ uint8_t nb;
+ uint8_t regs[REG_MAX];
+ } egis_readreg;
+ struct {
+ uint8_t regs[REG_MAX];
+ } sige_readreg;
+ struct {
+ uint8_t nb;
+ struct {
+ uint8_t reg;
+ uint8_t val;
+ } regs[REG_MAX];
+ } egis_writereg;
+ struct {
+ uint8_t length_factor;
+ uint8_t length;
+ uint8_t use_gvv;
+ uint8_t gain;
+ uint8_t vrt;
+ uint8_t vrb;
+ } egis_readf;
+ struct {
+ uint8_t len[2];
+ uint8_t val[3];
+ } egis_readfp;
+ struct {
+ uint8_t val[5];
+ } sige_misc;
+ uint8_t padding[0x40-6]; /* Ensure size of 0x40 */
+ };
+} __attribute__((packed));
+
+
+/* Structure to keep information between asynchronous functions. */
+struct etes603_dev {
+ uint8_t regs[256];
+ struct egis_msg *req;
+ size_t req_len;
+ struct egis_msg *ans;
+ size_t ans_len;
+
+ uint8_t *fp;
+ uint16_t fp_height;
+
+ uint8_t tunedc_min;
+ uint8_t tunedc_max;
+
+ /* Device parameters */
+ uint8_t gain;
+ uint8_t dcoffset;
+ uint8_t vrt;
+ uint8_t vrb;
+
+ unsigned int is_active;
+};
+
+static void m_start_fingerdetect(struct fp_img_dev *idev);
+/*
+ * Prepare the header of the message to be sent to the device.
+ */
+static void msg_header_prepare(struct egis_msg *msg)
+{
+ msg->magic[0] = 'E';
+ msg->magic[1] = 'G';
+ msg->magic[2] = 'I';
+ msg->magic[3] = 'S';
+ msg->magic[4] = 0x09;
+}
+
+/*
+ * Check that the header of the received message is correct.
+ */
+static int msg_header_check(struct egis_msg *msg)
+{
+ if (msg->magic[0] == 'S' && msg->magic[1] == 'I'
+ && msg->magic[2] == 'G' && msg->magic[3] == 'E'
+ && msg->magic[4] == 0x0A)
+ return 0;
+ return -1;
+}
+
+/*
+ * Prepare message to ask for a frame.
+ */
+static void msg_get_frame(struct etes603_dev *dev,
+ uint8_t use_gvv, uint8_t gain, uint8_t vrt, uint8_t vrb)
+{
+ struct egis_msg *msg = dev->req;
+ msg_header_prepare(msg);
+ msg->cmd = CMD_READ_FRAME;
+ msg->egis_readf.length_factor = 0x01;
+ /* length should be 0xC0 */
+ msg->egis_readf.length = FRAME_WIDTH;
+ msg->egis_readf.use_gvv = use_gvv;
+ /* if use_gvv is set, gain/vrt/vrb are used */
+ msg->egis_readf.gain = gain;
+ msg->egis_readf.vrt = vrt;
+ msg->egis_readf.vrb = vrb;
+
+ dev->req_len = MSG_HDR_SIZE + 6;
+ dev->ans_len = FRAME_SIZE;
+}
+
+/*
+ * Prepare message to ask for a fingerprint frame.
+ */
+static void msg_get_fp(struct etes603_dev *dev, uint8_t len0, uint8_t len1,
+ uint8_t v2, uint8_t v3, uint8_t v4)
+{
+ struct egis_msg *msg = dev->req;
+ msg_header_prepare(msg);
+ msg->cmd = CMD_READ_FE;
+ /* Unknown values and always same on captured frames.
+ * 1st 2nd bytes is unsigned short for height, but only on value range
+ * 0x01 0xF4 (500), 0x02 0x00 (512), 0x02 0xF4 (756) are ok
+ */
+ msg->egis_readfp.len[0] = len0;
+ msg->egis_readfp.len[1] = len1;
+ /* 3rd byte : ?? but changes frame size
+ * 4th byte : 0x00 -> can change width
+ * 5th byte : motion sensibility?
+ */
+ msg->egis_readfp.val[0] = v2;
+ msg->egis_readfp.val[1] = v3;
+ msg->egis_readfp.val[2] = v4;
+
+ dev->req_len = MSG_HDR_SIZE + 5;
+ dev->ans_len = FE_SIZE;
+}
+
+/*
+ * Prepare message to read registers from the sensor.
+ * Variadic argument pattern: int reg, ...
+ */
+static void msg_get_regs(struct etes603_dev *dev, int n_args, ... )
+{
+ struct egis_msg *msg = dev->req;
+ va_list ap;
+ int i;
+
+ assert(n_args > 0 && n_args <= REG_MAX);
+
+ msg_header_prepare(msg);
+ msg->cmd = CMD_READ_REG;
+ msg->egis_readreg.nb = n_args;
+ va_start(ap, n_args);
+ for (i = 0; i < n_args; i++) {
+ msg->egis_readreg.regs[i] = va_arg(ap, int);
+ }
+ va_end(ap);
+
+ dev->req_len = MSG_HDR_SIZE + 1 + n_args;
+ dev->ans_len = MSG_HDR_SIZE + 1 + n_args;
+}
+
+/*
+ * Parse the result of read register command.
+ */
+static int msg_parse_regs(struct etes603_dev *dev)
+{
+ size_t i, n_args;
+ struct egis_msg *msg_req = dev->req;
+ struct egis_msg *msg_ans = dev->ans;
+ n_args = dev->ans_len - MSG_HDR_SIZE;
+
+ if (msg_header_check(msg_ans)) {
+ return -1;
+ }
+ if (msg_ans->cmd != CMD_OK) {
+ return -2;
+ }
+
+ for (i = 0; i < n_args; i++) {
+ int reg = msg_req->egis_readreg.regs[i];
+ dev->regs[reg] = msg_ans->sige_readreg.regs[i];
+ }
+ return 0;
+}
+
+/*
+ * Prepare message to write sensor's registers.
+ * Variadic arguments are: int reg, int val, ...
+ */
+static void msg_set_regs(struct etes603_dev *dev, int n_args, ...)
+{
+ struct egis_msg *msg = dev->req;
+ va_list ap;
+ int i;
+
+ assert(n_args != 0 && n_args % 2 == 0 && n_args <= REG_MAX * 2);
+
+ msg_header_prepare(msg);
+ msg->cmd = CMD_WRITE_REG;
+ msg->egis_writereg.nb = n_args / 2;
+
+ va_start(ap, n_args);
+ for (i = 0; i < n_args / 2; i++) {
+ msg->egis_writereg.regs[i].reg = va_arg(ap, int);
+ msg->egis_writereg.regs[i].val = va_arg(ap, int);
+ }
+ va_end(ap);
+
+ dev->req_len = MSG_HDR_SIZE + 1 + n_args;
+ dev->ans_len = MSG_HDR_SIZE + 1;
+}
+
+static int msg_check_ok(struct etes603_dev *dev)
+{
+ struct egis_msg *msg = dev->ans;
+ if (msg_header_check(msg)) {
+ goto err;
+ }
+ if (msg->cmd != CMD_OK) {
+ goto err;
+ }
+ return 0;
+err:
+ return -1;
+}
+
+/*
+ * Check the model of the sensor.
+ */
+static int check_info(struct etes603_dev *dev)
+{
+ if (dev->regs[0x70] == 0x4A && dev->regs[0x71] == 0x44
+ && dev->regs[0x72] == 0x49 && dev->regs[0x73] == 0x31)
+ return 0;
+ fp_err("unknown device parameters (REG_70:%02X REG_71:%02X "
+ "REG_FIRMWARE:%02X REG_VERSION:%02X)",
+ dev->regs[0x70], dev->regs[0x71], dev->regs[0x72],
+ dev->regs[0x73]);
+ return -1;
+}
+
+static void msg_get_cmd20(struct etes603_dev *dev)
+{
+ struct egis_msg *msg = dev->req;
+ msg_header_prepare(msg);
+ msg->cmd = CMD_20;
+ dev->req_len = MSG_HDR_SIZE;
+}
+
+static int msg_check_cmd20(struct etes603_dev *dev)
+{
+ struct egis_msg *msg = dev->ans;
+ if (msg_header_check(msg)) {
+ fp_err("msg_header_check failed");
+ return -1;
+ }
+ /* status or flashtype/flashinfo or ? */
+ if (msg->cmd != 0x05
+ || msg->sige_misc.val[0] != 0x00
+ || msg->sige_misc.val[1] != 0x00) {
+ fp_warn("unexpected answer CMD_20 from device(%02X %02X %02X)",
+ msg->cmd, msg->sige_misc.val[0], msg->sige_misc.val[1]);
+ }
+
+ return 0;
+}
+
+static void msg_get_cmd25(struct etes603_dev *dev)
+{
+ struct egis_msg *msg = dev->req;
+ msg_header_prepare(msg);
+ msg->cmd = CMD_25;
+ dev->req_len = MSG_HDR_SIZE;
+}
+
+static int msg_check_cmd25(struct etes603_dev *dev)
+{
+ struct egis_msg *msg = dev->ans;
+ if (msg_header_check(msg)) {
+ fp_err("msg_header_check failed");
+ goto err;
+ }
+ if (msg->cmd != CMD_OK) {
+ fp_err("CMD_OK failed");
+ goto err;
+ }
+ /* flashtype or status or ? */
+ if (msg->sige_misc.val[0] != 0x00) {
+ fp_warn("unexpected answer for CMD_25 (%02X)",
+ msg->sige_misc.val[0]);
+ }
+ return 0;
+err:
+ return -1;
+}
+
+static void msg_set_mode_control(struct etes603_dev *dev, uint8_t mode)
+{
+ msg_set_regs(dev, 2, REG_MODE_CONTROL, mode);
+}
+
+
+/* Processing functions */
+
+/*
+ * Return the brightness of a 4bpp frame
+ */
+static unsigned int process_get_brightness(uint8_t *f, size_t s)
+{
+ unsigned int i, sum = 0;
+ for (i = 0; i < s; i++) {
+ sum += f[i] >> 4;
+ sum += f[i] & 0x0F;
+ }
+ return sum;
+}
+
+/*
+ * Return the histogram of a 4bpp frame
+ */
+static void process_hist(uint8_t *f, size_t s, float stat[5])
+{
+ float hist[16];
+ float black_mean, white_mean;
+ int i;
+ /* Clean histogram */
+ for (i = 0; i < 16; i++)
+ hist[i] = 0.0;
+ for (i = 0; i < s; i++) {
+ hist[f[i] >> 4]++;
+ hist[f[i] & 0x0F]++;
+ }
+ /* histogram average */
+ for (i = 0; i < 16; i++) {
+ hist[i] = hist[i] / (s * 2);
+ }
+ /* Average black/white pixels (full black and full white pixels
+ * are excluded). */
+ black_mean = white_mean = 0.0;
+ for (i = 1; i < 8; i++)
+ black_mean += hist[i];
+ for (i = 8; i < 15; i++)
+ white_mean += hist[i];
+ stat[0] = hist[0];
+ stat[1] = black_mean;
+ stat[2] = black_mean+white_mean;
+ stat[3] = white_mean;
+ stat[4] = hist[15];
+ fp_dbg("fullb=%6f black=%6f grey=%6f white=%6f fullw=%6f",
+ hist[0], black_mean, black_mean+white_mean, white_mean,
+ hist[15]);
+}
+
+/*
+ * Return true if the frame is almost empty.
+ */
+static int process_frame_empty(uint8_t *frame, size_t size)
+{
+ unsigned int sum = process_get_brightness(frame, size);
+ /* Allow an average of 'threshold' luminosity per pixel */
+ if (sum < size)
+ return 1;
+ return 0;
+}
+
+/* Transform 4 bits image to 8 bits image */
+static void process_4to8_bpp(uint8_t *input, unsigned int input_size,
+ uint8_t *output)
+{
+ unsigned int i, j = 0;
+ for (i = 0; i < input_size; i++, j += 2) {
+ /* 16 gray levels transform to 256 levels using << 4 */
+ output[j] = input[i] & 0xF0;
+ output[j+1] = input[i] << 4;
+ }
+}
+
+/*
+ * Remove duplicated lines at the end of a fingerprint.
+ */
+static void process_remove_fp_end(struct etes603_dev *dev)
+{
+ unsigned int i;
+ /* 2 last lines with Fly-Estimation are the empty pattern. */
+ uint8_t *pattern = dev->fp + (dev->fp_height - 2) * FE_WIDTH / 2;
+ for (i = 2; i < dev->fp_height; i+= 2) {
+ if (memcmp(pattern, pattern - (i * FE_WIDTH / 2), FE_WIDTH))
+ break;
+ }
+ dev->fp_height -= i;
+ fp_dbg("Removing %d empty lines from image", i - 2);
+}
+
+static void reset_param(struct etes603_dev *dev)
+{
+ dev->dcoffset = 0;
+ dev->vrt = 0;
+ dev->vrb = 0;
+ dev->gain = 0;
+}
+
+
+/* Asynchronous stuff */
+
+enum {
+ INIT_CHECK_INFO_REQ,
+ INIT_CHECK_INFO_ANS,
+ INIT_CMD20_REQ,
+ INIT_CMD20_ANS,
+ INIT_CMD25_REQ,
+ INIT_CMD25_ANS,
+ INIT_SENSOR_REQ,
+ INIT_SENSOR_ANS,
+ INIT_ENC_REQ,
+ INIT_ENC_ANS,
+ INIT_REGS_REQ,
+ INIT_REGS_ANS,
+ INIT_NUM_STATES
+};
+
+enum {
+ TUNEDC_INIT,
+ TUNEDC_SET_DCOFFSET_REQ,
+ TUNEDC_SET_DCOFFSET_ANS,
+ TUNEDC_GET_FRAME_REQ,
+ TUNEDC_GET_FRAME_ANS,
+ TUNEDC_FINAL_SET_REG2122_REQ,
+ TUNEDC_FINAL_SET_REG2122_ANS,
+ TUNEDC_FINAL_SET_GAIN_REQ,
+ TUNEDC_FINAL_SET_GAIN_ANS,
+ TUNEDC_FINAL_SET_DCOFFSET_REQ,
+ TUNEDC_FINAL_SET_DCOFFSET_ANS,
+ TUNEDC_NUM_STATES
+};
+
+enum {
+ TUNEVRB_INIT,
+ TUNEVRB_GET_GAIN_REQ,
+ TUNEVRB_GET_GAIN_ANS,
+ TUNEVRB_GET_DCOFFSET_REQ,
+ TUNEVRB_GET_DCOFFSET_ANS,
+ TUNEVRB_SET_DCOFFSET_REQ,
+ TUNEVRB_SET_DCOFFSET_ANS,
+ TUNEVRB_FRAME_REQ,
+ TUNEVRB_FRAME_ANS,
+ TUNEVRB_FINAL_SET_DCOFFSET_REQ,
+ TUNEVRB_FINAL_SET_DCOFFSET_ANS,
+ TUNEVRB_FINAL_SET_REG2627_REQ,
+ TUNEVRB_FINAL_SET_REG2627_ANS,
+ TUNEVRB_FINAL_SET_GAINVRTVRB_REQ,
+ TUNEVRB_FINAL_SET_GAINVRTVRB_ANS,
+ TUNEVRB_FINAL_SET_MODE_SLEEP_REQ,
+ TUNEVRB_FINAL_SET_MODE_SLEEP_ANS,
+ TUNEVRB_NUM_STATES
+};
+
+enum {
+ FGR_FPA_INIT_SET_MODE_SLEEP_REQ,
+ FGR_FPA_INIT_SET_MODE_SLEEP_ANS,
+ FGR_FPA_INIT_SET_DCOFFSET_REQ,
+ FGR_FPA_INIT_SET_DCOFFSET_ANS,
+ FGR_FPA_INIT_SET_GAINVRTVRB_REQ,
+ FGR_FPA_INIT_SET_GAINVRTVRB_ANS,
+ FGR_FPA_INIT_SET_VCO_CONTROL_RT_REQ,
+ FGR_FPA_INIT_SET_VCO_CONTROL_RT_ANS,
+ FGR_FPA_INIT_SET_REG04_REQ,
+ FGR_FPA_INIT_SET_REG04_ANS,
+ FGR_FPA_INIT_SET_MODE_SENSOR_REQ,
+ FGR_FPA_INIT_SET_MODE_SENSOR_ANS,
+ FGR_FPA_GET_FRAME_REQ,
+ FGR_FPA_GET_FRAME_ANS,
+ FGR_NUM_STATES
+};
+
+enum {
+ CAP_FP_INIT_SET_REG10_REQ,
+ CAP_FP_INIT_SET_REG10_ANS,
+ CAP_FP_INIT_SET_MODE_FP_REQ,
+ CAP_FP_INIT_SET_MODE_FP_ANS,
+ CAP_FP_GET_FP_REQ,
+ CAP_FP_GET_FP_ANS,
+ CAP_NUM_STATES
+};
+
+enum {
+ EXIT_SET_REGS_REQ,
+ EXIT_SET_REGS_ANS,
+ EXIT_NUM_STATES
+};
+
+static int async_tx(struct fp_img_dev *idev, unsigned int ep, void *cb,
+ void *cb_arg)
+{
+ struct etes603_dev *dev = idev->priv;
+ struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ unsigned char *buffer;
+ int length;
+
+ if (!transfer)
+ return -ENOMEM;
+
+ if (ep == EP_OUT) {
+ buffer = (unsigned char *)dev->req;
+ length = dev->req_len;
+ } else if (ep == EP_IN) {
+ buffer = (unsigned char *)dev->ans;
+ length = dev->ans_len;
+ } else {
+ return -EIO;
+ }
+ libusb_fill_bulk_transfer(transfer, idev->udev, ep, buffer, length,
+ cb, cb_arg, BULK_TIMEOUT);
+
+ if (libusb_submit_transfer(transfer)) {
+ libusb_free_transfer(transfer);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+static void async_tx_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ fp_warn("transfer is not completed (status=%d)",
+ transfer->status);
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ libusb_free_transfer(transfer);
+ } else {
+ unsigned char endpoint = transfer->endpoint;
+ int actual_length = transfer->actual_length;
+ int length = transfer->length;
+ /* Freeing now transfer since fpi_ssm_* functions are not
+ * returning directly. */
+ libusb_free_transfer(transfer);
+ if (endpoint == EP_OUT) {
+ if (length != actual_length)
+ fp_warn("length %d != actual_length %d",
+ length, actual_length);
+ /* Chained with the answer */
+ if (async_tx(idev, EP_IN, async_tx_cb, ssm))
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ } else if (endpoint == EP_IN) {
+ dev->ans_len = actual_length;
+ fpi_ssm_next_state(ssm);
+ }
+ }
+}
+
+static void m_exit_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ switch (ssm->cur_state) {
+ case EXIT_SET_REGS_REQ:
+ msg_set_regs(dev, 4, REG_VCO_CONTROL, REG_VCO_IDLE,
+ REG_MODE_CONTROL, REG_MODE_SLEEP);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case EXIT_SET_REGS_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_mark_completed(ssm);
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+}
+
+static void m_exit_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+
+ if (ssm->error) {
+ fp_err("Error switching the device to idle state");
+ } else {
+ fp_dbg("The device is now in idle state");
+ }
+ fpi_imgdev_deactivate_complete(idev);
+ fpi_ssm_free(ssm);
+}
+
+static void m_exit_start(struct fp_img_dev *idev)
+{
+ struct fpi_ssm *ssm = fpi_ssm_new(idev->dev, m_exit_state,
+ EXIT_NUM_STATES);
+ fp_dbg("Switching device to idle mode");
+ ssm->priv = idev;
+ fpi_ssm_start(ssm, m_exit_complete);
+}
+
+static void m_capture_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (dev->is_active == FALSE) {
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ switch (ssm->cur_state) {
+ case CAP_FP_INIT_SET_REG10_REQ:
+ /* Reset fingerprint */
+ fp_dbg("Capturing a fingerprint...");
+ memset(dev->fp, 0, FE_SIZE * 2);
+ dev->fp_height = 0;
+ msg_set_regs(dev, 2, REG_10, 0x92);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case CAP_FP_INIT_SET_REG10_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case CAP_FP_INIT_SET_MODE_FP_REQ:
+ msg_set_mode_control(dev, REG_MODE_FP);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case CAP_FP_INIT_SET_MODE_FP_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fp_dbg("Capturing a 1st frame...");
+ fpi_ssm_next_state(ssm);
+ break;
+ case CAP_FP_GET_FP_REQ:
+ msg_get_fp(dev, 0x01, 0xF4, 0x02, 0x01, 0x64);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case CAP_FP_GET_FP_ANS:
+ memcpy(dev->fp + dev->fp_height * FE_WIDTH / 2, dev->ans,
+ FE_SIZE);
+ dev->fp_height += FE_HEIGHT;
+ if (dev->fp_height <= FE_HEIGHT) {
+ /* 2 lines are at least removed each time */
+ dev->fp_height -= 2;
+ fp_dbg("Capturing a 2nd frame...");
+ fpi_ssm_jump_to_state(ssm, CAP_FP_GET_FP_REQ);
+ } else {
+ struct fp_img *img;
+ unsigned int img_size;
+ /* Remove empty parts 2 times for the 2 frames */
+ process_remove_fp_end(dev);
+ process_remove_fp_end(dev);
+ img_size = dev->fp_height * FE_WIDTH;
+ img = fpi_img_new(img_size);
+ /* Images received are white on black, so invert it. */
+ /* TODO detect sweep direction */
+ img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
+ img->height = dev->fp_height;
+ process_4to8_bpp(dev->fp, img_size / 2, img->data);
+ fp_dbg("Sending the raw fingerprint image (%dx%d)",
+ img->width, img->height);
+ fpi_imgdev_image_captured(idev, img);
+ fpi_imgdev_report_finger_status(idev, FALSE);
+ fpi_ssm_mark_completed(ssm);
+ }
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+}
+
+static void m_capture_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (ssm->error) {
+ if (idev->action_state != IMG_ACQUIRE_STATE_DEACTIVATING) {
+ fp_err("Error while capturing fingerprint "
+ "(ssm->error=%d)", ssm->error);
+ fpi_imgdev_session_error(idev, ssm->error);
+ }
+ }
+ fpi_ssm_free(ssm);
+
+ if (dev->is_active == TRUE) {
+ fp_dbg("Device is still active, restarting finger detection");
+ m_start_fingerdetect(idev);
+ } else {
+ fp_dbg("And it's over.");
+ }
+}
+
+static void m_finger_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (dev->is_active == FALSE) {
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ switch (ssm->cur_state) {
+ case FGR_FPA_INIT_SET_MODE_SLEEP_REQ:
+ msg_set_mode_control(dev, REG_MODE_SLEEP);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_MODE_SLEEP_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_INIT_SET_DCOFFSET_REQ:
+ msg_set_regs(dev, 2, REG_DCOFFSET, dev->dcoffset);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_DCOFFSET_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_INIT_SET_GAINVRTVRB_REQ:
+ msg_set_regs(dev, 6, REG_GAIN, dev->gain, REG_VRT, dev->vrt,
+ REG_VRB, dev->vrb);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_GAINVRTVRB_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_INIT_SET_VCO_CONTROL_RT_REQ:
+ msg_set_regs(dev, 2, REG_VCO_CONTROL, REG_VCO_RT);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_VCO_CONTROL_RT_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_INIT_SET_REG04_REQ:
+ msg_set_regs(dev, 2, REG_04, 0x00);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_REG04_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_INIT_SET_MODE_SENSOR_REQ:
+ msg_set_mode_control(dev, REG_MODE_SENSOR);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_INIT_SET_MODE_SENSOR_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case FGR_FPA_GET_FRAME_REQ:
+ msg_get_frame(dev, 0x00, 0x00, 0x00, 0x00);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case FGR_FPA_GET_FRAME_ANS:
+ if (process_frame_empty((uint8_t *)dev->ans, FRAME_SIZE)) {
+ fpi_ssm_jump_to_state(ssm, FGR_FPA_GET_FRAME_REQ);
+ } else {
+ fpi_imgdev_report_finger_status(idev, TRUE);
+ fpi_ssm_mark_completed(ssm);
+ }
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+}
+
+static void m_finger_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (!ssm->error) {
+ struct fpi_ssm *ssm_cap;
+ ssm_cap = fpi_ssm_new(idev->dev, m_capture_state,
+ CAP_NUM_STATES);
+ ssm_cap->priv = idev;
+ fpi_ssm_start(ssm_cap, m_capture_complete);
+ } else {
+ if (idev->action_state != IMG_ACQUIRE_STATE_DEACTIVATING) {
+ fp_err("Error while capturing fingerprint "
+ "(ssm->error=%d)", ssm->error);
+ fpi_imgdev_session_error(idev, -4);
+ }
+ dev->is_active = FALSE;
+ }
+
+ fpi_ssm_free(ssm);
+}
+
+static void m_start_fingerdetect(struct fp_img_dev *idev)
+{
+ struct fpi_ssm *ssmf;
+ ssmf = fpi_ssm_new(idev->dev, m_finger_state, FGR_NUM_STATES);
+ ssmf->priv = idev;
+ fpi_ssm_start(ssmf, m_finger_complete);
+}
+
+/*
+ * Tune value of VRT and VRB for contrast and brightness.
+ */
+static void m_tunevrb_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+ float hist[5];
+
+ if (dev->is_active == FALSE) {
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ switch (ssm->cur_state) {
+ case TUNEVRB_INIT:
+ fp_dbg("Tuning of VRT/VRB");
+ assert(dev->dcoffset);
+ /* VRT(reg E1)=0x0A and VRB(reg E2)=0x10 are starting values */
+ dev->vrt = 0x0A;
+ dev->vrb = 0x10;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_GET_GAIN_REQ:
+ msg_get_regs(dev, 1, REG_GAIN);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_GET_GAIN_ANS:
+ if (msg_parse_regs(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_GET_DCOFFSET_REQ:
+ msg_get_regs(dev, 1, REG_DCOFFSET);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_GET_DCOFFSET_ANS:
+ if (msg_parse_regs(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_SET_DCOFFSET_REQ:
+ /* Reduce DCoffset by 1 to allow tuning */
+ msg_set_regs(dev, 2, REG_DCOFFSET, dev->dcoffset - 1);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_SET_DCOFFSET_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_FRAME_REQ:
+ fp_dbg("Testing VRT=0x%02X VRB=0x%02X", dev->vrt, dev->vrb);
+ msg_get_frame(dev, 0x01, dev->gain, dev->vrt, dev->vrb);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_FRAME_ANS:
+ process_hist((uint8_t *)dev->ans, FRAME_SIZE, hist);
+ /* Note that this tuning could probably be improved */
+ if (hist[0] + hist[1] > 0.95) {
+ if (dev->vrt <= 0 || dev->vrb <= 0) {
+ fp_dbg("Image is too dark, reducing DCOffset");
+ dev->dcoffset--;
+ fpi_ssm_jump_to_state(ssm, TUNEVRB_INIT);
+ } else {
+ dev->vrt--;
+ dev->vrb--;
+ fpi_ssm_jump_to_state(ssm, TUNEVRB_FRAME_REQ);
+ }
+ break;
+ }
+ if (hist[4] > 0.95) {
+ fp_dbg("Image is too bright, increasing DCOffset");
+ dev->dcoffset++;
+ fpi_ssm_jump_to_state(ssm, TUNEVRB_INIT);
+ break;
+ }
+ if (hist[4] + hist[3] > 0.4) {
+ if (dev->vrt >= 2 * dev->vrb - 0x0a) {
+ dev->vrt++; dev->vrb++;
+ } else {
+ dev->vrt++;
+ }
+ /* Check maximum for vrt/vrb */
+ /* TODO if maximum is reached, leave with an error? */
+ if (dev->vrt > VRT_MAX)
+ dev->vrt = VRT_MAX;
+ if (dev->vrb > VRB_MAX)
+ dev->vrb = VRB_MAX;
+ fpi_ssm_jump_to_state(ssm, TUNEVRB_FRAME_REQ);
+ break;
+ }
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_FINAL_SET_DCOFFSET_REQ:
+ fp_dbg("-> VRT=0x%02X VRB=0x%02X", dev->vrt, dev->vrb);
+ /* Reset the DCOffset */
+ msg_set_regs(dev, 2, REG_DCOFFSET, dev->dcoffset);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_FINAL_SET_DCOFFSET_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_FINAL_SET_REG2627_REQ:
+ /* In traces, REG_26/REG_27 are set. purpose? values? */
+ msg_set_regs(dev, 4, REG_26, 0x11, REG_27, 0x00);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_FINAL_SET_REG2627_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_FINAL_SET_GAINVRTVRB_REQ:
+ /* Set Gain/VRT/VRB values found */
+ msg_set_regs(dev, 6, REG_GAIN, dev->gain, REG_VRT, dev->vrt,
+ REG_VRB, dev->vrb);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_FINAL_SET_GAINVRTVRB_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ /* In traces, Gain/VRT/VRB are read again. */
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEVRB_FINAL_SET_MODE_SLEEP_REQ:
+ msg_set_mode_control(dev, REG_MODE_SLEEP);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEVRB_FINAL_SET_MODE_SLEEP_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_mark_completed(ssm);
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+}
+
+static void m_tunevrb_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+
+ fpi_imgdev_activate_complete(idev, ssm->error != 0);
+ if (!ssm->error) {
+ fp_dbg("Tuning is done. Starting finger detection.");
+ m_start_fingerdetect(idev);
+ } else {
+ struct etes603_dev *dev = idev->priv;
+ fp_err("Error while tuning VRT");
+ dev->is_active = FALSE;
+ reset_param(dev);
+ fpi_imgdev_session_error(idev, -3);
+ }
+ fpi_ssm_free(ssm);
+}
+
+/*
+ * This function tunes the DCoffset value and adjusts the gain value if
+ * required.
+ */
+static void m_tunedc_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (dev->is_active == FALSE) {
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ /* TODO To get better results, tuning could be done 3 times as in
+ * captured traffic to make sure that the value is correct. */
+ /* The default gain should work but it may reach a DCOffset limit so in
+ * this case we decrease the gain. */
+ switch (ssm->cur_state) {
+ case TUNEDC_INIT:
+ /* reg_e0 = 0x23 is sensor normal/small gain */
+ dev->gain = GAIN_SMALL_INIT;
+ dev->tunedc_min = DCOFFSET_MIN;
+ dev->tunedc_max = DCOFFSET_MAX;
+ fp_dbg("Tuning DCoffset");
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEDC_SET_DCOFFSET_REQ:
+ /* Dichotomic search to find at which value the frame becomes
+ * almost black. */
+ dev->dcoffset = (dev->tunedc_max + dev->tunedc_min) / 2;
+ fp_dbg("Testing DCoffset=0x%02X Gain=0x%02X", dev->dcoffset,
+ dev->gain);
+ msg_set_regs(dev, 2, REG_DCOFFSET, dev->dcoffset);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEDC_SET_DCOFFSET_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEDC_GET_FRAME_REQ:
+ /* vrt:0x15 vrb:0x10 are constant in all tuning frames. */
+ msg_get_frame(dev, 0x01, dev->gain, 0x15, 0x10);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEDC_GET_FRAME_ANS:
+ if (process_frame_empty((uint8_t *)dev->ans, FRAME_WIDTH))
+ dev->tunedc_max = dev->dcoffset;
+ else
+ dev->tunedc_min = dev->dcoffset;
+ if (dev->tunedc_min + 1 < dev->tunedc_max) {
+ fpi_ssm_jump_to_state(ssm, TUNEDC_SET_DCOFFSET_REQ);
+ } else if (dev->tunedc_max < DCOFFSET_MAX) {
+ dev->dcoffset = dev->tunedc_max + 1;
+ fpi_ssm_next_state(ssm);
+ } else {
+ dev->gain--;
+ fpi_ssm_jump_to_state(ssm, TUNEDC_SET_DCOFFSET_REQ);
+ }
+ break;
+ case TUNEDC_FINAL_SET_REG2122_REQ:
+ fp_dbg("-> DCoffset=0x%02X Gain=0x%02X", dev->dcoffset,
+ dev->gain);
+ /* ??? how reg21 / reg22 are calculated */
+ msg_set_regs(dev, 4, REG_21, 0x23, REG_22, 0x21);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEDC_FINAL_SET_REG2122_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEDC_FINAL_SET_GAIN_REQ:
+ msg_set_regs(dev, 2, REG_GAIN, dev->gain);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEDC_FINAL_SET_GAIN_ANS:
+ fpi_ssm_next_state(ssm);
+ break;
+ case TUNEDC_FINAL_SET_DCOFFSET_REQ:
+ msg_set_regs(dev, 2, REG_DCOFFSET, dev->dcoffset);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case TUNEDC_FINAL_SET_DCOFFSET_ANS:
+ /* In captured traffic, read GAIN, VRT, and VRB registers. */
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_mark_completed(ssm);
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+
+}
+
+static void m_tunedc_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ if (!ssm->error) {
+ struct fpi_ssm *ssm_tune;
+ ssm_tune = fpi_ssm_new(idev->dev, m_tunevrb_state,
+ TUNEVRB_NUM_STATES);
+ ssm_tune->priv = idev;
+ fpi_ssm_start(ssm_tune, m_tunevrb_complete);
+ } else {
+ struct etes603_dev *dev = idev->priv;
+ fp_err("Error while tuning DCOFFSET");
+ dev->is_active = FALSE;
+ reset_param(dev);
+ fpi_imgdev_session_error(idev, -2);
+ }
+ fpi_ssm_free(ssm);
+}
+
+static void m_init_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ struct etes603_dev *dev = idev->priv;
+
+ if (dev->is_active == FALSE) {
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ switch (ssm->cur_state) {
+ case INIT_CHECK_INFO_REQ:
+ msg_get_regs(dev, 4, REG_INFO0, REG_INFO1, REG_INFO2,
+ REG_INFO3);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_CHECK_INFO_ANS:
+ if (msg_parse_regs(dev))
+ goto err;
+ if (check_info(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case INIT_CMD20_REQ:
+ msg_get_cmd20(dev);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_CMD20_ANS:
+ if (msg_check_cmd20(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case INIT_CMD25_REQ:
+ msg_get_cmd25(dev);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_CMD25_ANS:
+ if (msg_check_cmd25(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case INIT_SENSOR_REQ:
+ /* In captured traffic, those are splitted. */
+ msg_set_regs(dev, 18, REG_MODE_CONTROL, REG_MODE_SLEEP,
+ REG_50, 0x0F, REG_GAIN, 0x04, REG_VRT, 0x08,
+ REG_VRB, 0x0D, REG_VCO_CONTROL, REG_VCO_RT,
+ REG_DCOFFSET, 0x36, REG_F0, 0x00, REG_F2, 0x00);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_SENSOR_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case INIT_ENC_REQ:
+ /* Initialize encryption registers without encryption. */
+ /* Set registers from 0x41 to 0x48 (0x8 regs) */
+ msg_set_regs(dev, 16, REG_ENC1, 0x12, REG_ENC2, 0x34,
+ REG_ENC3, 0x56, REG_ENC4, 0x78, REG_ENC5, 0x90,
+ REG_ENC6, 0xAB, REG_ENC7, 0xCD, REG_ENC8, 0xEF);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_ENC_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_next_state(ssm);
+ break;
+ case INIT_REGS_REQ:
+ /* Set register from 0x20 to 0x37 (0x18 regs) */
+ msg_set_regs(dev, 48,
+ REG_20, 0x00, REG_21, 0x23, REG_22, 0x21, REG_23, 0x20,
+ REG_24, 0x14, REG_25, 0x6A, REG_26, 0x00, REG_27, 0x00,
+ REG_28, 0x00, REG_29, 0xC0, REG_2A, 0x50, REG_2B, 0x50,
+ REG_2C, 0x4D, REG_2D, 0x03, REG_2E, 0x06, REG_2F, 0x06,
+ REG_30, 0x10, REG_31, 0x02, REG_32, 0x14, REG_33, 0x34,
+ REG_34, 0x01, REG_35, 0x08, REG_36, 0x03, REG_37, 0x21);
+ if (async_tx(idev, EP_OUT, async_tx_cb, ssm))
+ goto err;
+ break;
+ case INIT_REGS_ANS:
+ if (msg_check_ok(dev))
+ goto err;
+ fpi_ssm_mark_completed(ssm);
+ break;
+ default:
+ fp_err("Unknown state %d", ssm->cur_state);
+ goto err;
+ break;
+ }
+
+ return;
+err:
+ fpi_ssm_mark_aborted(ssm, -EIO);
+
+}
+
+static void m_init_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *idev = ssm->priv;
+ if (!ssm->error) {
+ struct fpi_ssm *ssm_tune;
+ ssm_tune = fpi_ssm_new(idev->dev, m_tunedc_state,
+ TUNEDC_NUM_STATES);
+ ssm_tune->priv = idev;
+ fpi_ssm_start(ssm_tune, m_tunedc_complete);
+ } else {
+ struct etes603_dev *dev = idev->priv;
+ fp_err("Error initializing the device");
+ dev->is_active = FALSE;
+ reset_param(dev);
+ fpi_imgdev_session_error(idev, -1);
+ }
+ fpi_ssm_free(ssm);
+}
+
+static int dev_activate(struct fp_img_dev *idev, enum fp_imgdev_state state)
+{
+ struct etes603_dev *dev = idev->priv;
+ struct fpi_ssm *ssm;
+
+ assert(dev);
+
+ if (state != IMGDEV_STATE_AWAIT_FINGER_ON) {
+ fp_err("The driver is in an unexpected state: %d.", state);
+ fpi_imgdev_activate_complete(idev, 1);
+ return -1;
+ }
+
+ /* Reset info and data */
+ dev->is_active = TRUE;
+
+ if (dev->dcoffset == 0) {
+ fp_dbg("Tuning device...");
+ ssm = fpi_ssm_new(idev->dev, m_init_state, INIT_NUM_STATES);
+ ssm->priv = idev;
+ fpi_ssm_start(ssm, m_init_complete);
+ } else {
+ fp_dbg("Using previous tuning (DCOFFSET=0x%02X,VRT=0x%02X,"
+ "VRB=0x%02X,GAIN=0x%02X).", dev->dcoffset, dev->vrt,
+ dev->vrb, dev->gain);
+ fpi_imgdev_activate_complete(idev, 0);
+ ssm = fpi_ssm_new(idev->dev, m_finger_state, FGR_NUM_STATES);
+ ssm->priv = idev;
+ fpi_ssm_start(ssm, m_finger_complete);
+ }
+ return 0;
+}
+
+static void dev_deactivate(struct fp_img_dev *idev)
+{
+ struct etes603_dev *dev = idev->priv;
+
+ fp_dbg("deactivating");
+
+ /* this can be called even if still activated. */
+ if (dev->is_active == TRUE) {
+ dev->is_active = FALSE;
+ m_exit_start(idev);
+ }
+}
+
+static int dev_open(struct fp_img_dev *idev, unsigned long driver_data)
+{
+ int ret;
+ struct etes603_dev *dev;
+
+ dev = g_malloc0(sizeof(struct etes603_dev));
+ idev->priv = dev;
+
+ dev->req = g_malloc(sizeof(struct egis_msg));
+ dev->ans = g_malloc(FE_SIZE);
+ dev->fp = g_malloc(FE_SIZE * 4);
+
+ ret = libusb_claim_interface(idev->udev, 0);
+ if (ret != LIBUSB_SUCCESS) {
+ fp_err("libusb_claim_interface failed on interface 0 "
+ "(err=%d)", ret);
+ return ret;
+ }
+
+ fpi_imgdev_open_complete(idev, 0);
+ return 0;
+}
+
+static void dev_close(struct fp_img_dev *idev)
+{
+ struct etes603_dev *dev = idev->priv;
+
+ g_free(dev->req);
+ g_free(dev->ans);
+ g_free(dev->fp);
+ g_free(dev);
+
+ libusb_release_interface(idev->udev, 0);
+ fpi_imgdev_close_complete(idev);
+}
+
+static const struct usb_id id_table[] = {
+ /* EgisTec (aka Lightuning) ES603 */
+ { .vendor = 0x1c7a, .product = 0x0603},
+ { 0, 0, 0, },
+};
+
+struct fp_img_driver etes603_driver = {
+ .driver = {
+ .id = ETES603_ID,
+ .name = FP_COMPONENT,
+ .full_name = "EgisTec ES603",
+ .id_table = id_table,
+ .scan_type = FP_SCAN_TYPE_SWIPE,
+ },
+ .flags = 0,
+ .img_height = -1,
+ .img_width = 256,
+
+ .open = dev_open,
+ .close = dev_close,
+ .activate = dev_activate,
+ .deactivate = dev_deactivate,
+};
+
diff --git a/libfprint/drivers/upeke2.c b/libfprint/drivers/upeke2.c
index a7db54d..f685205 100644
--- a/libfprint/drivers/upeke2.c
+++ b/libfprint/drivers/upeke2.c
@@ -48,7 +48,6 @@
enum {
UPEKE2_2016,
- UPEKE2_2020,
};
struct upeke2_dev {
@@ -856,9 +855,6 @@ static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
return 1;
- if (dsc->idProduct == 0x2020 && dsc->bcdDevice == 1)
- return 1;
-
return 0;
}
@@ -1076,6 +1072,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct fp_print_data *fdata = NULL;
+ struct fp_print_data_item *item = NULL;
int result = -EPROTO;
if (data_len < sizeof(scan_comp)) {
@@ -1084,9 +1081,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
fp_err("unrecognised data prefix %x %x %x %x %x",
data[0], data[1], data[2], data[3], data[4]);
} else {
- fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
- memcpy(fdata->data, data + sizeof(scan_comp),
+ fdata = fpi_print_data_new(dev);
+ item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
+ memcpy(item->data, data + sizeof(scan_comp),
data_len - sizeof(scan_comp));
+ fdata->prints = g_slist_prepend(fdata->prints, item);
result = FP_ENROLL_COMPLETE;
}
@@ -1248,12 +1247,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
break;
case VERIFY_INIT: ;
struct fp_print_data *print = dev->verify_data;
- size_t data_len = sizeof(verify_hdr) + print->length;
+ struct fp_print_data_item *item = print->prints->data;
+ size_t data_len = sizeof(verify_hdr) + item->length;
unsigned char *data = g_malloc(data_len);
struct libusb_transfer *transfer;
memcpy(data, verify_hdr, sizeof(verify_hdr));
- memcpy(data + sizeof(verify_hdr), print->data, print->length);
+ memcpy(data + sizeof(verify_hdr), item->data, item->length);
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
verify_init_2803_cb, ssm);
g_free(data);
@@ -1461,7 +1461,6 @@ static int verify_stop(struct fp_dev *dev, gboolean iterating)
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKE2_2016 },
- { .vendor = 0x147e, .product = 0x2020, .driver_data = UPEKE2_2020 },
{ 0, 0, 0, }, /* terminating entry */
};
diff --git a/libfprint/drivers/upektc_img.c b/libfprint/drivers/upektc_img.c
new file mode 100644
index 0000000..9bf099d
--- /dev/null
+++ b/libfprint/drivers/upektc_img.c
@@ -0,0 +1,659 @@
+/*
+ * UPEK TouchChip driver for libfprint
+ * Copyright (C) 2013 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 "upekts_img"
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include "upektc_img.h"
+#include "driver_ids.h"
+
+static void start_capture(struct fp_img_dev *dev);
+static void start_deactivation(struct fp_img_dev *dev);
+
+#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
+#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
+#define CTRL_TIMEOUT 4000
+#define BULK_TIMEOUT 4000
+
+#define IMAGE_WIDTH 144
+#define IMAGE_HEIGHT 384
+#define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT)
+
+#define MAX_CMD_SIZE 64
+#define MAX_RESPONSE_SIZE 2052
+#define SHORT_RESPONSE_SIZE 64
+
+struct upekts_img_dev {
+ unsigned char cmd[MAX_CMD_SIZE];
+ unsigned char response[MAX_RESPONSE_SIZE];
+ unsigned char image_bits[IMAGE_SIZE * 2];
+ unsigned char seq;
+ size_t image_size;
+ size_t response_rest;
+ gboolean deactivating;
+};
+
+/****** HELPERS ******/
+
+static const uint16_t crc_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+static uint16_t udf_crc(unsigned char *buffer, size_t size)
+{
+ uint16_t crc = 0;
+ while (size--)
+ crc = (uint16_t) ((crc << 8) ^
+ crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]);
+ return crc;
+}
+
+static void upektc_img_cmd_fix_seq(unsigned char *cmd_buf, unsigned char seq)
+{
+ uint8_t byte;
+
+ byte = cmd_buf[5];
+ byte &= 0x0f;
+ byte |= (seq << 4);
+ cmd_buf[5] = byte;
+}
+
+static void upektc_img_cmd_update_crc(unsigned char *cmd_buf, size_t size)
+{
+ /* CRC does not cover Ciao prefix (4 bytes) and CRC location (2 bytes) */
+ uint16_t crc = udf_crc(cmd_buf + 4, size - 6);
+
+ cmd_buf[size - 2] = (crc & 0x00ff);
+ cmd_buf[size - 1] = (crc & 0xff00) >> 8;
+}
+
+static void upektc_img_submit_req(struct fpi_ssm *ssm,
+ const unsigned char *buf, size_t buf_size, unsigned char seq,
+ libusb_transfer_cb_fn cb)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ int r;
+
+ BUG_ON(buf_size > MAX_CMD_SIZE);
+
+ if (!transfer) {
+ fpi_ssm_mark_aborted(ssm, -ENOMEM);
+ return;
+ }
+
+ transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
+
+ memcpy(upekdev->cmd, buf, buf_size);
+ upektc_img_cmd_fix_seq(upekdev->cmd, seq);
+ upektc_img_cmd_update_crc(upekdev->cmd, buf_size);
+
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, upekdev->cmd, buf_size,
+ cb, ssm, BULK_TIMEOUT);
+
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ libusb_free_transfer(transfer);
+ fpi_ssm_mark_aborted(ssm, r);
+ }
+}
+
+static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t buf_offset, libusb_transfer_cb_fn cb)
+{
+ struct libusb_transfer *transfer = libusb_alloc_transfer(0);
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ int r;
+
+ if (!transfer) {
+ fpi_ssm_mark_aborted(ssm, -ENOMEM);
+ return;
+ }
+
+ BUG_ON(buf_size > MAX_RESPONSE_SIZE);
+
+ transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
+
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, upekdev->response + buf_offset, buf_size,
+ cb, ssm, BULK_TIMEOUT);
+
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ libusb_free_transfer(transfer);
+ fpi_ssm_mark_aborted(ssm, r);
+ }
+}
+
+/****** CAPTURE ******/
+
+enum capture_states {
+ CAPTURE_INIT_CAPTURE,
+ CAPTURE_READ_DATA,
+ CAPTURE_ACK_00_28,
+ CAPTURE_ACK_08,
+ CAPTURE_ACK_FRAME,
+ CAPTURE_NUM_STATES,
+};
+
+static void capture_reqs_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
+ (transfer->length == transfer->actual_length)) {
+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+static int upektc_img_process_image_frame(unsigned char *image_buf, unsigned char *cmd_res)
+{
+ int offset = 8;
+ int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]);
+
+ len -= 1;
+ if (cmd_res[7] == 0x2c) {
+ len -= 10;
+ offset += 10;
+ }
+ if (cmd_res[7] == 0x20) {
+ len -= 4;
+ }
+ memcpy(image_buf, cmd_res + offset, len);
+
+ return len;
+}
+
+static void capture_read_data_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ unsigned char *data = upekdev->response;
+ struct fp_img *img;
+ size_t response_size;
+
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ fp_dbg("request is not completed, %d", transfer->status);
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ return;
+ }
+
+ if (upekdev->deactivating) {
+ fp_dbg("Deactivate requested\n");
+ fpi_ssm_mark_completed(ssm);
+ return;
+ }
+
+ fp_dbg("request completed, len: %.4x", transfer->actual_length);
+ if (transfer->actual_length == 0) {
+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
+ return;
+ }
+
+ if (!upekdev->response_rest) {
+ response_size = ((data[5] & 0x0f) << 8) + data[6];
+ response_size += 9; /* 7 bytes for header, 2 for CRC */
+ if (response_size > transfer->actual_length) {
+ fp_dbg("response_size is %d, actual_length is %d\n",
+ response_size, transfer->actual_length);
+ fp_dbg("Waiting for rest of transfer");
+ BUG_ON(upekdev->response_rest);
+ upekdev->response_rest = response_size - transfer->actual_length;
+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
+ return;
+ }
+ }
+ upekdev->response_rest = 0;
+
+ switch (data[4]) {
+ case 0x00:
+ switch (data[7]) {
+ /* No finger */
+ case 0x28:
+ fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
+ break;
+ /* Image frame with additional info */
+ case 0x2c:
+ fpi_imgdev_report_finger_status(dev, TRUE);
+ /* Plain image frame */
+ case 0x24:
+ upekdev->image_size +=
+ upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
+ data);
+ fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_FRAME);
+ break;
+ /* Last image frame */
+ case 0x20:
+ upekdev->image_size +=
+ upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
+ data);
+ BUG_ON(upekdev->image_size != IMAGE_SIZE);
+ fp_dbg("Image size is %d\n", upekdev->image_size);
+ img = fpi_img_new(IMAGE_SIZE);
+ memcpy(img->data, upekdev->image_bits, IMAGE_SIZE);
+ fpi_imgdev_image_captured(dev, img);
+ fpi_imgdev_report_finger_status(dev, FALSE);
+ fpi_ssm_mark_completed(ssm);
+ break;
+ default:
+ fp_err("Uknown response!\n");
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ break;
+ }
+ break;
+ case 0x08:
+ fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_08);
+ break;
+ default:
+ fp_err("Not handled response!\n");
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+static void capture_run_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+
+ switch (ssm->cur_state) {
+ case CAPTURE_INIT_CAPTURE:
+ upektc_img_submit_req(ssm, upek2020_init_capture, sizeof(upek2020_init_capture),
+ upekdev->seq, capture_reqs_cb);
+ upekdev->seq++;
+ break;
+ case CAPTURE_READ_DATA:
+ if (!upekdev->response_rest)
+ upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
+ else
+ upektc_img_read_data(ssm, MAX_RESPONSE_SIZE - SHORT_RESPONSE_SIZE,
+ SHORT_RESPONSE_SIZE, capture_read_data_cb);
+ break;
+ case CAPTURE_ACK_00_28:
+ upektc_img_submit_req(ssm, upek2020_ack_00_28, sizeof(upek2020_ack_00_28),
+ upekdev->seq, capture_reqs_cb);
+ upekdev->seq++;
+ break;
+ case CAPTURE_ACK_08:
+ upektc_img_submit_req(ssm, upek2020_ack_08, sizeof(upek2020_ack_08),
+ 0, capture_reqs_cb);
+ break;
+ case CAPTURE_ACK_FRAME:
+ upektc_img_submit_req(ssm, upek2020_ack_frame, sizeof(upek2020_ack_frame),
+ upekdev->seq, capture_reqs_cb);
+ upekdev->seq++;
+ break;
+ };
+}
+
+static void capture_sm_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ int err = ssm->error;
+
+ fp_dbg("Capture completed, %d", err);
+ fpi_ssm_free(ssm);
+
+ if (upekdev->deactivating)
+ start_deactivation(dev);
+ else if (err)
+ fpi_imgdev_session_error(dev, err);
+ else
+ start_capture(dev);
+}
+
+static void start_capture(struct fp_img_dev *dev)
+{
+ struct upekts_img_dev *upekdev = dev->priv;
+ struct fpi_ssm *ssm;
+
+ upekdev->image_size = 0;
+
+ ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
+ ssm->priv = dev;
+ fpi_ssm_start(ssm, capture_sm_complete);
+}
+
+/****** INITIALIZATION/DEINITIALIZATION ******/
+
+enum deactivate_states {
+ DEACTIVATE_DEINIT,
+ DEACTIVATE_READ_DEINIT_DATA,
+ DEACTIVATE_NUM_STATES,
+};
+
+static void deactivate_reqs_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
+ (transfer->length == transfer->actual_length)) {
+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+/* TODO: process response properly */
+static void deactivate_read_data_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ fpi_ssm_mark_completed(ssm);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+static void deactivate_run_state(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+
+ switch (ssm->cur_state) {
+ case DEACTIVATE_DEINIT:
+ upektc_img_submit_req(ssm, upek2020_deinit, sizeof(upek2020_deinit),
+ upekdev->seq, deactivate_reqs_cb);
+ upekdev->seq++;
+ break;
+ case DEACTIVATE_READ_DEINIT_DATA:
+ upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, deactivate_read_data_cb);
+ break;
+ };
+}
+
+static void deactivate_sm_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ int err = ssm->error;
+
+ fp_dbg("Deactivate completed");
+ fpi_ssm_free(ssm);
+
+ if (err) {
+ fpi_imgdev_session_error(dev, err);
+ return;
+ }
+
+ upekdev->deactivating = FALSE;
+ fpi_imgdev_deactivate_complete(dev);
+}
+
+static void start_deactivation(struct fp_img_dev *dev)
+{
+ struct upekts_img_dev *upekdev = dev->priv;
+ struct fpi_ssm *ssm;
+
+ upekdev->image_size = 0;
+
+ ssm = fpi_ssm_new(dev->dev, deactivate_run_state, DEACTIVATE_NUM_STATES);
+ ssm->priv = dev;
+ fpi_ssm_start(ssm, deactivate_sm_complete);
+}
+
+enum activate_states {
+ ACTIVATE_CONTROL_REQ_1,
+ ACTIVATE_READ_CTRL_RESP_1,
+ ACTIVATE_INIT_1,
+ ACTIVATE_READ_INIT_1_RESP,
+ ACTIVATE_INIT_2,
+ ACTIVATE_READ_INIT_2_RESP,
+ ACTIVATE_CONTROL_REQ_2,
+ ACTIVATE_READ_CTRL_RESP_2,
+ ACTIVATE_INIT_3,
+ ACTIVATE_READ_INIT_3_RESP,
+ ACTIVATE_INIT_4,
+ ACTIVATE_READ_INIT_4_RESP,
+ ACTIVATE_NUM_STATES,
+};
+
+static void init_reqs_ctrl_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ fpi_ssm_next_state(ssm);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+static void init_reqs_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
+ (transfer->length == transfer->actual_length)) {
+ fpi_ssm_next_state(ssm);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+/* TODO: process response properly */
+static void init_read_data_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+
+ if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
+ fpi_ssm_next_state(ssm);
+ } else {
+ fpi_ssm_mark_aborted(ssm, -EIO);
+ }
+}
+
+static void activate_run_state(struct fpi_ssm *ssm)
+{
+ struct libusb_transfer *transfer;
+ struct fp_img_dev *dev = ssm->priv;
+ struct upekts_img_dev *upekdev = dev->priv;
+ int r;
+
+ switch (ssm->cur_state) {
+ case ACTIVATE_CONTROL_REQ_1:
+ case ACTIVATE_CONTROL_REQ_2:
+ {
+ unsigned char *data;
+
+ transfer = libusb_alloc_transfer(0);
+ if (!transfer) {
+ fpi_ssm_mark_aborted(ssm, -ENOMEM);
+ break;
+ }
+ transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER |
+ LIBUSB_TRANSFER_FREE_TRANSFER;
+
+ data = g_malloc0(LIBUSB_CONTROL_SETUP_SIZE + 1);
+ libusb_fill_control_setup(data,
+ LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x0c, 0x100, 0x0400, 1);
+ libusb_fill_control_transfer(transfer, ssm->dev->udev, data,
+ init_reqs_ctrl_cb, ssm, CTRL_TIMEOUT);
+ r = libusb_submit_transfer(transfer);
+ if (r < 0) {
+ g_free(data);
+ libusb_free_transfer(transfer);
+ fpi_ssm_mark_aborted(ssm, r);
+ }
+ }
+ break;
+ case ACTIVATE_INIT_1:
+ upektc_img_submit_req(ssm, upek2020_init_1, sizeof(upek2020_init_1),
+ 0, init_reqs_cb);
+ break;
+ case ACTIVATE_INIT_2:
+ upektc_img_submit_req(ssm, upek2020_init_2, sizeof(upek2020_init_2),
+ 0, init_reqs_cb);
+ break;
+ case ACTIVATE_INIT_3:
+ upektc_img_submit_req(ssm, upek2020_init_3, sizeof(upek2020_init_3),
+ 0, init_reqs_cb);
+ break;
+ case ACTIVATE_INIT_4:
+ upektc_img_submit_req(ssm, upek2020_init_4, sizeof(upek2020_init_4),
+ upekdev->seq, init_reqs_cb);
+ /* Seq should be updated after 4th init */
+ upekdev->seq++;
+ break;
+ case ACTIVATE_READ_CTRL_RESP_1:
+ case ACTIVATE_READ_CTRL_RESP_2:
+ case ACTIVATE_READ_INIT_1_RESP:
+ case ACTIVATE_READ_INIT_2_RESP:
+ case ACTIVATE_READ_INIT_3_RESP:
+ case ACTIVATE_READ_INIT_4_RESP:
+ upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, init_read_data_cb);
+ break;
+ }
+}
+
+static void activate_sm_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ int err = ssm->error;
+
+ fpi_ssm_free(ssm);
+ fp_dbg("%s status %d", __func__, err);
+ fpi_imgdev_activate_complete(dev, err);
+
+ if (!err)
+ start_capture(dev);
+}
+
+static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
+{
+ struct upekts_img_dev *upekdev = dev->priv;
+ struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
+ ACTIVATE_NUM_STATES);
+ ssm->priv = dev;
+ upekdev->seq = 0;
+ fpi_ssm_start(ssm, activate_sm_complete);
+ return 0;
+}
+
+static void dev_deactivate(struct fp_img_dev *dev)
+{
+ struct upekts_img_dev *upekdev = dev->priv;
+
+ upekdev->deactivating = TRUE;
+}
+
+static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
+{
+ /* TODO check that device has endpoints we're using */
+ int r;
+
+ r = libusb_claim_interface(dev->udev, 0);
+ if (r < 0) {
+ fp_err("could not claim interface 0");
+ return r;
+ }
+
+ dev->priv = g_malloc0(sizeof(struct upekts_img_dev));
+ fpi_imgdev_open_complete(dev, 0);
+ return 0;
+}
+
+static void dev_deinit(struct fp_img_dev *dev)
+{
+ g_free(dev->priv);
+ libusb_release_interface(dev->udev, 0);
+ fpi_imgdev_close_complete(dev);
+}
+
+static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
+{
+ if (dsc->idProduct == 0x2020 && dsc->bcdDevice == 1)
+ return 1;
+#ifndef ENABLE_UPEKE2
+ if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
+ return 1;
+#endif
+
+ return 0;
+}
+
+static const struct usb_id id_table[] = {
+#ifndef ENABLE_UPEKE2
+ { .vendor = 0x147e, .product = 0x2016 },
+#endif
+ { .vendor = 0x147e, .product = 0x2020 },
+ { 0, 0, 0, },
+};
+
+struct fp_img_driver upektc_img_driver = {
+ .driver = {
+ .id = UPEKTC_IMG_ID,
+ .name = FP_COMPONENT,
+ .full_name = "Upek TouchChip Fingerprint Coprocessor",
+ .id_table = id_table,
+ .scan_type = FP_SCAN_TYPE_SWIPE,
+ .discover = discover,
+ },
+ .flags = 0,
+ .img_height = IMAGE_HEIGHT,
+ .img_width = IMAGE_WIDTH,
+ .bz3_threshold = 70,
+
+ .open = dev_init,
+ .close = dev_deinit,
+ .activate = dev_activate,
+ .deactivate = dev_deactivate,
+};
diff --git a/libfprint/drivers/upektc_img.h b/libfprint/drivers/upektc_img.h
new file mode 100644
index 0000000..6146557
--- /dev/null
+++ b/libfprint/drivers/upektc_img.h
@@ -0,0 +1,144 @@
+/*
+ * Upek TouchChip Fingerprint Coprocessor definitions
+ * Copyright (c) 2013 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 __UPEKTC_IMG_H
+#define __UPEKTC_IMG_H
+
+static const unsigned char upek2020_init_1[] = {
+'C', 'i', 'a', 'o',
+0x04,
+0x00, 0x0d,
+0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x01, 0x00, 0x00, 0x00,
+0xda, 0xc1
+};
+
+static const unsigned char upek2020_init_2[] = {
+0x43, 0x69, 0x61, 0x6f,
+0x07,
+0x00, 0x01,
+0x01,
+0x3d, 0x72
+};
+
+static const unsigned char upek2020_init_3[] = {
+'C', 'i', 'a', 'o',
+0x04,
+0x00, 0x0d,
+0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
+0x01, 0x00, 0x00, 0x00,
+0x55, 0x2f
+};
+
+static const unsigned char upek2020_init_4[] = {
+'C', 'i', 'a', 'o',
+0x00,
+0x00, 0x07,
+0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04,
+0xc0, 0xd6
+};
+
+static const unsigned char upek2020_deinit[] = {
+'C', 'i', 'a', 'o',
+0x07,
+0x00, 0x01,
+0x01,
+0x3d,
+0x72
+};
+
+static const unsigned char upek2020_init_capture[] = {
+'C', 'i', 'a', 'o',
+0x00,
+0x00, 0x0e, /* Seq = 7, len = 0x00e */
+0x28, /* CMD = 0x28 */
+0x0b, 0x00, /* Inner len = 0x000b */
+0x00, 0x00,
+0x0e, /* SUBCMD = 0x0e */
+0x02,
+0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */
+0x02,
+0x00, /* Wait for acceptable finger */
+0x02,
+0x14, 0x9a /* CRC */
+};
+
+#if 0
+static const unsigned char finger_status[] = {
+'C', 'i', 'a', 'o',
+0x00,
+0x70, 0x14, /* Seq = 7, len = 0x014 */
+0x28, /* CMD = 0x28 */
+0x11, 0x00, /* Inner len = 0x0011 */
+0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
+0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00,
+0x26, 0x03, /* CRC */
+0x00, 0x00, 0x00, /* Rest is garbage */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+#endif
+
+static const unsigned char upek2020_ack_00_28[] = {
+'C', 'i', 'a', 'o',
+0x00,
+0x80, 0x08, /* Seq = 8, len = 0x008 */
+0x28, /* CMD = 0x28 */
+0x05, 0x00, /* Inner len = 0x0005 */
+0x00, 0x00, 0x00, 0x30, 0x01,
+0x6a, 0xc4 /* CRC */
+};
+
+#if 0
+/* No seq should be tracked here */
+static const unsigned char got_finger[] = {
+'C', 'i', 'a', 'o',
+0x08,
+0x00, 0x00, /* Seq = 0, len = 0x000 */
+0xa1, 0xa9, /* CRC */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+#endif
+
+/* No seq should be put in there */
+static const unsigned char upek2020_ack_08[] = {
+'C', 'i', 'a', 'o',
+0x09,
+0x00, 0x00, /* Seq = 0, len = 0x0 */
+0x91, 0x9e /* CRC */
+};
+
+static const unsigned char upek2020_ack_frame[] = {
+'C', 'i', 'a', 'o',
+0x00,
+0x50, 0x01, /* Seq = 5, len = 0x001 */
+0x30,
+0xac, 0x5b /* CRC */
+};
+
+#endif
diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c
index b347949..c191a2d 100644
--- a/libfprint/drivers/upekts.c
+++ b/libfprint/drivers/upekts.c
@@ -1077,6 +1077,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct fp_print_data *fdata = NULL;
+ struct fp_print_data_item *item = NULL;
int result = -EPROTO;
if (data_len < sizeof(scan_comp)) {
@@ -1085,9 +1086,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
fp_err("unrecognised data prefix %x %x %x %x %x",
data[0], data[1], data[2], data[3], data[4]);
} else {
- fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
- memcpy(fdata->data, data + sizeof(scan_comp),
+ fdata = fpi_print_data_new(dev);
+ item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
+ memcpy(item->data, data + sizeof(scan_comp),
data_len - sizeof(scan_comp));
+ fdata->prints = g_slist_prepend(fdata->prints, item);
result = FP_ENROLL_COMPLETE;
}
@@ -1249,12 +1252,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
break;
case VERIFY_INIT: ;
struct fp_print_data *print = dev->verify_data;
- size_t data_len = sizeof(verify_hdr) + print->length;
+ struct fp_print_data_item *item = print->prints->data;
+ size_t data_len = sizeof(verify_hdr) + item->length;
unsigned char *data = g_malloc(data_len);
struct libusb_transfer *transfer;
memcpy(data, verify_hdr, sizeof(verify_hdr));
- memcpy(data + sizeof(verify_hdr), print->data, print->length);
+ memcpy(data + sizeof(verify_hdr), item->data, item->length);
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
verify_init_2803_cb, ssm);
g_free(data);
diff --git a/libfprint/drivers/vfs0050.c b/libfprint/drivers/vfs0050.c
new file mode 100644
index 0000000..e31d5bb
--- /dev/null
+++ b/libfprint/drivers/vfs0050.c
@@ -0,0 +1,587 @@
+#define FP_COMPONENT "vfs0050"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "vfs0050.h"
+#include "driver_ids.h"
+
+static int is_noise(struct vfs0050_line *a)
+{
+ int i;
+ int total_white = 0;
+ int total_black = 0;
+ for (i = 0; i < VFS0050_IMG_WIDTH; i++) {
+ if (a->row[i] >= WHITE_THRESH)
+ total_white++;
+ if (a->row[i] <= BLACK_THRESH)
+ total_black++;
+ }
+ if (total_black < 5 && total_white < 5) {
+ return 1;
+ }
+ return 0;
+}
+
+static void process_image_data(struct fp_img_dev *dev, char **output, int *output_height)
+{
+ //pixman stuff taken from libfprint/pixman.c, adapted for my purposes.
+ pixman_image_t *orig, *resized;
+ pixman_transform_t transform;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ struct vfs0050_line *line, *calibration_line;
+ char *buf = malloc(vfs_dev->scanbuf_idx);
+ int lines = vfs_dev->scanbuf_idx / VFS0050_FRAME_SIZE;
+ int i, x, sum, last_sum, diff;
+ int new_height;
+ //just grab one around middle, there should be 100
+ calibration_line = (struct vfs0050_line *) ((char *) vfs_dev->calbuf + (50 * VFS0050_FRAME_SIZE));
+
+ new_height = 0;
+ for (i = 0; i < lines; i++) {
+ line = (struct vfs0050_line *) ((char *) vfs_dev->scanbuf + (i * VFS0050_FRAME_SIZE));
+ if (!is_noise(line))
+ memcpy(buf + (new_height++ * VFS0050_IMG_WIDTH), line->row, VFS0050_IMG_WIDTH);
+ else
+ fp_dbg("removed noise at line: %d\n", i);
+ }
+
+ orig = pixman_image_create_bits(PIXMAN_a8, VFS0050_IMG_WIDTH, new_height, (uint32_t *) buf, VFS0050_IMG_WIDTH);
+ new_height *= VFS0050_SCALE_FACTOR; //scale for resized image
+ resized = pixman_image_create_bits(PIXMAN_a8, VFS0050_IMG_WIDTH, new_height, NULL, VFS0050_IMG_WIDTH);
+ pixman_transform_init_identity(&transform);
+ pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(1), pixman_double_to_fixed(0.2));
+ pixman_image_set_transform(orig, &transform);
+ pixman_image_set_filter(orig, PIXMAN_FILTER_BEST, NULL, 0);
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ orig,
+ NULL,
+ resized,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ VFS0050_IMG_WIDTH, new_height
+ );
+ memcpy(buf, pixman_image_get_data(resized), VFS0050_IMG_WIDTH * new_height);
+
+ pixman_image_unref(orig);
+ pixman_image_unref(resized);
+
+ *output_height = new_height;
+ *output = buf;
+}
+
+static void tmp_writeout_buf(struct fp_img_dev *dev)
+{
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ FILE *fp = fopen("/tmp/test.pgm", "w");
+ if (!fp) {
+ return;
+ }
+ struct vfs0050_line *line;
+ int i, x;
+ char tmpbuf[100];
+ fwrite("P2\n", 3, 1, fp);
+ sprintf(tmpbuf, "%d %d\n", VFS0050_IMG_WIDTH + 32, (vfs_dev->scanbuf_idx / VFS0050_FRAME_SIZE));
+ fwrite(tmpbuf, strlen(tmpbuf), 1, fp);
+ fwrite("255\n", 4, 1, fp);
+ for (i = 0; i < (vfs_dev->scanbuf_idx / VFS0050_FRAME_SIZE); i++) {
+ line = (struct vfs0050_line *) ((char *) vfs_dev->scanbuf + (i * VFS0050_FRAME_SIZE));
+ for (x = 0; x < VFS0050_IMG_WIDTH + 32; x++) {
+ sprintf(tmpbuf, "%d\t", line->row[x] & 0xff);
+ fwrite(tmpbuf, strlen(tmpbuf), 1, fp);
+ }
+ fwrite("\n", 1, 1, fp);
+ }
+ fclose(fp);
+}
+
+static int submit_image(struct fp_img_dev *dev)
+{
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ struct fp_img *img = NULL;
+ int final_height;
+ char *processed_image;
+ tmp_writeout_buf(dev);
+
+ process_image_data(dev, &processed_image, &final_height); //the fun part
+
+ img = fpi_img_new(VFS0050_IMG_WIDTH * final_height);
+ if (img == NULL)
+ return 0;
+
+ memcpy(img->data, processed_image, final_height * VFS0050_IMG_WIDTH);
+ free(processed_image);
+ img->width = VFS0050_IMG_WIDTH;
+ img->height = final_height;
+ img->flags = FP_IMG_V_FLIPPED;
+ fpi_imgdev_image_captured(dev, img);
+ return 1;
+}
+
+//activate ssm states
+enum {
+ M_ACTIVATE_START,
+ M_ACTIVATE_1_STEP2,
+ M_ACTIVATE_1_SINGLE_READ,
+ M_ACTIVATE_2_SEND,
+ M_ACTIVATE_EP1_DRAIN,
+ M_ACTIVATE_EP3_INT1,
+ M_ACTIVATE_AWAIT_FINGER,
+ M_ACTIVATE_RECEIVE_FINGERPRINT,
+ M_ACTIVATE_POST_RECEIVE,
+ M_ACTIVATE_NUMSTATES
+};
+
+static void async_sleep_cb(void *data)
+{
+ struct fpi_ssm *ssm = data;
+ struct fp_img_dev *dev = ssm->priv;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ if (vfs_dev->is_active) {
+ fpi_ssm_jump_to_state(ssm, M_ACTIVATE_START);
+ } else {
+ fpi_ssm_next_state(ssm);
+ }
+}
+
+static void state_activate_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+ struct fp_img_dev *dev = ssm->priv;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ switch (ssm->cur_state) {
+ case M_ACTIVATE_1_SINGLE_READ:
+ //check bytes are 0x00 and 0x00
+ if (vfs_dev->tmpbuf[0] != 0x00 || vfs_dev->tmpbuf[1] != 0x00) {
+ fp_dbg("unexpected bytes back from endpoint in M_ACTIVATE_1_SINGLE_READ");
+ libusb_free_transfer(transfer);
+ fpi_ssm_jump_to_state(ssm, M_ACTIVATE_START);
+ break;
+ }
+ libusb_free_transfer(transfer);
+ fpi_ssm_next_state(ssm);
+ break;
+ case M_ACTIVATE_2_SEND:
+ vfs_dev->activate_offset += transfer->actual_length;
+ if (vfs_dev->activate_offset == sizeof(vfs0050_activate2)) { //finished sending this activation section
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ fpi_ssm_jump_to_state(ssm, M_ACTIVATE_2_SEND);
+ break;
+ case M_ACTIVATE_EP1_DRAIN:
+ //just draining this data, not sure what it does yet.
+ if (transfer->actual_length < 64) {
+ libusb_free_transfer(transfer);
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ libusb_free_transfer(transfer);
+ fpi_ssm_jump_to_state(ssm, M_ACTIVATE_EP1_DRAIN);
+ break;
+ case M_ACTIVATE_EP3_INT1:
+ if (transfer->actual_length != 5) {
+ fp_dbg("unexpected length for interrupt transfer in M_ACTIVATE_EP3_INT1");
+ //TODO: fail here, exit ssm
+ }
+ fpi_ssm_next_state(ssm);
+ break;
+ case M_ACTIVATE_AWAIT_FINGER:
+ //if we got here, it's time to read fingerprint data.
+ //interrupt data should be 02 00 0e 00 f0
+ if (vfs_dev->is_active) {
+ if (transfer->actual_length != 5) {
+ fp_dbg("unexpected length for interrupt transfer in M_ACTIVATE_AWAIT_FINGER");
+ //TODO: fail here, exit ssm.
+ }
+ if (memcmp(vfs_dev->tmpbuf, vfs0050_valid_interrupt, 5) != 0) {
+ fp_dbg("invalid interrupt data in M_ACTIVATE_AWAIT_FINGER");
+ //TODO: fail here, exit ssm.
+ }
+ fpi_imgdev_report_finger_status(dev, TRUE); //report finger on to libfprint
+ fpi_ssm_next_state(ssm);
+ } else {
+ fpi_ssm_mark_completed(ssm);
+ //break fall through
+ }
+ break;
+ case M_ACTIVATE_RECEIVE_FINGERPRINT:
+ if (transfer->actual_length == 0) {
+ libusb_free_transfer(transfer);
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ vfs_dev->scanbuf_idx += transfer->actual_length;
+ libusb_free_transfer(transfer);
+ fpi_ssm_jump_to_state(ssm, M_ACTIVATE_RECEIVE_FINGERPRINT);
+ break;
+ default:
+ libusb_free_transfer(transfer);
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+}
+
+static void state_activate(struct fpi_ssm *ssm)
+{
+ struct libusb_transfer *transfer;
+ struct fp_img_dev *dev = ssm->priv;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ int to_send;
+ switch (ssm->cur_state) {
+ case M_ACTIVATE_START:
+ vfs_dev->activate_offset = 0;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs0050_activate1, 64, state_activate_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_1_STEP2:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs0050_activate1 + 64, 61, state_activate_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_1_SINGLE_READ:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, state_activate_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_2_SEND:
+ to_send = sizeof(vfs0050_activate2) - vfs_dev->activate_offset;
+ to_send = to_send >= 64 ? 64 : to_send;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs0050_activate2 + vfs_dev->activate_offset, to_send, state_activate_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_EP1_DRAIN:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, state_activate_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_EP3_INT1: //first interrupt, should be 5 x 0x00
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_interrupt_transfer(transfer, dev->udev, EP3_IN, vfs_dev->tmpbuf, 8, state_activate_cb, ssm, INTERRUPT_TIMEOUT1);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_AWAIT_FINGER:
+ //this sets up infinite wait for interrupt. When the interrupt occurs, we're ready to read data on EP2.
+ if (!vfs_dev->is_active) {
+ vfs_dev->is_active = 1;
+ fpi_imgdev_activate_complete(dev, 0); //notify libfprint activation complete.
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_interrupt_transfer(transfer, dev->udev, EP3_IN, vfs_dev->tmpbuf, 8, state_activate_cb, ssm, INTERRUPT_TIMEOUT_NONE);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_RECEIVE_FINGERPRINT:
+ if (vfs_dev->scanbuf_idx + 64 >= vfs_dev->scanbuf_sz) {
+ vfs_dev->scanbuf_sz <<= 1;
+ vfs_dev->scanbuf = g_realloc(vfs_dev->scanbuf, vfs_dev->scanbuf_sz);
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP2_IN, vfs_dev->scanbuf + vfs_dev->scanbuf_idx, 64, state_activate_cb, ssm, 500);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_ACTIVATE_POST_RECEIVE:
+ submit_image(dev);
+ fpi_imgdev_report_finger_status(dev, FALSE);
+ vfs_dev->activate_offset = 0;
+ vfs_dev->scanbuf_idx = 0;
+ fpi_timeout_add(300, async_sleep_cb, ssm); //wait a bit and see if we're swiping again.
+ break;
+
+ }
+}
+
+static void state_activate_complete(struct fpi_ssm *ssm)
+{
+ fpi_ssm_free(ssm);
+}
+
+//init ssm states
+enum {
+ M_INIT_START,
+ M_INIT_1_ONGOING,
+ M_INIT_1_STEP2,
+ M_INIT_1_STEP3,
+ M_INIT_1_STEP4,
+ M_INIT_2_ONGOING,
+ M_INIT_2_RECV_EP1_ONGOING,
+ M_INIT_2_RECV_EP2_ONGOING, //TODO: this looks like some sensor calibration data, could be useful later.
+ M_INIT_2_COMPLETE,
+ M_INIT_NUMSTATES,
+};
+
+static void state_init_cb(struct libusb_transfer *transfer)
+{
+ struct fpi_ssm *ssm = transfer->user_data;
+ struct fp_img_dev *dev = ssm->priv;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ //TODO: check for transfer errors in tranfer->status
+ switch (ssm->cur_state) {
+ case M_INIT_1_ONGOING:
+ vfs_dev->init1_offset += transfer->actual_length;
+ libusb_free_transfer(transfer);
+ if (vfs_dev->init1_offset == sizeof(vfs0050_init1)) { //done transferring this initialization section
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ fpi_ssm_jump_to_state(ssm, M_INIT_1_ONGOING);
+ break;
+ case M_INIT_2_ONGOING:
+ vfs_dev->init2_offset += transfer->actual_length;
+ libusb_free_transfer(transfer);
+ if (vfs_dev->init2_offset == sizeof(vfs0050_init2)) { //done transferring this initialization section
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ fpi_ssm_jump_to_state(ssm, M_INIT_2_ONGOING);
+ break;
+ case M_INIT_2_RECV_EP1_ONGOING:
+ //we wait for a timeout here indicating no more data to receive.
+ if (transfer->actual_length < 64) {
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ fpi_ssm_jump_to_state(ssm, M_INIT_2_RECV_EP1_ONGOING);
+ break;
+ case M_INIT_2_RECV_EP2_ONGOING:
+ vfs_dev->calbuf_idx += transfer->actual_length;
+ if (transfer->actual_length < 64) {
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+ fpi_ssm_jump_to_state(ssm, M_INIT_2_RECV_EP2_ONGOING);
+ break;
+ default:
+ libusb_free_transfer(transfer);
+ fpi_ssm_next_state(ssm);
+ break;
+ }
+
+}
+
+static void state_init(struct fpi_ssm *ssm)
+{
+ int transferred;
+ int to_send;
+ struct libusb_transfer *transfer;
+ struct fp_img_dev *dev = ssm->priv;
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ switch (ssm->cur_state) {
+ case M_INIT_START:
+ //couple of synchronous transfers here in the beginning, don't think this hurts much.
+ vfs_dev->tmpbuf[0] = 0x1a;
+ libusb_bulk_transfer(dev->udev, EP1_OUT, vfs_dev->tmpbuf, 1, &transferred, BULK_TIMEOUT);
+ libusb_bulk_transfer(dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, &transferred, BULK_TIMEOUT);
+ fpi_ssm_next_state(ssm);
+ break;
+ case M_INIT_1_ONGOING:
+ to_send = sizeof(vfs0050_init1) - vfs_dev->init1_offset;
+ to_send = to_send >= 64 ? 64 : to_send;
+ assert(to_send > 0);
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs0050_init1 + vfs_dev->init1_offset, to_send, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_1_STEP2:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_1_STEP3:
+ vfs_dev->tmpbuf[0] = 0x01;
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs_dev->tmpbuf, 1, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_1_STEP4:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_2_ONGOING:
+ to_send = sizeof(vfs0050_init2) - vfs_dev->init2_offset;
+ to_send = to_send >= 64 ? 64 : to_send;
+ assert(to_send > 0);
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_OUT, vfs0050_init2 + vfs_dev->init2_offset, to_send, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_2_RECV_EP1_ONGOING:
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ case M_INIT_2_RECV_EP2_ONGOING:
+ if (vfs_dev->calbuf_idx + 64 >= vfs_dev->calbuf_sz) {
+ vfs_dev->calbuf_sz <<= 1;
+ vfs_dev->calbuf = g_realloc(vfs_dev->calbuf, vfs_dev->calbuf_sz);
+ }
+ transfer = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(transfer, dev->udev, EP2_IN, vfs_dev->calbuf + vfs_dev->calbuf_idx, 64, state_init_cb, ssm, BULK_TIMEOUT);
+ libusb_submit_transfer(transfer);
+ break;
+ default:
+ fpi_ssm_mark_completed(ssm);
+ break;
+ }
+}
+
+static void state_init_complete(struct fpi_ssm *ssm)
+{
+ struct fp_img_dev *dev = ssm->priv;
+ fpi_imgdev_open_complete(dev, 0);
+ fpi_ssm_free(ssm);
+}
+
+static void generic_async_cb(struct libusb_transfer *t)
+{
+ libusb_free_transfer(t);
+}
+
+static void dev_deactivate(struct fp_img_dev *dev)
+{
+ struct vfs0050_dev *vfs_dev = dev->priv;
+ int err = 0;
+ int tmpoffset;
+ int to_send;
+ struct libusb_transfer *t;
+ vfs_dev->is_active = 0;
+ //EP2 IN
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP2_IN, vfs_dev->tmpbuf, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //EP1_IN
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //EP1_OUT
+ t = libusb_alloc_transfer(0);
+ vfs_dev->tmpbuf[0] = 0x04;
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_OUT, vfs_dev->tmpbuf, 1, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //EP1_IN
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //EP1_OUT
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_OUT, vfs0050_deactivate1, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //EP1_OUT
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_OUT, &vfs0050_deactivate1[64], 61, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ tmpoffset = 0;
+ do {
+ to_send = sizeof(vfs0050_activate2) - tmpoffset;
+ to_send = to_send >= 64 ? 64 : to_send;
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_OUT, vfs0050_activate2 + tmpoffset, to_send, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ tmpoffset += err;
+ } while (err == 64);
+ do {
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ } while(err == 64);
+ t = libusb_alloc_transfer(0);
+ libusb_fill_bulk_transfer(t, dev->udev, EP1_IN, vfs_dev->tmpbuf, 64, generic_async_cb, NULL, 100);
+ libusb_submit_transfer(t);
+ //TODO: finish this, leaves device in inconsistent state.
+ fpi_imgdev_deactivate_complete(dev);
+}
+
+static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
+{
+ fp_dbg("dev_activate called");
+ struct fpi_ssm *activate_ssm;
+ activate_ssm = fpi_ssm_new(dev->dev, state_activate, M_ACTIVATE_NUMSTATES);
+ activate_ssm->priv = dev;
+ fpi_ssm_start(activate_ssm, state_activate_complete);
+ return 0;
+}
+
+static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
+{
+ struct fpi_ssm *init_ssm;
+ struct vfs0050_dev *vdev = NULL;
+ int r;
+ fp_dbg("dev_open called");
+ //TODO: this is probably frowned upon :/ but right now a reset is the only
+ // way for me to know I'm working with a clean slate and the rest of this will work.
+ // Will be trying to reverse engineer more of the protocol so I can avoid resetting
+ // the device each time it's opened.
+ libusb_reset_device(dev->udev);
+
+ r = libusb_claim_interface(dev->udev, 0);
+ if (r < 0) {
+ fp_err("could not claim interface 0");
+ return r;
+ }
+ libusb_control_transfer(dev->udev, 0x00, 0x09, 0x0001, 0, NULL, 0, 100);
+
+
+ vdev = g_malloc0(sizeof(struct vfs0050_dev));
+ vdev->scanbuf = g_malloc0(VFS0050_INITIAL_SCANBUF_SIZE);
+ vdev->scanbuf_sz = VFS0050_INITIAL_SCANBUF_SIZE;
+ vdev->calbuf = g_malloc0(VFS0050_INITIAL_SCANBUF_SIZE);
+ vdev->calbuf_sz = VFS0050_INITIAL_SCANBUF_SIZE;
+ dev->priv = vdev;
+ init_ssm = fpi_ssm_new(dev->dev, state_init, M_INIT_NUMSTATES);
+ init_ssm->priv = dev;
+ fpi_ssm_start(init_ssm, state_init_complete);
+
+ return 0;
+}
+
+static void dev_close(struct fp_img_dev *dev)
+{
+ fp_dbg("dev_close called");
+ //release private structure
+ g_free(((struct vfs0050_dev *)dev->priv)->calbuf);
+ g_free(((struct vfs0050_dev *)dev->priv)->scanbuf);
+ g_free(dev->priv);
+
+ libusb_release_interface(dev->udev, 0);
+
+ fpi_imgdev_close_complete(dev);
+}
+
+
+static const struct usb_id id_table[] =
+{
+ { .vendor = 0x138a, .product = 0x0050 },
+ { 0, 0, 0 },
+};
+
+struct fp_img_driver vfs0050_driver =
+{
+ .driver =
+ {
+ .id = VFS0050_ID,
+ .name = FP_COMPONENT,
+ .full_name = "Validity 0050",
+ .id_table = id_table,
+ .scan_type = FP_SCAN_TYPE_SWIPE,
+ },
+
+ .flags = 0,
+ .img_width = VFS0050_IMG_WIDTH,
+ .img_height = -1,
+ .bz3_threshold = 24,
+
+ .open = dev_open,
+ .close = dev_close,
+ .activate = dev_activate,
+ .deactivate = dev_deactivate,
+};
diff --git a/libfprint/drivers/vfs0050.h b/libfprint/drivers/vfs0050.h
new file mode 100644
index 0000000..552d743
--- /dev/null
+++ b/libfprint/drivers/vfs0050.h
@@ -0,0 +1,404 @@
+#define BULK_TIMEOUT 100
+#define INTERRUPT_TIMEOUT1 300
+#define INTERRUPT_TIMEOUT_NONE 0
+#define VFS0050_FRAME_SIZE 148
+#define VFS0050_IMG_WIDTH 100
+#define VFS0050_INITIAL_SCANBUF_SIZE (1 << 19)
+#define VFS0050_SCALE_FACTOR 0.20
+#define WHITE_THRESH 200
+#define BLACK_THRESH 50
+#define EP1_OUT 0x01
+#define EP1_IN 0x81
+#define EP2_OUT 0x02
+#define EP2_IN 0x82
+#define EP3_OUT 0x03
+#define EP3_IN 0x83
+
+struct vfs0050_dev {
+ int init1_offset;
+ int init2_offset;
+ int activate_offset;
+ char tmpbuf[100];
+ char *calbuf;
+ int calbuf_idx;
+ int calbuf_sz;
+ char *scanbuf;
+ int scanbuf_idx;
+ int scanbuf_sz;
+ int is_active;
+};
+
+struct vfs0050_line {
+ unsigned char _0x01;
+ unsigned char _0xfe;
+ unsigned char _low;
+ unsigned char _high;
+ unsigned char _0x08;
+ unsigned char _0xb8; //not sure what this is for
+ unsigned char _0x00;
+ unsigned char _counter; //not sure what this is either.
+ unsigned char row[100]; //grayscale data
+ unsigned char mag[32]; //magnified portion of scan
+ unsigned char sums[8];
+} __attribute__((__packed__));
+
+static unsigned char vfs0050_valid_interrupt[] =
+{
+ 0x02, 0x00, 0x0e, 0x00, 0xf0
+};
+
+static unsigned char vfs0050_init1[] =
+{
+ 0x36, 0x38, 0x2b, 0x5c, 0x70, 0xac, 0x73, 0x69,
+ 0x12, 0xc9, 0xe0, 0xbc, 0xfa, 0x88, 0x10, 0xc7,
+ 0x98, 0xbf, 0x52, 0xe3, 0xd5, 0xbc, 0x99, 0x9f,
+ 0x73, 0x48, 0x06, 0xba, 0xe0, 0x11, 0x0e, 0x87,
+ 0x87, 0xb5, 0x18, 0x95, 0x63, 0x98, 0xae, 0x00,
+ 0xc2, 0x67, 0xd5, 0x05, 0x5d, 0x66, 0xd6, 0xb3,
+ 0x3f, 0xb7, 0xa7, 0xa0, 0x61, 0x5b, 0x1d, 0xa0,
+ 0x36, 0xc6, 0xe1, 0x78, 0x0d, 0x86, 0x72, 0x84, //end block 1
+ 0xd2, 0xf3, 0xd9, 0xb9, 0x78, 0xd8, 0xde, 0xad,
+ 0x7a, 0x45, 0x3c, 0x96, 0x08, 0x2d, 0xc6, 0xae,
+ 0xc3, 0x3d, 0x8c, 0x6e, 0x5a, 0xfd, 0x91, 0x90,
+ 0x0d, 0x78, 0x98, 0xf1, 0x28, 0x67, 0x15, 0x80,
+ 0xea, 0x7b, 0xa9, 0xbd, 0xe0, 0x4b, 0x54, 0x8f,
+ 0x91, 0xea, 0x2a, 0x99, 0x38, 0xaf, 0x52, 0x11,
+ 0xc8, 0x34, 0x17, 0x42, 0xb8, 0xea, 0xd3, 0x8e,
+ 0xbc, 0x6a, 0xaa, 0x54, 0x3e, 0x77, 0x44, 0xd6, //end block 2
+ 0x4a, 0x46, 0x04, 0xa5, 0x33, 0xe1, 0x86, 0xfd,
+ 0xed, 0x80, 0xa8, 0x12, 0x3a, 0xc2, 0x29, 0x3e,
+ 0xce, 0x9e, 0x00, 0xe7, 0xb3, 0xb5, 0x11, 0x2b,
+ 0x50, 0x6c, 0x2c, 0x5e, 0x33, 0x17, 0xf5, 0x8b,
+ 0xc4, 0x0f, 0x25, 0x65, 0x87, 0x0d, 0x88, 0x3c,
+ 0x30, 0xad, 0x9b, 0x40, 0x8a, 0x6c, 0x60, 0xc9,
+ 0xf4, 0x03, 0xc1, 0x0f, 0x0e, 0x08, 0xa1, 0x81,
+ 0x5e, 0x6a, 0x79, 0x3f, 0x7a, 0xf6, 0x18, 0x7e, //end block 3
+ 0x30, 0x98, 0xf1, 0x25, 0xcb, 0xbd, 0xb9, 0xae,
+ 0x5b, 0xeb, 0xa7, 0xea, 0x7c, 0xb6, 0x6f, 0x06,
+ 0x0f, 0xaa, 0xe5, 0xd6, 0xe3, 0x46, 0x83, 0xa3,
+ 0xe1, 0x66, 0x38, 0x70, 0xb0, 0x5c, 0x3e, 0xe3,
+ 0xe0, 0x50, 0x25, 0x60, 0x98, 0x08, 0xa8, 0x86,
+ 0xc9, 0xdc, 0xbc, 0xaf, 0x02, 0x54, 0xfc, 0xad,
+ 0x8d, 0x9f, 0x87, 0x5a, 0x2f, 0x4a, 0xea, 0x92,
+ 0x71, 0x5d, 0x88, 0x64, 0xeb, 0xb0, 0x98, 0x27, //end block 4
+ 0x96, 0xd5, 0x1f, 0x80, 0x44, 0xe3, 0x0f, 0x95,
+ 0x95, 0xcb, 0x43, 0xb0, 0xca, 0xbf, 0xac, 0xd9,
+ 0x70, 0xe8, 0xc6, 0xf9, 0x4a, 0x94, 0xf7, 0x6c,
+ 0x8d, 0x46, 0x97, 0x63, 0x85, 0xb9, 0x85, 0xae,
+ 0x3c, 0xe0, 0xa2, 0xad, 0xd7, 0x36, 0x27, 0xcf,
+ 0xa1, 0x1f, 0x18, 0x34, 0xf9, 0xff, 0x52, 0xc7,
+ 0xae, 0x60, 0x2d, 0xa5, 0x76, 0x79, 0x42, 0xfb,
+ 0xa8, 0x52, 0xa8, 0x08, 0x37, 0x79, 0x96, 0x7e, //end block 5
+ 0x33, 0x1d, 0xd0, 0x6f, 0xc6, 0x13, 0x43, 0xdb,
+ 0xb0, 0xa9, 0xdd, 0x1c, 0x7a, 0x8a, 0xce, 0xe8,
+ 0xb6, 0xf9, 0x23, 0x43, 0x47, 0x1d, 0xd8, 0xbf,
+ 0xb3, 0x68, 0x66, 0x55, 0x92, 0xae, 0x7a, 0x76,
+ 0x54, 0xcf, 0x2c, 0xfc, 0x11, 0xf1, 0xd2, 0x08,
+ 0x27, 0xd9, 0x23, 0x5c, 0x3c, 0x0a, 0xde, 0x7c,
+ 0xd9, 0x26, 0x4b, 0x24, 0x5d, 0xc2, 0xbf, 0xe9,
+ 0x50, 0x87, 0xda, 0x2d, 0xfd, 0x5e, 0x20, 0x66, //end block 6
+ 0x08, 0x9d, 0xe2, 0xa6, 0xc1, 0xc7, 0x0e, 0x86,
+ 0xcb, 0xe0, 0xe7, 0x56, 0x82, 0xf1, 0x7e, 0x7e,
+ 0x4a, 0xb3, 0x42, 0x8d, 0x25, 0xec, 0x1e, 0xb1,
+ 0x44, 0x17, 0xdf, 0xb3, 0x06, 0xf4, 0x60, 0x3d,
+ 0x68, 0x36, 0x45, 0xa5, 0xee, 0x5b, 0xea, 0xc3,
+ 0x5d, 0x67, 0x51, 0x14, 0xdc, 0xcc, 0x6b, 0x9d,
+ 0xd3, 0x01, 0xdb, 0x99, 0xc7, 0x85, 0x15, 0x68,
+ 0xe5, 0x04, 0xe4, 0x12, 0xd4, 0x83, 0x44, 0x7d, //end block 7
+ 0xe8, 0x7c, 0x6c, 0xaa, 0xaa, 0xd2, 0x97, 0x5b,
+ 0xae, 0xac, 0x03, 0xac, 0x3c, 0x73, 0xd6, 0x16,
+ 0x72, 0x29, 0xc7, 0x2a, 0x57, 0xbc, 0x3e, 0xdc,
+ 0x2e, 0xaf, 0xd2, 0x1a, 0x03, 0x76, 0x39, 0x9d,
+ 0x3f, 0x66, 0xe5, 0xcc, 0x32, 0x0d, 0xd8, 0x58,
+ 0x4b, 0xa4, 0xa2, 0x39, 0xe2, 0xe8, 0xb7, 0x44,
+ 0x33, 0xab, 0x7c, 0x49, 0xf6, 0xe4, 0x24, 0xc4,
+ 0x7c, 0xa9, 0x07, 0x31, 0x93, 0x16, 0xb4, 0x31, //end block 8
+ 0x38, 0x5d, 0x13, 0x0b, 0x8f, 0x46, 0xa8, 0x77,
+ 0xb2, 0x86, 0x15, 0x2c, 0xa7, 0x6a, 0x04, 0xb1,
+ 0x4c, 0xdd, 0xc8, 0xe0, 0x26, 0xc5, 0xa3, 0x4d,
+ 0x6a, 0x94, 0x57, 0x1f, 0x85, 0x83, 0x8f, 0x05,
+ 0xe3, 0xd9, 0x3d, 0x3c, 0x54, 0x96, 0x05, 0xec,
+ 0xf8, 0x25, 0xea, 0x84, 0x9f, 0x5d, 0x3f, 0x4a,
+ 0xa1, 0x89, 0x86, 0x9f, 0xb7, 0x73, 0x49, 0x6c,
+ 0x8e, 0xcf, 0x9c, 0x88, 0xb6, 0xce, 0x18, 0x15, //end block 9
+ 0x93, 0xe0, 0x17, 0x9a, 0x69, 0x57, 0xd1, 0xb6,
+ 0x25, 0xf9, 0x62, 0xd2, 0xba, 0x2c, 0xcb, 0xe6,
+ 0x5f, 0xe5, 0xb4, 0x18, 0xe8, 0x65, 0x46, 0x7d,
+ 0x06, 0x36, 0x85, 0x74, 0xc4, 0x1d, 0x62, 0xf1,
+ 0x50, 0x54, 0x9a, 0x02, 0xda, 0x4b, 0x67, 0x70,
+ 0xfd, 0x00
+};
+static unsigned char vfs0050_init2[] =
+{
+ 0x02, 0x94, 0x00, 0x64, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x2c, 0x03, 0x00, 0x30, 0x1b, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x03, 0x00,
+ 0x30, 0x3d, 0x10, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x18, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x24, 0x03, 0x00,
+ 0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x28, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00, //end block 1
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x30, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x38, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x3c, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x44, 0x03, 0x00, 0x30, 0x14, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x48, 0x03, 0x00,
+ 0x30, 0x01, 0x04, 0x02, 0x00, 0x20, 0x00, 0x08, //end block 2
+ 0x00, 0x4c, 0x03, 0x00, 0x30, 0x01, 0x0c, 0x02,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x54, 0x03, 0x00,
+ 0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x5c, 0x03, 0x00, 0x30, 0x90, 0x01, 0x02,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x60, 0x03, 0x00,
+ 0x30, 0x2c, 0x01, 0x19, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x64, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x6c, 0x03, 0x00, //end block 3
+ 0x30, 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x70, 0x03, 0x00, 0x30, 0x21, 0x80, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x78, 0x03, 0x00,
+ 0x30, 0x09, 0x00, 0x02, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x7c, 0x03, 0x00, 0x30, 0x0b, 0x00, 0x19,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x80, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x84, 0x03, 0x00, 0x30, 0x3a, 0x00, 0x00, //end block 4
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x88, 0x03, 0x00,
+ 0x30, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x8c, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x90, 0x03, 0x00,
+ 0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x94, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x98, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0xa1, 0x01, 0x20, 0x00, 0x08, //end block 5
+ 0x00, 0x9c, 0x03, 0x00, 0x30, 0x00, 0x00, 0xa1,
+ 0x01, 0x20, 0x00, 0x08, 0x00, 0xa8, 0x03, 0x00,
+ 0x30, 0x64, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xac, 0x03, 0x00, 0x30, 0x64, 0x01, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xb0, 0x03, 0x00,
+ 0x30, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xb4, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xb8, 0x03, 0x00, //end block 6
+ 0x30, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xbc, 0x03, 0x00, 0x30, 0x05, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xc0, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x84, 0x03, 0x00, 0x30, 0x3b, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x08, 0x07, 0x00,
+ 0x30, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x0c, 0x07, 0x00, 0x30, 0x00, 0x00, 0x00, //end block 7
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x14, 0x07, 0x00,
+ 0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x1c, 0x07, 0x00, 0x30, 0x1a, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x0d, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x28,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //end block 8
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x2b,
+ 0xff, 0x2b, 0xff, 0x2b, 0xed, 0x00, 0x00, 0x2b,
+ 0xfb, 0x00, 0x00, 0x2b, 0xc5, 0x00, 0x00, 0x2b,
+ 0x05, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x24, 0xd3, 0x2e, 0xc0, 0x2c, 0x3b, 0x08,
+ 0xf0, 0x3b, 0x09, 0x24, 0xbb, 0x3b, 0x0b, 0x24, //end block 9
+ 0xaa, 0x3b, 0x1f, 0xf8, 0x00, 0x3b, 0x3f, 0xf0,
+ 0x00, 0x3b, 0x35, 0xc0, 0x00, 0x38, 0x80, 0x2c,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x38,
+ 0x80, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x3a, 0x80, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x3b, 0x0a, 0x80, 0x2e, 0x83, 0x24, 0xdb,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x2c,
+ 0x31, 0x83, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00, //end block 10
+ 0xcb, 0x33, 0x1b, 0x83, 0x2c, 0x70, 0x00, 0x00,
+ 0x00, 0x00, 0xcb, 0x31, 0x83, 0x2c, 0x70, 0x00,
+ 0x00, 0x00, 0x00, 0xcb, 0x00, 0x33, 0x1e, 0x83,
+ 0x2e, 0x25, 0xff, 0xc4, 0x00, 0x2f, 0x06, 0x84,
+ 0x2e, 0x00, 0x00, 0x10, 0x20, 0x29, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
+ 0x00, 0x21, 0x00, 0x10, 0x00, 0x48, 0x03, 0x00,
+ 0x30, 0xff, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, //end block 11
+ 0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x00, 0x10,
+ 0x00, 0x4c, 0x03, 0x00, 0x30, 0xff, 0xf0, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0x21, 0x00, 0x10, 0x00, 0x20, 0x03, 0x00,
+ 0x30, 0x7f, 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x10, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x24, 0x03, 0x00, 0x30, 0x04, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x1c, 0x07, 0x00, //end block 12
+ 0x30, 0x1a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10,
+ 0x00, 0x20, 0x03, 0x00, 0x30, 0xc3, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x80, 0x03, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x00
+};
+
+static unsigned char vfs0050_activate1[] =
+{
+ 0x39, 0x20, 0xbf, 0x02, 0x00, 0xf4, 0x01, 0x00,
+ 0x00, 0x01, 0xd1, 0x00, 0x20, 0xd1, 0xd1, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //end block 1
+ 0x00, 0xf4, 0x01, 0x00, 0x00, 0x02, 0xd1, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char vfs0050_deactivate1[] =
+{
+ 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //end block 1
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char vfs0050_activate2[] =
+{
+ 0x02, 0x94, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x2c, 0x03, 0x00, 0x30, 0x1b, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x03, 0x00,
+ 0x30, 0x3d, 0x10, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x18, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x24, 0x03, 0x00,
+ 0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x28, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00, //end block 1
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x30, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x38, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x3c, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x44, 0x03, 0x00, 0x30, 0x14, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x48, 0x03, 0x00,
+ 0x30, 0x01, 0x04, 0x02, 0x00, 0x20, 0x00, 0x08, //end block 2
+ 0x00, 0x4c, 0x03, 0x00, 0x30, 0x01, 0x0c, 0x02,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x54, 0x03, 0x00,
+ 0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x5c, 0x03, 0x00, 0x30, 0x90, 0x01, 0x02,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x60, 0x03, 0x00,
+ 0x30, 0x2c, 0x01, 0x19, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x64, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x6c, 0x03, 0x00, //end block 3
+ 0x30, 0x1e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x70, 0x03, 0x00, 0x30, 0x21, 0x80, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x78, 0x03, 0x00,
+ 0x30, 0x09, 0x00, 0x02, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x7c, 0x03, 0x00, 0x30, 0x0b, 0x00, 0x19,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x80, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x84, 0x03, 0x00, 0x30, 0x3a, 0x00, 0x00, //end block 4
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x88, 0x03, 0x00,
+ 0x30, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x8c, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x90, 0x03, 0x00,
+ 0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x94, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x98, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0xa1, 0x01, 0x20, 0x00, 0x08, //end block 5
+ 0x00, 0x9c, 0x03, 0x00, 0x30, 0x00, 0x00, 0xa1,
+ 0x01, 0x20, 0x00, 0x08, 0x00, 0xa8, 0x03, 0x00,
+ 0x30, 0x64, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xac, 0x03, 0x00, 0x30, 0x64, 0x01, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xb0, 0x03, 0x00,
+ 0x30, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xb4, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xb8, 0x03, 0x00, //end block 6
+ 0x30, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0xbc, 0x03, 0x00, 0x30, 0x05, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0xc0, 0x03, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x84, 0x03, 0x00, 0x30, 0x3b, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x08, 0x07, 0x00,
+ 0x30, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x0c, 0x07, 0x00, 0x30, 0x00, 0x00, 0x00, //end block 7
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x14, 0x07, 0x00,
+ 0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x1c, 0x07, 0x00, 0x30, 0x1a, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x0d, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x28,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //end block 8
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x2b,
+ 0xff, 0x2b, 0xff, 0x2b, 0xed, 0x00, 0x00, 0x2b,
+ 0xfb, 0x00, 0x00, 0x2b, 0xc5, 0x00, 0x00, 0x2b,
+ 0x05, 0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x24, 0xd3, 0x2e, 0xc0, 0x2c, 0x3b, 0x08,
+ 0xf0, 0x3b, 0x09, 0x24, 0xbb, 0x3b, 0x0b, 0x24, //end block 9
+ 0xaa, 0x3b, 0x1f, 0xf8, 0x00, 0x3b, 0x3f, 0xf0,
+ 0x00, 0x3b, 0x35, 0xc0, 0x00, 0x38, 0x80, 0x2c,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x38,
+ 0x80, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x3a, 0x80, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x3b, 0x0a, 0x80, 0x2e, 0x83, 0x24, 0xdb,
+ 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x2c,
+ 0x31, 0x83, 0x2c, 0x70, 0x00, 0x00, 0x00, 0x00, //end block 10
+ 0xcb, 0x33, 0x1b, 0x83, 0x2c, 0x70, 0x00, 0x00,
+ 0x00, 0x00, 0xcb, 0x31, 0x83, 0x2c, 0x70, 0x00,
+ 0x00, 0x00, 0x00, 0xcb, 0x00, 0x33, 0x1e, 0x83,
+ 0x2e, 0x25, 0xff, 0xc4, 0x00, 0x2f, 0x06, 0x84,
+ 0x2e, 0x00, 0x00, 0x10, 0x20, 0x29, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
+ 0x00, 0x21, 0x00, 0x10, 0x00, 0x48, 0x03, 0x00,
+ 0x30, 0xff, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, //end block 11
+ 0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x00, 0x10,
+ 0x00, 0x4c, 0x03, 0x00, 0x30, 0xff, 0xf0, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
+ 0x00, 0x21, 0x00, 0x10, 0x00, 0x20, 0x03, 0x00,
+ 0x30, 0x7f, 0x00, 0xfc, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x10, 0x00, 0x00, 0x20, 0x00, 0x08,
+ 0x00, 0x24, 0x03, 0x00, 0x30, 0x04, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x1c, 0x07, 0x00, //end block 12
+ 0x30, 0x1a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10,
+ 0x00, 0x20, 0x03, 0x00, 0x30, 0xc3, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x08, 0x00, 0x80, 0x03, 0x00,
+ 0x30, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x8c,
+ 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, //end block 13
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, //end block 14
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x26, 0x00, 0x28,
+ 0x00, 0xff, 0x00, 0x0f, 0x00, 0xf0, 0xf0, 0x0f, //end block 15
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x01, 0x02,
+ 0x00, 0x2c, 0x01, 0x28, 0x00, 0x20, 0x80, 0x00,
+ 0x00, 0x0a, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x19,
+ 0x00, 0x40, 0x1f, 0x10, 0x27, 0x00, 0x0f, 0x03,
+ 0x00
+};
+
diff --git a/libfprint/drivers/vfs101.c b/libfprint/drivers/vfs101.c
index 95e654f..db0b06e 100644
--- a/libfprint/drivers/vfs101.c
+++ b/libfprint/drivers/vfs101.c
@@ -64,9 +64,6 @@
/* Best image contrast */
#define VFS_IMG_BEST_CONRAST 128
-/* Number of enroll stages */
-#define VFS_NR_ENROLL 3
-
/* Device parameters address */
#define VFS_PAR_000E 0x000e
#define VFS_PAR_0011 0x0011
@@ -656,7 +653,7 @@ static int action_completed(struct fp_img_dev *dev)
struct vfs101_dev *vdev = dev->priv;
if ((dev->action == IMG_ACTION_ENROLL) &&
- (vdev->enroll_stage < VFS_NR_ENROLL))
+ (vdev->enroll_stage < 1))
/* Enroll not completed, return false */
return FALSE;
@@ -1511,9 +1508,6 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
return r;
}
- /* Set enroll stage number */
- dev->dev->nr_enroll_stages = VFS_NR_ENROLL;
-
/* Initialize private structure */
vdev = g_malloc0(sizeof(struct vfs101_dev));
vdev->seqnum = -1;
diff --git a/libfprint/drivers/vfs301.c b/libfprint/drivers/vfs301.c
index c0e3612..04db05e 100644
--- a/libfprint/drivers/vfs301.c
+++ b/libfprint/drivers/vfs301.c
@@ -244,9 +244,6 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
return r;
}
- /* Set enroll stage number */
- dev->dev->nr_enroll_stages = 1;
-
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
dev->priv = vdev;
diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h
index 04d304f..3ef8348 100644
--- a/libfprint/fp_internal.h
+++ b/libfprint/fp_internal.h
@@ -91,6 +91,10 @@ enum fp_dev_state {
DEV_STATE_IDENTIFYING,
DEV_STATE_IDENTIFY_DONE,
DEV_STATE_IDENTIFY_STOPPING,
+ DEV_STATE_CAPTURE_STARTING,
+ DEV_STATE_CAPTURING,
+ DEV_STATE_CAPTURE_DONE,
+ DEV_STATE_CAPTURE_STOPPING,
};
struct fp_driver **fprint_get_drivers (void);
@@ -108,8 +112,8 @@ struct fp_dev {
/* drivers should not mess with any of the below */
enum fp_dev_state state;
-
int __enroll_stage;
+ int unconditional_capture;
/* async I/O callbacks and data */
/* FIXME: convert this to generic state operational data mechanism? */
@@ -129,6 +133,10 @@ struct fp_dev {
void *identify_cb_data;
fp_identify_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
+ fp_capture_cb capture_cb;
+ void *capture_cb_data;
+ fp_capture_stop_cb capture_stop_cb;
+ void *capture_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
@@ -146,6 +154,7 @@ enum fp_imgdev_action {
IMG_ACTION_ENROLL,
IMG_ACTION_VERIFY,
IMG_ACTION_IDENTIFY,
+ IMG_ACTION_CAPTURE,
};
enum fp_imgdev_enroll_state {
@@ -170,7 +179,9 @@ struct fp_img_dev {
int action_state;
struct fp_print_data *acquire_data;
+ struct fp_print_data *enroll_data;
struct fp_img *acquire_img;
+ int enroll_stage;
int action_result;
/* FIXME: better place to put this? */
@@ -179,8 +190,6 @@ struct fp_img_dev {
void *priv;
};
-int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
- struct fp_img **image);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
@@ -215,6 +224,8 @@ struct fp_driver {
int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
int (*identify_start)(struct fp_dev *dev);
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
+ int (*capture_start)(struct fp_dev *dev);
+ int (*capture_stop)(struct fp_dev *dev);
};
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
@@ -267,6 +278,9 @@ extern struct fp_img_driver aes2550_driver;
#ifdef ENABLE_AES2660
extern struct fp_img_driver aes2660_driver;
#endif
+#ifdef ENABLE_AES3500
+extern struct fp_img_driver aes3500_driver;
+#endif
#ifdef ENABLE_AES4000
extern struct fp_img_driver aes4000_driver;
#endif
@@ -282,6 +296,15 @@ extern struct fp_img_driver vfs101_driver;
#ifdef ENABLE_VFS301
extern struct fp_img_driver vfs301_driver;
#endif
+#ifdef ENABLE_UPEKTC_IMG
+extern struct fp_img_driver upektc_img_driver;
+#endif
+#ifdef ENABLE_ETES603
+extern struct fp_img_driver etes603_driver;
+#endif
+#ifdef ENABLE_VFS0050
+extern struct fp_img_driver vfs0050_driver;
+#endif
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
@@ -310,15 +333,19 @@ enum fp_print_data_type {
PRINT_DATA_NBIS_MINUTIAE,
};
+struct fp_print_data_item {
+ size_t length;
+ unsigned char data[0];
+};
+
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
- size_t length;
- unsigned char data[0];
+ GSList *prints;
};
-struct fpi_print_data_fp1 {
+struct fpi_print_data_fp2 {
char prefix[3];
uint16_t driver_id;
uint32_t devtype;
@@ -326,8 +353,14 @@ struct fpi_print_data_fp1 {
unsigned char data[0];
} __attribute__((__packed__));
+struct fpi_print_data_item_fp2 {
+ uint32_t length;
+ unsigned char data[0];
+} __attribute__((__packed__));
+
void fpi_data_exit(void);
-struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length);
+struct fp_print_data *fpi_print_data_new(struct fp_dev *dev);
+struct fp_print_data_item *fpi_print_data_item_new(size_t length);
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
enum fp_print_data_type type2);
@@ -436,6 +469,11 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img);
void fpi_drvcb_identify_stopped(struct fp_dev *dev);
+void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
+void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
+ struct fp_img *img);
+void fpi_drvcb_capture_stopped(struct fp_dev *dev);
+
/* for image drivers */
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);
diff --git a/libfprint/fprint.h b/libfprint/fprint.h
index 99fa1e1..af1d686 100644
--- a/libfprint/fprint.h
+++ b/libfprint/fprint.h
@@ -107,6 +107,17 @@ uint32_t fp_dev_get_devtype(struct fp_dev *dev);
int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data);
int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print);
+/** \ingroup dev
+ * Image capture result codes returned from fp_dev_img_capture().
+ */
+enum fp_capture_result {
+ /** Capture completed successfully, the capture data has been
+ * returned to the caller. */
+ FP_CAPTURE_COMPLETE = 0,
+ /** Capture failed for some reason */
+ FP_CAPTURE_FAIL,
+};
+
int fp_dev_supports_imaging(struct fp_dev *dev);
int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **image);
@@ -340,6 +351,13 @@ typedef void (*fp_identify_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_identify_stop(struct fp_dev *dev, fp_identify_stop_cb callback,
void *user_data);
+typedef void (*fp_capture_cb)(struct fp_dev *dev, int result,
+ struct fp_img *img, void *user_data);
+int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_capture_cb callback, void *user_data);
+
+typedef void (*fp_capture_stop_cb)(struct fp_dev *dev, void *user_data);
+int fp_async_capture_stop(struct fp_dev *dev, fp_capture_stop_cb callback, void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/libfprint/gdkpixbuf.c b/libfprint/gdkpixbuf.c
deleted file mode 100644
index 4de6151..0000000
--- a/libfprint/gdkpixbuf.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Imaging utility functions for libfprint
- * Copyright (C) 2007-2008 Daniel Drake
- *
- * 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
-#include
-
-#include "fp_internal.h"
-
-struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
-{
- int new_width = img->width * w_factor;
- int new_height = img->height * h_factor;
- GdkPixbuf *orig, *resized;
- struct fp_img *newimg;
- guchar *pixels;
- guint y;
- int rowstride;
-
- g_type_init ();
-
- /* It is possible to implement resizing using a simple algorithm, however
- * we use gdk-pixbuf because it applies some kind of smoothing to the
- * result, which improves matching performances in my experiments. */
-
- /* Create the original pixbuf, and fill it in from the grayscale data */
- orig = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
- FALSE,
- 8,
- img->width,
- img->height);
- rowstride = gdk_pixbuf_get_rowstride (orig);
- pixels = gdk_pixbuf_get_pixels (orig);
- for (y = 0; y < img->height; y++) {
- guint x;
- for (x = 0; x < img->width; x++) {
- guchar *p, *r;
-
- p = pixels + y * rowstride + x * 3;
- r = img->data + y * img->width + x;
- p[0] = r[0];
- p[1] = r[0];
- p[2] = r[0];
- }
- }
-
- /* Resize the pixbuf, and create the new fp_img */
- resized = gdk_pixbuf_scale_simple (orig, new_width, new_height, GDK_INTERP_HYPER);
- g_object_unref (orig);
-
- newimg = fpi_img_new(new_width * new_height);
- newimg->width = new_width;
- newimg->height = new_height;
- newimg->flags = img->flags;
-
- rowstride = gdk_pixbuf_get_rowstride (resized);
- pixels = gdk_pixbuf_get_pixels (resized);
- for (y = 0; y < newimg->height; y++) {
- guint x;
- for (x = 0; x < newimg->width; x++) {
- guchar *p, *r;
-
- r = newimg->data + y * newimg->width + x;
- p = pixels + y * rowstride + x * 3;
- r[0] = p[0];
- }
- }
-
- g_object_unref (resized);
-
- return newimg;
-}
-
diff --git a/libfprint/imagemagick.c b/libfprint/imagemagick.c
deleted file mode 100644
index 68e7146..0000000
--- a/libfprint/imagemagick.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Imaging utility functions for libfprint
- * Copyright (C) 2007-2008 Daniel Drake
- *
- * 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
-#include
-
-#include "fp_internal.h"
-
-struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
-{
- Image *mimg;
- Image *resized;
- ExceptionInfo exception;
- MagickBooleanType ret;
- int new_width = img->width * w_factor;
- int new_height = img->height * h_factor;
- struct fp_img *newimg;
-
- /* It is possible to implement resizing using a simple algorithm, however
- * we use ImageMagick because it applies some kind of smoothing to the
- * result, which improves matching performances in my experiments. */
-
- if (!IsMagickInstantiated())
- InitializeMagick(NULL);
-
- GetExceptionInfo(&exception);
- mimg = ConstituteImage(img->width, img->height, "I", CharPixel, img->data,
- &exception);
-
- GetExceptionInfo(&exception);
- resized = ResizeImage(mimg, new_width, new_height, 0, 1.0, &exception);
-
- newimg = fpi_img_new(new_width * new_height);
- newimg->width = new_width;
- newimg->height = new_height;
- newimg->flags = img->flags;
-
- GetExceptionInfo(&exception);
- ret = ExportImagePixels(resized, 0, 0, new_width, new_height, "I",
- CharPixel, newimg->data, &exception);
- if (ret != MagickTrue) {
- fp_err("export failed");
- return NULL;
- }
-
- DestroyImage(mimg);
- DestroyImage(resized);
-
- return newimg;
-}
-
diff --git a/libfprint/img.c b/libfprint/img.c
index b1d32ed..3c91d93 100644
--- a/libfprint/img.c
+++ b/libfprint/img.c
@@ -313,6 +313,7 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret)
{
struct fp_print_data *print;
+ struct fp_print_data_item *item;
int r;
if (!img->minutiae) {
@@ -327,9 +328,11 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
/* FIXME: space is wasted if we dont hit the max minutiae count. would
* be good to make this dynamic. */
- print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
+ print = fpi_print_data_new(imgdev->dev);
+ item = fpi_print_data_item_new(sizeof(struct xyt_struct));
print->type = PRINT_DATA_NBIS_MINUTIAE;
- minutiae_to_xyt(img->minutiae, img->width, img->height, print->data);
+ minutiae_to_xyt(img->minutiae, img->width, img->height, item->data);
+ print->prints = g_slist_prepend(print->prints, item);
/* FIXME: the print buffer at this point is endian-specific, and will
* only work when loaded onto machines with identical endianness. not good!
@@ -342,42 +345,73 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print)
{
- struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->data;
- struct xyt_struct *pstruct = (struct xyt_struct *) new_print->data;
- GTimer *timer;
- int r;
+ int score, max_score = 0, probe_len;
+ struct xyt_struct *pstruct = NULL;
+ struct xyt_struct *gstruct = NULL;
+ struct fp_print_data_item *data_item;
+ GSList *list_item;
if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
- new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
+ new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
fp_err("invalid print format");
return -EINVAL;
}
- timer = g_timer_new();
- r = bozorth_main(pstruct, gstruct);
- g_timer_stop(timer);
- fp_dbg("bozorth processing took %f seconds, score=%d",
- g_timer_elapsed(timer, NULL), r);
- g_timer_destroy(timer);
+ if (g_slist_length(new_print->prints) != 1) {
+ fp_err("new_print contains more than one sample, is it enrolled print?");
+ return -EINVAL;
+ }
- return r;
+ data_item = new_print->prints->data;
+ pstruct = (struct xyt_struct *)data_item->data;
+
+ probe_len = bozorth_probe_init(pstruct);
+ list_item = enrolled_print->prints;
+ do {
+ data_item = list_item->data;
+ gstruct = (struct xyt_struct *)data_item->data;
+ score = bozorth_to_gallery(probe_len, pstruct, gstruct);
+ fp_dbg("score %d", score);
+ max_score = max(score, max_score);
+ list_item = g_slist_next(list_item);
+ } while (list_item);
+
+ return max_score;
}
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, size_t *match_offset)
{
- struct xyt_struct *pstruct = (struct xyt_struct *) print->data;
+ struct xyt_struct *pstruct;
+ struct xyt_struct *gstruct;
struct fp_print_data *gallery_print;
- int probe_len = bozorth_probe_init(pstruct);
+ struct fp_print_data_item *data_item;
+ int probe_len;
size_t i = 0;
+ int r;
+ GSList *list_item;
+
+ if (g_slist_length(print->prints) != 1) {
+ fp_err("new_print contains more than one sample, is it enrolled print?");
+ return -EINVAL;
+ }
+
+ data_item = print->prints->data;
+ pstruct = (struct xyt_struct *)data_item->data;
+ probe_len = bozorth_probe_init(pstruct);
while ((gallery_print = gallery[i++])) {
- struct xyt_struct *gstruct = (struct xyt_struct *) gallery_print->data;
- int r = bozorth_to_gallery(probe_len, pstruct, gstruct);
- if (r >= match_threshold) {
- *match_offset = i - 1;
- return FP_VERIFY_MATCH;
- }
+ list_item = gallery_print->prints;
+ do {
+ data_item = list_item->data;
+ gstruct = (struct xyt_struct *)data_item->data;
+ r = bozorth_to_gallery(probe_len, pstruct, gstruct);
+ if (r >= match_threshold) {
+ *match_offset = i - 1;
+ return FP_VERIFY_MATCH;
+ }
+ list_item = g_slist_next(list_item);
+ } while (list_item);
}
return FP_VERIFY_NO_MATCH;
}
diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c
index 1ed3f6d..f960ee3 100644
--- a/libfprint/imgdev.c
+++ b/libfprint/imgdev.c
@@ -25,6 +25,7 @@
#define MIN_ACCEPTABLE_MINUTIAE 10
#define BOZORTH3_DEFAULT_THRESHOLD 40
+#define IMG_ENROLL_STAGES 5
static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
{
@@ -33,8 +34,9 @@ static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
int r = 0;
imgdev->dev = dev;
+ imgdev->enroll_stage = 0;
dev->priv = imgdev;
- dev->nr_enroll_stages = 1;
+ dev->nr_enroll_stages = IMG_ENROLL_STAGES;
/* for consistency in driver code, allow udev access through imgdev */
imgdev->udev = dev->udev;
@@ -144,7 +146,13 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
switch (imgdev->action) {
case IMG_ACTION_ENROLL:
fp_dbg("reporting enroll result");
- fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img);
+ data = imgdev->enroll_data;
+ if (r == FP_ENROLL_COMPLETE) {
+ imgdev->enroll_data = NULL;
+ }
+ fpi_drvcb_enroll_stage_completed(imgdev->dev, r,
+ r == FP_ENROLL_COMPLETE ? data : NULL,
+ img);
/* the callback can cancel enrollment, so recheck current
* action and the status to see if retry is needed */
if (imgdev->action == IMG_ACTION_ENROLL &&
@@ -163,6 +171,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
imgdev->identify_match_offset, img);
fp_print_data_free(data);
break;
+ case IMG_ACTION_CAPTURE:
+ fpi_drvcb_report_capture_result(imgdev->dev, r, img);
+ break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -231,24 +242,41 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
fp_img_standardize(img);
imgdev->acquire_img = img;
- r = fpi_img_to_print_data(imgdev, img, &print);
- if (r < 0) {
- fp_dbg("image to print data conversion error: %d", r);
- imgdev->action_result = FP_ENROLL_RETRY;
- goto next_state;
- } else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
- fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
- MIN_ACCEPTABLE_MINUTIAE);
- fp_print_data_free(print);
- /* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
- imgdev->action_result = FP_ENROLL_RETRY;
- goto next_state;
+ if (imgdev->action != IMG_ACTION_CAPTURE) {
+ r = fpi_img_to_print_data(imgdev, img, &print);
+ if (r < 0) {
+ fp_dbg("image to print data conversion error: %d", r);
+ imgdev->action_result = FP_ENROLL_RETRY;
+ goto next_state;
+ } else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
+ fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
+ MIN_ACCEPTABLE_MINUTIAE);
+ fp_print_data_free(print);
+ /* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
+ imgdev->action_result = FP_ENROLL_RETRY;
+ goto next_state;
+ }
}
imgdev->acquire_data = print;
switch (imgdev->action) {
case IMG_ACTION_ENROLL:
- imgdev->action_result = FP_ENROLL_COMPLETE;
+ if (!imgdev->enroll_data) {
+ imgdev->enroll_data = fpi_print_data_new(imgdev->dev);
+ }
+ BUG_ON(g_slist_length(print->prints) != 1);
+ /* Move print data from acquire data into enroll_data */
+ imgdev->enroll_data->prints =
+ g_slist_prepend(imgdev->enroll_data->prints, print->prints->data);
+ print->prints = g_slist_remove(print->prints, print->prints->data);
+
+ fp_print_data_free(imgdev->acquire_data);
+ imgdev->acquire_data = NULL;
+ imgdev->enroll_stage++;
+ if (imgdev->enroll_stage == imgdev->dev->nr_enroll_stages)
+ imgdev->action_result = FP_ENROLL_COMPLETE;
+ else
+ imgdev->action_result = FP_ENROLL_PASS;
break;
case IMG_ACTION_VERIFY:
verify_process_img(imgdev);
@@ -256,6 +284,9 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
case IMG_ACTION_IDENTIFY:
identify_process_img(imgdev);
break;
+ case IMG_ACTION_CAPTURE:
+ imgdev->action_result = FP_CAPTURE_COMPLETE;
+ break;
default:
BUG();
break;
@@ -280,6 +311,9 @@ void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_report_identify_result(imgdev->dev, error, 0, NULL);
break;
+ case IMG_ACTION_CAPTURE:
+ fpi_drvcb_report_capture_result(imgdev->dev, error, NULL);
+ break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -300,6 +334,9 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_identify_started(imgdev->dev, status);
break;
+ case IMG_ACTION_CAPTURE:
+ fpi_drvcb_capture_started(imgdev->dev, status);
+ break;
default:
fp_err("unhandled action %d", imgdev->action);
return;
@@ -325,6 +362,9 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_identify_stopped(imgdev->dev);
break;
+ case IMG_ACTION_CAPTURE:
+ fpi_drvcb_capture_stopped(imgdev->dev);
+ break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -385,6 +425,7 @@ static int generic_acquire_start(struct fp_dev *dev, int action)
fp_dbg("action %d", action);
imgdev->action = action;
imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
+ imgdev->enroll_stage = 0;
r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
if (r < 0)
@@ -400,8 +441,10 @@ static void generic_acquire_stop(struct fp_img_dev *imgdev)
dev_deactivate(imgdev);
fp_print_data_free(imgdev->acquire_data);
+ fp_print_data_free(imgdev->enroll_data);
fp_img_free(imgdev->acquire_img);
imgdev->acquire_data = NULL;
+ imgdev->enroll_data = NULL;
imgdev->acquire_img = NULL;
imgdev->action_result = 0;
}
@@ -421,6 +464,14 @@ static int img_dev_identify_start(struct fp_dev *dev)
return generic_acquire_start(dev, IMG_ACTION_IDENTIFY);
}
+static int img_dev_capture_start(struct fp_dev *dev)
+{
+ /* Unconditional capture is not supported yet */
+ if (dev->unconditional_capture)
+ return -ENOTSUP;
+ return generic_acquire_start(dev, IMG_ACTION_CAPTURE);
+}
+
static int img_dev_enroll_stop(struct fp_dev *dev)
{
struct fp_img_dev *imgdev = dev->priv;
@@ -446,6 +497,14 @@ static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
return 0;
}
+static int img_dev_capture_stop(struct fp_dev *dev)
+{
+ struct fp_img_dev *imgdev = dev->priv;
+ BUG_ON(imgdev->action != IMG_ACTION_CAPTURE);
+ generic_acquire_stop(imgdev);
+ return 0;
+}
+
void fpi_img_driver_setup(struct fp_img_driver *idriver)
{
idriver->driver.type = DRIVER_IMAGING;
@@ -457,5 +516,7 @@ void fpi_img_driver_setup(struct fp_img_driver *idriver)
idriver->driver.verify_stop = img_dev_verify_stop;
idriver->driver.identify_start = img_dev_identify_start;
idriver->driver.identify_stop = img_dev_identify_stop;
+ idriver->driver.capture_start = img_dev_capture_start;
+ idriver->driver.capture_stop = img_dev_capture_stop;
}
diff --git a/libfprint/pixman.c b/libfprint/pixman.c
new file mode 100644
index 0000000..1b4ca06
--- /dev/null
+++ b/libfprint/pixman.c
@@ -0,0 +1,63 @@
+/*
+ * Imaging utility functions for libfprint
+ * Copyright (C) 2007-2008 Daniel Drake
+ * Copyright (C) 2013 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
+ */
+
+#include
+#include
+
+#include "fp_internal.h"
+
+struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
+{
+ int new_width = img->width * w_factor;
+ int new_height = img->height * h_factor;
+ pixman_image_t *orig, *resized;
+ pixman_transform_t transform;
+ struct fp_img *newimg;
+
+ orig = pixman_image_create_bits(PIXMAN_a8, img->width, img->height, (uint32_t *)img->data, img->width);
+ resized = pixman_image_create_bits(PIXMAN_a8, new_width, new_height, NULL, new_width);
+
+ pixman_transform_init_identity(&transform);
+ pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(w_factor), pixman_int_to_fixed(h_factor));
+ pixman_image_set_transform(orig, &transform);
+ pixman_image_set_filter(orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
+ pixman_image_composite32(PIXMAN_OP_SRC,
+ orig, /* src */
+ NULL, /* mask */
+ resized, /* dst */
+ 0, 0, /* src x y */
+ 0, 0, /* mask x y */
+ 0, 0, /* dst x y */
+ new_width, new_height /* width height */
+ );
+
+ newimg = fpi_img_new(new_width * new_height);
+ newimg->width = new_width;
+ newimg->height = new_height;
+ newimg->flags = img->flags;
+
+ memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
+
+ pixman_image_unref(orig);
+ pixman_image_unref(resized);
+
+ return newimg;
+}
+
diff --git a/libfprint/sync.c b/libfprint/sync.c
index ca2f302..b3b2898 100644
--- a/libfprint/sync.c
+++ b/libfprint/sync.c
@@ -512,3 +512,100 @@ err:
return r;
}
+struct sync_capture_data {
+ gboolean populated;
+ int result;
+ struct fp_img *img;
+};
+
+static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img,
+ void *user_data)
+{
+ struct sync_capture_data *vdata = user_data;
+ vdata->result = result;
+ vdata->img = img;
+ vdata->populated = TRUE;
+}
+
+static void capture_stop_cb(struct fp_dev *dev, void *user_data)
+{
+ gboolean *stopped = user_data;
+ fp_dbg("");
+ *stopped = TRUE;
+}
+/** \ingroup dev
+ * Captures an \ref img "image" from a device. The returned image is the raw
+ * image provided by the device, you may wish to \ref img_std "standardize" it.
+ *
+ * If set, the unconditional flag indicates that the device should
+ * capture an image unconditionally, regardless of whether a finger is there
+ * or not. If unset, this function will block until a finger is detected on
+ * the sensor.
+ *
+ * \param dev the device
+ * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
+ * \param img a location to return the captured image. Must be freed with
+ * fp_img_free() after use.
+ * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
+ * unconditional flag was set but the device does not support this, or that the
+ * device does not support imaging.
+ * \sa fp_dev_supports_imaging()
+ */
+API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
+ struct fp_img **img)
+{
+ struct sync_capture_data *vdata;
+ gboolean stopped = FALSE;
+ int r;
+
+ if (!dev->drv->capture_start) {
+ fp_dbg("image capture is not supported on %s device", dev->drv->name);
+ return -ENOTSUP;
+ }
+
+ fp_dbg("to be handled by %s", dev->drv->name);
+ vdata = g_malloc0(sizeof(struct sync_capture_data));
+ r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata);
+ if (r < 0) {
+ fp_dbg("capture_start error %d", r);
+ g_free(vdata);
+ return r;
+ }
+
+ while (!vdata->populated) {
+ r = fp_handle_events();
+ if (r < 0) {
+ g_free(vdata);
+ goto err;
+ }
+ }
+
+ if (img)
+ *img = vdata->img;
+ else
+ fp_img_free(vdata->img);
+
+ r = vdata->result;
+ g_free(vdata);
+ switch (r) {
+ case FP_CAPTURE_COMPLETE:
+ fp_dbg("result: complete");
+ break;
+ case FP_CAPTURE_FAIL:
+ fp_dbg("result: fail");
+ break;
+ default:
+ fp_err("unrecognised return code %d", r);
+ r = -EINVAL;
+ }
+
+err:
+ fp_dbg("ending capture");
+ if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0)
+ while (!stopped)
+ if (fp_handle_events() < 0)
+ break;
+
+ return r;
+}
+