Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 390940 Details for
Bug 531698
sys-auth/libfprint-0.6.0 - add support for vfs 0050 fingerprint scanner (and others from upstream)
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
libfprint-0.5.1-add-new-drivers.patch
libfprint-0.5.1-add-new-drivers.patch (text/plain), 175.78 KB, created by
Matthew Schultz
on 2014-12-04 18:20:29 UTC
(
hide
)
Description:
libfprint-0.5.1-add-new-drivers.patch
Filename:
MIME Type:
Creator:
Matthew Schultz
Created:
2014-12-04 18:20:29 UTC
Size:
175.78 KB
patch
obsolete
>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 <tt>unconditional</tt> flag indicates that the device should >- * capture an image unconditionally, regardless of whether a finger is there >- * or not. If unset, this function will block until a finger is detected on >- * the sensor. >- * >- * \param dev the device >- * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected >- * \param image a location to return the captured image. Must be freed with >- * fp_img_free() after use. >- * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the >- * unconditional flag was set but the device does not support this, or that the >- * device does not support imaging. >- * \sa fp_dev_supports_imaging() >- */ >-API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional, >- struct fp_img **image) >-{ >- struct fp_img_dev *imgdev = dev_to_img_dev(dev); >- if (!imgdev) { >- fp_dbg("image capture on non-imaging device"); >- return -ENOTSUP; >- } >- >- //return fpi_imgdev_capture(imgdev, unconditional, image); >- /* FIXME reimplement async */ >- return -ENOTSUP; >-} >- >-/** \ingroup dev > * Gets the expected width of images that will be captured from the device. > * This function will return -1 for devices that are not > * \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 <machese@gmail.com> >+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> >+ * >+ * This library is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU Lesser General Public License as >+ * published by the Free Software Foundation; either version 2.1 of the >+ * License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, but >+ * WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with this library; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >+ * 02110-1301 USA >+ */ >+ >+#define FP_COMPONENT "aes3500" >+ >+#include <errno.h> >+ >+#include <glib.h> >+#include <libusb.h> >+ >+#include <aeslib.h> >+#include <fp_internal.h> >+ >+#include "aes3k.h" >+#include "driver_ids.h" >+ >+#define DATA_BUFLEN 0x2089 >+ >+/* image size = FRAME_WIDTH x FRAME_WIDTH */ >+#define FRAME_WIDTH 128 >+#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2) >+#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT) >+#define ENLARGE_FACTOR 2 >+ >+ >+static struct aes_regwrite init_reqs[] = { >+ /* master reset */ >+ { 0x80, 0x01 }, >+ { 0, 0 }, >+ { 0x80, 0x00 }, >+ { 0, 0 }, >+ >+ { 0x81, 0x00 }, >+ { 0x80, 0x00 }, >+ { 0, 0 }, >+ >+ /* scan reset */ >+ { 0x80, 0x02 }, >+ { 0, 0 }, >+ { 0x80, 0x00 }, >+ { 0, 0 }, >+ >+ /* disable register buffering */ >+ { 0x80, 0x04 }, >+ { 0, 0 }, >+ { 0x80, 0x00 }, >+ { 0, 0 }, >+ >+ { 0x81, 0x00 }, >+ { 0, 0 }, >+ /* windows driver reads registers now (81 02) */ >+ { 0x80, 0x00 }, >+ { 0x81, 0x00 }, >+ >+ /* set excitation bias current: 2mhz drive ring frequency, >+ * 4V drive ring voltage, 16.5mA excitation bias */ >+ { 0x82, 0x04 }, >+ >+ /* continuously sample drive ring for finger detection, >+ * 62.50ms debounce delay */ >+ { 0x83, 0x13 }, >+ >+ { 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */ >+ { 0x85, 0x3d }, /* set calibration capacitance */ >+ { 0x86, 0x03 }, /* detect drive voltage */ >+ { 0x87, 0x01 }, /* set detection frequency to 125khz */ >+ { 0x88, 0x02 }, /* set column scan period */ >+ { 0x89, 0x02 }, /* set measure drive */ >+ { 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */ >+ { 0x8b, 0x33 }, /* set matrix pattern */ >+ { 0x8c, 0x0f }, /* set demodulation phase 1 */ >+ { 0x8d, 0x04 }, /* set demodulation phase 2 */ >+ { 0x8e, 0x23 }, /* set sensor gain */ >+ { 0x8f, 0x07 }, /* set image parameters */ >+ { 0x90, 0x00 }, /* carrier offset null */ >+ { 0x91, 0x1c }, /* set A/D reference high */ >+ { 0x92, 0x08 }, /* set A/D reference low */ >+ { 0x93, 0x00 }, /* set start row to 0 */ >+ { 0x94, 0x07 }, /* set end row */ >+ { 0x95, 0x00 }, /* set start column to 0 */ >+ { 0x96, 0x1f }, /* set end column */ >+ { 0x97, 0x04 }, /* data format and thresholds */ >+ { 0x98, 0x28 }, /* image data control */ >+ { 0x99, 0x00 }, /* disable general purpose outputs */ >+ { 0x9a, 0x0b }, /* set initial scan state */ >+ { 0x9b, 0x00 }, /* clear challenge word bits */ >+ { 0x9c, 0x00 }, /* clear challenge word bits */ >+ { 0x9d, 0x09 }, /* set some challenge word bits */ >+ { 0x9e, 0x53 }, /* clear challenge word bits */ >+ { 0x9f, 0x6b }, /* set some challenge word bits */ >+ { 0, 0 }, >+ >+ { 0x80, 0x00 }, >+ { 0x81, 0x00 }, >+ { 0, 0 }, >+ { 0x81, 0x04 }, >+ { 0, 0 }, >+ { 0x81, 0x00 }, >+}; >+ >+static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) >+{ >+ int r; >+ struct aes3k_dev *aesdev; >+ >+ r = libusb_claim_interface(dev->udev, 0); >+ if (r < 0) >+ fp_err("could not claim interface 0"); >+ >+ aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev)); >+ >+ if (!aesdev) >+ return -ENOMEM; >+ >+ if (r == 0) >+ aesdev->data_buflen = DATA_BUFLEN; >+ aesdev->frame_width = FRAME_WIDTH; >+ aesdev->frame_size = FRAME_SIZE; >+ aesdev->frame_number = FRAME_NUMBER; >+ aesdev->enlarge_factor = ENLARGE_FACTOR; >+ aesdev->init_reqs = init_reqs; >+ aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs); >+ fpi_imgdev_open_complete(dev, 0); >+ >+ return r; >+} >+ >+static void dev_deinit(struct fp_img_dev *dev) >+{ >+ struct aes3k_dev *aesdev = dev->priv; >+ g_free(aesdev); >+ libusb_release_interface(dev->udev, 0); >+ fpi_imgdev_close_complete(dev); >+} >+ >+ >+static const struct usb_id id_table[] = { >+ { .vendor = 0x08ff, .product = 0x5731 }, >+ { 0, 0, 0, }, >+}; >+ >+struct fp_img_driver aes3500_driver = { >+ .driver = { >+ .id = AES3500_ID, >+ .name = FP_COMPONENT, >+ .full_name = "AuthenTec AES3500", >+ .id_table = id_table, >+ .scan_type = FP_SCAN_TYPE_PRESS, >+ }, >+ .flags = 0, >+ .img_height = FRAME_WIDTH * ENLARGE_FACTOR, >+ .img_width = FRAME_WIDTH * ENLARGE_FACTOR, >+ >+ /* temporarily lowered until image quality improves */ >+ .bz3_threshold = 9, >+ >+ .open = dev_init, >+ .close = dev_deinit, >+ .activate = aes3k_dev_activate, >+ .deactivate = aes3k_dev_deactivate, >+}; >+ >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 <machese@gmail.com> >+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> >+ * >+ * This library is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU Lesser General Public License as >+ * published by the Free Software Foundation; either version 2.1 of the >+ * License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, but >+ * WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with this library; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >+ * 02110-1301 USA >+ * >+ */ >+ >+#define FP_COMPONENT "aes3k" >+ >+#include <errno.h> >+ >+#include <glib.h> >+#include <libusb.h> >+ >+#include <aeslib.h> >+#include <fp_internal.h> >+ >+#include "aes3k.h" >+ >+#define CTRL_TIMEOUT 1000 >+#define EP_IN (1 | LIBUSB_ENDPOINT_IN) >+#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) >+ >+static void do_capture(struct fp_img_dev *dev); >+ >+static void 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 <machese@gmail.com> >+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> >+ * >+ * This library is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU Lesser General Public License as >+ * published by the Free Software Foundation; either version 2.1 of the >+ * License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, but >+ * WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with this library; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >+ * 02110-1301 USA >+ * >+ */ >+ >+#ifndef __AES3K_H >+#define __AES3K_H >+ >+#define AES3K_FRAME_HEIGHT 16 >+ >+struct aes3k_dev { >+ struct libusb_transfer *img_trf; >+ size_t frame_width; /* image size = frame_width x frame_width */ >+ size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */ >+ size_t frame_number; /* number of frames */ >+ size_t enlarge_factor; >+ >+ size_t data_buflen; /* buffer length of usb bulk transfer */ >+ struct aes_regwrite *init_reqs; /* initial values sent to device */ >+ size_t init_reqs_len; >+}; >+ >+ >+int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state); >+void aes3k_dev_deactivate(struct fp_img_dev *dev); >+ >+#endif >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 <machese@gmail.com> > * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> > * > * This library is free software; you can redistribute it and/or >@@ -27,24 +34,19 @@ > #include <aeslib.h> > #include <fp_internal.h> > >+#include "aes3k.h" > #include "driver_ids.h" > >-#define CTRL_TIMEOUT 1000 >-#define EP_IN (1 | LIBUSB_ENDPOINT_IN) >-#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) >-#define DATA_BUFLEN 0x1259 >-#define NR_SUBARRAYS 6 >-#define SUBARRAY_LEN 768 >+#define DATA_BUFLEN 0x1259 > >-#define IMG_HEIGHT 96 >-#define IMG_WIDTH 96 >-#define ENLARGE_FACTOR 3 >+/* image size = FRAME_WIDTH x FRAME_WIDTH */ >+#define FRAME_WIDTH 96 >+#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2) >+#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT) >+#define ENLARGE_FACTOR 3 > >-struct aes4k_dev { >- struct libusb_transfer *img_trf; >-}; > >-static const struct aes_regwrite init_reqs[] = { >+static struct aes_regwrite init_reqs[] = { > /* master reset */ > { 0x80, 0x01 }, > { 0, 0 }, >@@ -119,119 +121,28 @@ static const struct aes_regwrite init_reqs[] = { > { 0x81, 0x00 }, > }; > >-static void do_capture(struct fp_img_dev *dev); >- >-static void img_cb(struct libusb_transfer *transfer) >-{ >- struct fp_img_dev *dev = transfer->user_data; >- struct aes4k_dev *aesdev = dev->priv; >- unsigned char *ptr = transfer->buffer; >- struct fp_img *tmp; >- struct fp_img *img; >- int i; >- >- if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { >- goto err; >- } else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { >- fpi_imgdev_session_error(dev, -EIO); >- goto err; >- } else if (transfer->length != transfer->actual_length) { >- fpi_imgdev_session_error(dev, -EPROTO); >- goto err; >- } >- >- fpi_imgdev_report_finger_status(dev, TRUE); >- >- tmp = fpi_img_new(IMG_WIDTH * IMG_HEIGHT); >- tmp->width = IMG_WIDTH; >- tmp->height = IMG_HEIGHT; >- tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED; >- for (i = 0; i < NR_SUBARRAYS; i++) { >- fp_dbg("subarray header byte %02x", *ptr); >- ptr++; >- aes_assemble_image(ptr, 96, 16, tmp->data + (i * 96 * 16)); >- ptr += SUBARRAY_LEN; >- } >- >- /* FIXME: this is an ugly hack to make the image big enough for NBIS >- * to process reliably */ >- img = fpi_im_resize(tmp, ENLARGE_FACTOR, ENLARGE_FACTOR); >- fp_img_free(tmp); >- fpi_imgdev_image_captured(dev, img); >- >- /* FIXME: rather than assuming finger has gone, we should poll regs until >- * it really has, then restart the capture */ >- fpi_imgdev_report_finger_status(dev, FALSE); >- >- do_capture(dev); >- >-err: >- g_free(transfer->buffer); >- aesdev->img_trf = NULL; >- libusb_free_transfer(transfer); >-} >- >-static void do_capture(struct fp_img_dev *dev) >-{ >- struct aes4k_dev *aesdev = dev->priv; >- unsigned char *data; >- int r; >- >- aesdev->img_trf = libusb_alloc_transfer(0); >- if (!aesdev->img_trf) { >- fpi_imgdev_session_error(dev, -EIO); >- return; >- } >- >- data = g_malloc(DATA_BUFLEN); >- libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data, >- DATA_BUFLEN, img_cb, dev, 0); >- >- r = libusb_submit_transfer(aesdev->img_trf); >- if (r < 0) { >- g_free(data); >- libusb_free_transfer(aesdev->img_trf); >- aesdev->img_trf = NULL; >- fpi_imgdev_session_error(dev, r); >- } >-} >- >-static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data) >-{ >- fpi_imgdev_activate_complete(dev, result); >- if (result == 0) >- do_capture(dev); >-} >- >-static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) >-{ >- aes_write_regv(dev, init_reqs, G_N_ELEMENTS(init_reqs), init_reqs_cb, NULL); >- return 0; >-} >- >-static void dev_deactivate(struct fp_img_dev *dev) >-{ >- struct aes4k_dev *aesdev = dev->priv; >- >- /* FIXME: should wait for cancellation to complete before returning >- * from deactivation, otherwise app may legally exit before we've >- * cleaned up */ >- if (aesdev->img_trf) >- libusb_cancel_transfer(aesdev->img_trf); >- fpi_imgdev_deactivate_complete(dev); >-} >- > static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) > { > int r; >+ struct aes3k_dev *aesdev; > > r = libusb_claim_interface(dev->udev, 0); > if (r < 0) > fp_err("could not claim interface 0"); > >- dev->priv = g_malloc0(sizeof(struct aes4k_dev)); >+ aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev)); >+ >+ if (!aesdev) >+ return -ENOMEM; > > if (r == 0) >+ aesdev->data_buflen = DATA_BUFLEN; >+ aesdev->frame_width = FRAME_WIDTH; >+ aesdev->frame_size = FRAME_SIZE; >+ aesdev->frame_number = FRAME_NUMBER; >+ aesdev->enlarge_factor = ENLARGE_FACTOR; >+ aesdev->init_reqs = init_reqs; >+ aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs); > fpi_imgdev_open_complete(dev, 0); > > return r; >@@ -239,11 +150,13 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) > > static void dev_deinit(struct fp_img_dev *dev) > { >- g_free(dev->priv); >+ struct aes3k_dev *aesdev = dev->priv; >+ g_free(aesdev); > libusb_release_interface(dev->udev, 0); > fpi_imgdev_close_complete(dev); > } > >+ > static const struct usb_id id_table[] = { > { .vendor = 0x08ff, .product = 0x5501 }, > { 0, 0, 0, }, >@@ -258,15 +171,15 @@ struct fp_img_driver aes4000_driver = { > .scan_type = FP_SCAN_TYPE_PRESS, > }, > .flags = 0, >- .img_height = IMG_HEIGHT * ENLARGE_FACTOR, >- .img_width = IMG_WIDTH * ENLARGE_FACTOR, >+ .img_height = FRAME_WIDTH * ENLARGE_FACTOR, >+ .img_width = FRAME_WIDTH * ENLARGE_FACTOR, > > /* temporarily lowered until image quality improves */ > .bz3_threshold = 9, > > .open = dev_init, > .close = dev_deinit, >- .activate = dev_activate, >- .deactivate = dev_deactivate, >+ .activate = aes3k_dev_activate, >+ .deactivate = aes3k_dev_deactivate, > }; > >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 <string.h> >+#include <stdint.h> >+#include <stdarg.h> >+#include <errno.h> >+#include <assert.h> >+#include <libusb.h> >+#include <glib.h> >+ >+#define FP_COMPONENT "etes603" >+#include <fp_internal.h> >+#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 <anarsoul@gmail.com> >+ * >+ * This library is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Lesser General Public >+ * License as published by the Free Software Foundation; either >+ * version 2.1 of the License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with this library; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >+ */ >+ >+#define FP_COMPONENT "upekts_img" >+ >+#include <errno.h> >+#include <string.h> >+ >+#include <libusb.h> >+ >+#include <aeslib.h> >+#include <fp_internal.h> >+ >+#include "upektc_img.h" >+#include "driver_ids.h" >+ >+static void start_capture(struct fp_img_dev *dev); >+static void start_deactivation(struct fp_img_dev *dev); >+ >+#define EP_IN (1 | LIBUSB_ENDPOINT_IN) >+#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) >+#define CTRL_TIMEOUT 4000 >+#define BULK_TIMEOUT 4000 >+ >+#define IMAGE_WIDTH 144 >+#define IMAGE_HEIGHT 384 >+#define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT) >+ >+#define MAX_CMD_SIZE 64 >+#define MAX_RESPONSE_SIZE 2052 >+#define SHORT_RESPONSE_SIZE 64 >+ >+struct upekts_img_dev { >+ unsigned char cmd[MAX_CMD_SIZE]; >+ unsigned char response[MAX_RESPONSE_SIZE]; >+ unsigned char image_bits[IMAGE_SIZE * 2]; >+ unsigned char seq; >+ size_t image_size; >+ size_t response_rest; >+ gboolean deactivating; >+}; >+ >+/****** HELPERS ******/ >+ >+static const uint16_t crc_table[256] = { >+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, >+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, >+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, >+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, >+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, >+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, >+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, >+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, >+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, >+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, >+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, >+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, >+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, >+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, >+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, >+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, >+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, >+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, >+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, >+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, >+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, >+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, >+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, >+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, >+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, >+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, >+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, >+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, >+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, >+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, >+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, >+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 >+}; >+ >+static uint16_t udf_crc(unsigned char *buffer, size_t size) >+{ >+ uint16_t crc = 0; >+ while (size--) >+ crc = (uint16_t) ((crc << 8) ^ >+ crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]); >+ return crc; >+} >+ >+static void upektc_img_cmd_fix_seq(unsigned char *cmd_buf, unsigned char seq) >+{ >+ uint8_t byte; >+ >+ byte = cmd_buf[5]; >+ byte &= 0x0f; >+ byte |= (seq << 4); >+ cmd_buf[5] = byte; >+} >+ >+static void upektc_img_cmd_update_crc(unsigned char *cmd_buf, size_t size) >+{ >+ /* CRC does not cover Ciao prefix (4 bytes) and CRC location (2 bytes) */ >+ uint16_t crc = udf_crc(cmd_buf + 4, size - 6); >+ >+ cmd_buf[size - 2] = (crc & 0x00ff); >+ cmd_buf[size - 1] = (crc & 0xff00) >> 8; >+} >+ >+static void upektc_img_submit_req(struct fpi_ssm *ssm, >+ const unsigned char *buf, size_t buf_size, unsigned char seq, >+ libusb_transfer_cb_fn cb) >+{ >+ struct fp_img_dev *dev = ssm->priv; >+ struct upekts_img_dev *upekdev = dev->priv; >+ struct libusb_transfer *transfer = libusb_alloc_transfer(0); >+ int r; >+ >+ BUG_ON(buf_size > MAX_CMD_SIZE); >+ >+ if (!transfer) { >+ fpi_ssm_mark_aborted(ssm, -ENOMEM); >+ return; >+ } >+ >+ transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; >+ >+ memcpy(upekdev->cmd, buf, buf_size); >+ upektc_img_cmd_fix_seq(upekdev->cmd, seq); >+ upektc_img_cmd_update_crc(upekdev->cmd, buf_size); >+ >+ libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, upekdev->cmd, buf_size, >+ cb, ssm, BULK_TIMEOUT); >+ >+ r = libusb_submit_transfer(transfer); >+ if (r < 0) { >+ libusb_free_transfer(transfer); >+ fpi_ssm_mark_aborted(ssm, r); >+ } >+} >+ >+static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t buf_offset, libusb_transfer_cb_fn cb) >+{ >+ struct libusb_transfer *transfer = libusb_alloc_transfer(0); >+ struct fp_img_dev *dev = ssm->priv; >+ struct upekts_img_dev *upekdev = dev->priv; >+ int r; >+ >+ if (!transfer) { >+ fpi_ssm_mark_aborted(ssm, -ENOMEM); >+ return; >+ } >+ >+ BUG_ON(buf_size > MAX_RESPONSE_SIZE); >+ >+ transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER; >+ >+ libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, upekdev->response + buf_offset, buf_size, >+ cb, ssm, BULK_TIMEOUT); >+ >+ r = libusb_submit_transfer(transfer); >+ if (r < 0) { >+ libusb_free_transfer(transfer); >+ fpi_ssm_mark_aborted(ssm, r); >+ } >+} >+ >+/****** CAPTURE ******/ >+ >+enum capture_states { >+ CAPTURE_INIT_CAPTURE, >+ CAPTURE_READ_DATA, >+ CAPTURE_ACK_00_28, >+ CAPTURE_ACK_08, >+ CAPTURE_ACK_FRAME, >+ CAPTURE_NUM_STATES, >+}; >+ >+static void capture_reqs_cb(struct libusb_transfer *transfer) >+{ >+ struct fpi_ssm *ssm = transfer->user_data; >+ >+ if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) && >+ (transfer->length == transfer->actual_length)) { >+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA); >+ } else { >+ fpi_ssm_mark_aborted(ssm, -EIO); >+ } >+} >+ >+static int upektc_img_process_image_frame(unsigned char *image_buf, unsigned char *cmd_res) >+{ >+ int offset = 8; >+ int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]); >+ >+ len -= 1; >+ if (cmd_res[7] == 0x2c) { >+ len -= 10; >+ offset += 10; >+ } >+ if (cmd_res[7] == 0x20) { >+ len -= 4; >+ } >+ memcpy(image_buf, cmd_res + offset, len); >+ >+ return len; >+} >+ >+static void capture_read_data_cb(struct libusb_transfer *transfer) >+{ >+ struct fpi_ssm *ssm = transfer->user_data; >+ struct fp_img_dev *dev = ssm->priv; >+ struct upekts_img_dev *upekdev = dev->priv; >+ unsigned char *data = upekdev->response; >+ struct fp_img *img; >+ size_t response_size; >+ >+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { >+ fp_dbg("request is not completed, %d", transfer->status); >+ fpi_ssm_mark_aborted(ssm, -EIO); >+ return; >+ } >+ >+ if (upekdev->deactivating) { >+ fp_dbg("Deactivate requested\n"); >+ fpi_ssm_mark_completed(ssm); >+ return; >+ } >+ >+ fp_dbg("request completed, len: %.4x", transfer->actual_length); >+ if (transfer->actual_length == 0) { >+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA); >+ return; >+ } >+ >+ if (!upekdev->response_rest) { >+ response_size = ((data[5] & 0x0f) << 8) + data[6]; >+ response_size += 9; /* 7 bytes for header, 2 for CRC */ >+ if (response_size > transfer->actual_length) { >+ fp_dbg("response_size is %d, actual_length is %d\n", >+ response_size, transfer->actual_length); >+ fp_dbg("Waiting for rest of transfer"); >+ BUG_ON(upekdev->response_rest); >+ upekdev->response_rest = response_size - transfer->actual_length; >+ fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA); >+ return; >+ } >+ } >+ upekdev->response_rest = 0; >+ >+ switch (data[4]) { >+ case 0x00: >+ switch (data[7]) { >+ /* No finger */ >+ case 0x28: >+ 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 <anarsoul@gmail.com> >+ * >+ * This library is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Lesser General Public >+ * License as published by the Free Software Foundation; either >+ * version 2.1 of the License, or (at your option) any later version. >+ * >+ * This library is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with this library; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >+ */ >+ >+#ifndef __UPEKTC_IMG_H >+#define __UPEKTC_IMG_H >+ >+static const unsigned char upek2020_init_1[] = { >+'C', 'i', 'a', 'o', >+0x04, >+0x00, 0x0d, >+0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, >+0x01, 0x00, 0x00, 0x00, >+0xda, 0xc1 >+}; >+ >+static const unsigned char upek2020_init_2[] = { >+0x43, 0x69, 0x61, 0x6f, >+0x07, >+0x00, 0x01, >+0x01, >+0x3d, 0x72 >+}; >+ >+static const unsigned char upek2020_init_3[] = { >+'C', 'i', 'a', 'o', >+0x04, >+0x00, 0x0d, >+0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, >+0x01, 0x00, 0x00, 0x00, >+0x55, 0x2f >+}; >+ >+static const unsigned char upek2020_init_4[] = { >+'C', 'i', 'a', 'o', >+0x00, >+0x00, 0x07, >+0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04, >+0xc0, 0xd6 >+}; >+ >+static const unsigned char upek2020_deinit[] = { >+'C', 'i', 'a', 'o', >+0x07, >+0x00, 0x01, >+0x01, >+0x3d, >+0x72 >+}; >+ >+static const unsigned char upek2020_init_capture[] = { >+'C', 'i', 'a', 'o', >+0x00, >+0x00, 0x0e, /* Seq = 7, len = 0x00e */ >+0x28, /* CMD = 0x28 */ >+0x0b, 0x00, /* Inner len = 0x000b */ >+0x00, 0x00, >+0x0e, /* SUBCMD = 0x0e */ >+0x02, >+0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */ >+0x02, >+0x00, /* Wait for acceptable finger */ >+0x02, >+0x14, 0x9a /* CRC */ >+}; >+ >+#if 0 >+static const unsigned char finger_status[] = { >+'C', 'i', 'a', 'o', >+0x00, >+0x70, 0x14, /* Seq = 7, len = 0x014 */ >+0x28, /* CMD = 0x28 */ >+0x11, 0x00, /* Inner len = 0x0011 */ >+0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, >+0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, >+0x26, 0x03, /* CRC */ >+0x00, 0x00, 0x00, /* Rest is garbage */ >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 >+}; >+#endif >+ >+static const unsigned char upek2020_ack_00_28[] = { >+'C', 'i', 'a', 'o', >+0x00, >+0x80, 0x08, /* Seq = 8, len = 0x008 */ >+0x28, /* CMD = 0x28 */ >+0x05, 0x00, /* Inner len = 0x0005 */ >+0x00, 0x00, 0x00, 0x30, 0x01, >+0x6a, 0xc4 /* CRC */ >+}; >+ >+#if 0 >+/* No seq should be tracked here */ >+static const unsigned char got_finger[] = { >+'C', 'i', 'a', 'o', >+0x08, >+0x00, 0x00, /* Seq = 0, len = 0x000 */ >+0xa1, 0xa9, /* CRC */ >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */ >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, >+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 >+}; >+#endif >+ >+/* No seq should be put in there */ >+static const unsigned char upek2020_ack_08[] = { >+'C', 'i', 'a', 'o', >+0x09, >+0x00, 0x00, /* Seq = 0, len = 0x0 */ >+0x91, 0x9e /* CRC */ >+}; >+ >+static const unsigned char upek2020_ack_frame[] = { >+'C', 'i', 'a', 'o', >+0x00, >+0x50, 0x01, /* Seq = 5, len = 0x001 */ >+0x30, >+0xac, 0x5b /* CRC */ >+}; >+ >+#endif >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 <errno.h> >+#include <stdlib.h> >+#include <string.h> >+#include <stdio.h> >+#include <assert.h> >+#include <pixman.h> >+#include <math.h> >+ >+#include <fp_internal.h> >+ >+#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 <dsd@gentoo.org> >- * >- * This library is free software; you can redistribute it and/or >- * modify it under the terms of the GNU Lesser General Public >- * License as published by the Free Software Foundation; either >- * version 2.1 of the License, or (at your option) any later version. >- * >- * This library is distributed in the hope that it will be useful, >- * but WITHOUT ANY WARRANTY; without even the implied warranty of >- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >- * Lesser General Public License for more details. >- * >- * You should have received a copy of the GNU Lesser General Public >- * License along with this library; if not, write to the Free Software >- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >- */ >- >-#include <errno.h> >-#include <gdk-pixbuf/gdk-pixbuf.h> >- >-#include "fp_internal.h" >- >-struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor) >-{ >- int new_width = img->width * w_factor; >- int new_height = img->height * h_factor; >- GdkPixbuf *orig, *resized; >- struct fp_img *newimg; >- guchar *pixels; >- guint y; >- int rowstride; >- >- g_type_init (); >- >- /* It is possible to implement resizing using a simple algorithm, however >- * we use gdk-pixbuf because it applies some kind of smoothing to the >- * result, which improves matching performances in my experiments. */ >- >- /* Create the original pixbuf, and fill it in from the grayscale data */ >- orig = gdk_pixbuf_new (GDK_COLORSPACE_RGB, >- FALSE, >- 8, >- img->width, >- img->height); >- rowstride = gdk_pixbuf_get_rowstride (orig); >- pixels = gdk_pixbuf_get_pixels (orig); >- for (y = 0; y < img->height; y++) { >- guint x; >- for (x = 0; x < img->width; x++) { >- guchar *p, *r; >- >- p = pixels + y * rowstride + x * 3; >- r = img->data + y * img->width + x; >- p[0] = r[0]; >- p[1] = r[0]; >- p[2] = r[0]; >- } >- } >- >- /* Resize the pixbuf, and create the new fp_img */ >- resized = gdk_pixbuf_scale_simple (orig, new_width, new_height, GDK_INTERP_HYPER); >- g_object_unref (orig); >- >- newimg = fpi_img_new(new_width * new_height); >- newimg->width = new_width; >- newimg->height = new_height; >- newimg->flags = img->flags; >- >- rowstride = gdk_pixbuf_get_rowstride (resized); >- pixels = gdk_pixbuf_get_pixels (resized); >- for (y = 0; y < newimg->height; y++) { >- guint x; >- for (x = 0; x < newimg->width; x++) { >- guchar *p, *r; >- >- r = newimg->data + y * newimg->width + x; >- p = pixels + y * rowstride + x * 3; >- r[0] = p[0]; >- } >- } >- >- g_object_unref (resized); >- >- return newimg; >-} >- >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 <dsd@gentoo.org> >- * >- * This library is free software; you can redistribute it and/or >- * modify it under the terms of the GNU Lesser General Public >- * License as published by the Free Software Foundation; either >- * version 2.1 of the License, or (at your option) any later version. >- * >- * This library is distributed in the hope that it will be useful, >- * but WITHOUT ANY WARRANTY; without even the implied warranty of >- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >- * Lesser General Public License for more details. >- * >- * You should have received a copy of the GNU Lesser General Public >- * License along with this library; if not, write to the Free Software >- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >- */ >- >-#include <errno.h> >-#include <magick/ImageMagick.h> >- >-#include "fp_internal.h" >- >-struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor) >-{ >- Image *mimg; >- Image *resized; >- ExceptionInfo exception; >- MagickBooleanType ret; >- int new_width = img->width * w_factor; >- int new_height = img->height * h_factor; >- 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 <dsd@gentoo.org> >+ * Copyright (C) 2013 Vasily Khoruzhick <anarsoul@gmail.com> >+ * >+ * This library is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Lesser General Public >+ * 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 <pixman.h> >+#include <string.h> >+ >+#include "fp_internal.h" >+ >+struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor) >+{ >+ 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 <tt>unconditional</tt> flag indicates that the device should >+ * capture an image unconditionally, regardless of whether a finger is there >+ * or not. If unset, this function will block until a finger is detected on >+ * the sensor. >+ * >+ * \param dev the device >+ * \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected >+ * \param img a location to return the captured image. Must be freed with >+ * fp_img_free() after use. >+ * \return 0 on success, non-zero on error. -ENOTSUP indicates that either the >+ * unconditional flag was set but the device does not support this, or that the >+ * device does not support imaging. >+ * \sa fp_dev_supports_imaging() >+ */ >+API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional, >+ struct fp_img **img) >+{ >+ struct sync_capture_data *vdata; >+ gboolean stopped = FALSE; >+ int r; >+ >+ if (!dev->drv->capture_start) { >+ fp_dbg("image capture is not supported on %s device", dev->drv->name); >+ return -ENOTSUP; >+ } >+ >+ fp_dbg("to be handled by %s", dev->drv->name); >+ vdata = g_malloc0(sizeof(struct sync_capture_data)); >+ r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata); >+ if (r < 0) { >+ fp_dbg("capture_start error %d", r); >+ g_free(vdata); >+ return r; >+ } >+ >+ while (!vdata->populated) { >+ r = fp_handle_events(); >+ if (r < 0) { >+ g_free(vdata); >+ goto err; >+ } >+ } >+ >+ if (img) >+ *img = vdata->img; >+ else >+ fp_img_free(vdata->img); >+ >+ r = vdata->result; >+ g_free(vdata); >+ switch (r) { >+ case FP_CAPTURE_COMPLETE: >+ fp_dbg("result: complete"); >+ break; >+ case FP_CAPTURE_FAIL: >+ fp_dbg("result: fail"); >+ break; >+ default: >+ fp_err("unrecognised return code %d", r); >+ r = -EINVAL; >+ } >+ >+err: >+ fp_dbg("ending capture"); >+ if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0) >+ while (!stopped) >+ if (fp_handle_events() < 0) >+ break; >+ >+ return r; >+} >+
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 531698
:
390940
|
390944
|
408804
|
408806