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; +} +