--- A/configure 2009-06-20 15:51:50.000000000 +0200 +++ B/configure 2009-07-26 19:20:53.000000000 +0200 @@ -810,6 +810,8 @@ ENABLE_AES4000_TRUE ENABLE_AES2501_FALSE ENABLE_AES2501_TRUE +ENABLE_AES1610_FALSE +ENABLE_AES1610_TRUE ENABLE_URU4000_FALSE ENABLE_URU4000_TRUE ENABLE_VCOM5S_FALSE @@ -4381,13 +4383,13 @@ else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:4384: $ac_compile\"" >&5) + (eval echo "\"\$as_me:4386: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:4387: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:4389: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:4390: output\"" >&5) + (eval echo "\"\$as_me:4392: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -5593,7 +5595,7 @@ ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5596 "configure"' > conftest.$ac_ext + echo '#line 5598 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7450,11 +7452,11 @@ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7453: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7455: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7457: \$? = $ac_status" >&5 + echo "$as_me:7459: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7789,11 +7791,11 @@ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7792: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7794: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7796: \$? = $ac_status" >&5 + echo "$as_me:7798: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7894,11 +7896,11 @@ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7897: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7899: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7901: \$? = $ac_status" >&5 + echo "$as_me:7903: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -7949,11 +7951,11 @@ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7952: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7954: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7956: \$? = $ac_status" >&5 + echo "$as_me:7958: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10762,7 +10764,7 @@ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10765 "configure" +#line 10767 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10858,7 +10860,7 @@ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10861 "configure" +#line 10863 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11439,7 +11441,14 @@ fi #AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" != "no"]) -#AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"]) + if test "$enable_aes1610" != "no"; then + ENABLE_AES1610_TRUE= + ENABLE_AES1610_FALSE='#' +else + ENABLE_AES1610_TRUE='#' + ENABLE_AES1610_FALSE= +fi + if test "$enable_aes2501" != "no"; then ENABLE_AES2501_TRUE= ENABLE_AES2501_FALSE='#' @@ -12376,6 +12385,13 @@ Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${ENABLE_AES1610_TRUE}" && test -z "${ENABLE_AES1610_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"ENABLE_AES1610\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"ENABLE_AES1610\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi if test -z "${ENABLE_AES2501_TRUE}" && test -z "${ENABLE_AES2501_FALSE}"; then { { $as_echo "$as_me:$LINENO: error: conditional \"ENABLE_AES2501\" was never defined. Usually this means the macro was only invoked conditionally." >&5 --- A/configure.ac 2009-06-20 15:51:43.000000000 +0200 +++ B/configure.ac 2009-07-26 19:17:12.000000000 +0200 @@ -89,10 +89,10 @@ AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" != "no"]) AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" != "no"]) #AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" != "no"]) -#AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"]) AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" != "no"]) AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" != "no"]) AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" != "no"]) +AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" != "no"]) PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1]) --- A/libfprint/core.c 2008-11-25 17:08:27.000000000 +0100 +++ B/libfprint/core.c 2009-07-26 19:17:25.000000000 +0200 @@ -361,10 +361,11 @@ #ifdef ENABLE_UPEKSONLY &upeksonly_driver, #endif - /* + #ifdef ENABLE_AES1610 &aes1610_driver, #endif +/* #ifdef ENABLE_UPEKTC &upektc_driver, #endif --- A/libfprint/Makefile.am 2008-11-18 18:18:21.000000000 +0100 +++ B/libfprint/Makefile.am 2009-07-26 19:18:16.000000000 +0200 @@ -91,9 +91,9 @@ #DRIVER_SRC += $(FDU2000_SRC) #endif -#if ENABLE_AES1610 -#DRIVER_SRC += $(AES1610_SRC) -#endif +if ENABLE_AES1610 +DRIVER_SRC += $(AES1610_SRC) +endif if ENABLE_AES2501 DRIVER_SRC += $(AES2501_SRC) --- A/libfprint/Makefile.in 2009-06-20 15:51:49.000000000 +0200 +++ B/libfprint/Makefile.in 2009-07-29 14:23:34.000000000 +0200 @@ -52,6 +52,7 @@ #if ENABLE_AES1610 #DRIVER_SRC += $(AES1610_SRC) #endif +@ENABLE_AES1610_TRUE@am__append_5 = $(AES1610_SRC) @ENABLE_AES2501_TRUE@am__append_5 = $(AES2501_SRC) @ENABLE_AES4000_TRUE@am__append_6 = $(AES4000_SRC) @REQUIRE_IMAGEMAGICK_TRUE@am__append_7 = imagemagick.c @@ -90,6 +91,7 @@ drv.c img.c imgdev.c poll.c sync.c drivers/upekts.c \ drivers/upeksonly.c drivers/uru4000.c drivers/vcom5s.c \ drivers/aes2501.c drivers/aes2501.h drivers/aes4000.c \ + drivers/aes1610.c \ imagemagick.c aeslib.c aeslib.h nbis/include/bozorth.h \ nbis/include/bz_array.h nbis/include/defs.h nbis/include/lfs.h \ nbis/include/log.h nbis/include/morph.h nbis/include/sunrast.h \ @@ -117,8 +119,10 @@ @ENABLE_AES2501_TRUE@am__objects_10 = $(am__objects_9) am__objects_11 = libfprint_la-aes4000.lo @ENABLE_AES4000_TRUE@am__objects_12 = $(am__objects_11) +am__objects_19 = libfprint_la-aes1610.lo +@ENABLE_AES1610_TRUE@am__objects_20 = $(am__objects_19) am__objects_13 = $(am__objects_2) $(am__objects_4) $(am__objects_6) \ - $(am__objects_8) $(am__objects_10) $(am__objects_12) + $(am__objects_8) $(am__objects_10) $(am__objects_12) $(am__objects_20) @REQUIRE_IMAGEMAGICK_TRUE@am__objects_14 = \ @REQUIRE_IMAGEMAGICK_TRUE@ libfprint_la-imagemagick.lo @REQUIRE_AESLIB_TRUE@am__objects_15 = libfprint_la-aeslib.lo @@ -308,7 +312,7 @@ UPEKTC_SRC = drivers/upektc.c UPEKSONLY_SRC = drivers/upeksonly.c URU4000_SRC = drivers/uru4000.c -AES1610_SRC = drivers/aes1610.c +AES1610_SRC = drivers/aes1610.c AES2501_SRC = drivers/aes2501.c drivers/aes2501.h AES4000_SRC = drivers/aes4000.c FDU2000_SRC = drivers/fdu2000.c @@ -459,6 +463,7 @@ -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fprint_list_hal_info-fprint-list-hal-info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfprint_la-aes1610.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfprint_la-aes2501.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfprint_la-aes4000.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfprint_la-aeslib.Plo@am__quote@ @@ -609,6 +614,13 @@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfprint_la_CFLAGS) $(CFLAGS) -c -o libfprint_la-vcom5s.lo `test -f 'drivers/vcom5s.c' || echo '$(srcdir)/'`drivers/vcom5s.c +libfprint_la-aes1610.lo: drivers/aes1610.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfprint_la_CFLAGS) $(CFLAGS) -MT libfprint_la-aes1610.lo -MD -MP -MF $(DEPDIR)/libfprint_la-aes1610.Tpo -c -o libfprint_la-aes1610.lo `test -f 'drivers/aes1610.c' || echo '$(srcdir)/'`drivers/aes1610.c +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libfprint_la-aes1610.Tpo $(DEPDIR)/libfprint_la-aes1610.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='drivers/aes1610.c' object='libfprint_la-aes1610.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfprint_la_CFLAGS) $(CFLAGS) -c -o libfprint_la-aes1610.lo `test -f 'drivers/aes1610.c' || echo '$(srcdir)/'`drivers/aes1610.c + libfprint_la-aes2501.lo: drivers/aes2501.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfprint_la_CFLAGS) $(CFLAGS) -MT libfprint_la-aes2501.lo -MD -MP -MF $(DEPDIR)/libfprint_la-aes2501.Tpo -c -o libfprint_la-aes2501.lo `test -f 'drivers/aes2501.c' || echo '$(srcdir)/'`drivers/aes2501.c @am__fastdepCC_TRUE@ mv -f $(DEPDIR)/libfprint_la-aes2501.Tpo $(DEPDIR)/libfprint_la-aes2501.Plo --- A/libfprint/drivers/aes1610.c 1970-01-01 01:00:00.000000000 +0100 +++ B/libfprint/drivers/aes1610.c 2009-10-09 00:21:11.000000000 +0200 @@ -0,0 +1,1131 @@ +/* + * AuthenTec AES1610 driver for libfprint + * Copyright (C) 2007-2008 Daniel Drake + * Copyright (C) 2007 Cyrille Bagard + * Copyright (C) 2007 Vasily Khoruzhick + * Copyright (C) 2009 Guido Grazioli + * + * Based on code from libfprint aes2501 driver. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "aes1610" + +#include +#include + +#include + +#include +#include + +static void start_capture(struct fp_img_dev *dev); +static void complete_deactivation(struct fp_img_dev *dev); +static int adjust_gain(unsigned char *buffer, int status); + +#define FIRST_AES1610_REG 0x1B +#define LAST_AES1610_REG 0xFF + +#define GAIN_STATUS_FIRST 1 +#define GAIN_STATUS_NORMAL 2 + +/* FIXME these need checking */ +#define EP_IN (1 | LIBUSB_ENDPOINT_IN) +#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) + +#define BULK_TIMEOUT 4000 + +/* + * The AES1610 is an imaging device using a swipe-type sensor. It samples + * the finger at preprogrammed intervals, sending a 128x8 frame to the + * computer. + * Unless the user is scanning their finger unreasonably fast, the frames + * *will* overlap. The implementation below detects this overlap and produces + * a contiguous image as the end result. + * The fact that the user determines the length of the swipe (and hence the + * number of useful frames) and also the fact that overlap varies means that + * images returned from this driver vary in height. + */ + +#define FRAME_WIDTH 128 +#define FRAME_HEIGHT 8 +#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT) +/* maximum number of frames to read during a scan */ +/* FIXME reduce substantially */ +#define MAX_FRAMES 350 + +/****** GENERAL FUNCTIONS ******/ + +struct aes1610_dev { + uint8_t read_regs_retry_count; + GSList *strips; + size_t strips_len; + gboolean deactivating; + uint8_t blanks_count; +}; + +typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status, + unsigned char *regs, void *user_data); + +struct aes1610_read_regs { + struct fp_img_dev *dev; + aes1610_read_regs_cb callback; + struct aes_regwrite *regwrite; + void *user_data; +}; + +/* FIXME: what to do here? */ +static void stub_capture_stop_cb(struct fp_img_dev *dev, int result, void *user_data) +{ + +} + + +/* check that read succeeded but ignore all data */ +static void generic_ignore_data_cb(struct libusb_transfer *transfer) +{ + struct fpi_ssm *ssm = transfer->user_data; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) + fpi_ssm_mark_aborted(ssm, -EIO); + else if (transfer->length != transfer->actual_length) + fpi_ssm_mark_aborted(ssm, -EPROTO); + else + fpi_ssm_next_state(ssm); + + g_free(transfer->buffer); + libusb_free_transfer(transfer); +} + + +static void read_regs_data_cb(struct libusb_transfer *transfer) +{ + struct aes1610_read_regs *rdata = transfer->user_data; + unsigned char *retdata = NULL; + int r; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + r = -EIO; + } else if (transfer->length != transfer->actual_length) { + r = -EPROTO; + } else { + r = 0; + retdata = transfer->buffer; + } + + rdata->callback(rdata->dev, r, retdata, rdata->user_data); + g_free(rdata); + g_free(transfer->buffer); + libusb_free_transfer(transfer); +} + +static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data) +{ + struct aes1610_read_regs *rdata = user_data; + struct libusb_transfer *transfer; + unsigned char *data; + int r; + + g_free(rdata->regwrite); + if (result != 0) + goto err; + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + result = -ENOMEM; + goto err; + } + + data = g_malloc(126); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126, + read_regs_data_cb, rdata, BULK_TIMEOUT); + + r = libusb_submit_transfer(transfer); + if (r < 0) { + g_free(data); + libusb_free_transfer(transfer); + result = -EIO; + goto err; + } + + return; +err: + rdata->callback(dev, result, NULL, rdata->user_data); + g_free(rdata); +} + + +// XXX: this comes from aes2501 driver but it is unused here +static void read_regs(struct fp_img_dev *dev, aes1610_read_regs_cb callback, + void *user_data) +{ + /* FIXME: regwrite is dynamic because of asynchronity. is this really + * required? */ + struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite)); + struct aes1610_read_regs *rdata = g_malloc(sizeof(*rdata)); + + fp_dbg(""); + //regwrite->reg = AES1610_REG_CTRL2; + //regwrite->value = AES1610_CTRL2_READ_REGS; + rdata->dev = dev; + rdata->callback = callback; + rdata->user_data = user_data; + rdata->regwrite = regwrite; + + //aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1, + // read_regs_rq_cb, rdata); +} + +/* Read the value of a specific register from a register dump */ +static int regval_from_dump(unsigned char *data, uint8_t target) +{ + if (*data != FIRST_AES1610_REG) { + fp_err("not a register dump"); + return -EILSEQ; + } + + if (!(FIRST_AES1610_REG <= target && target <= LAST_AES1610_REG)) { + fp_err("out of range"); + return -EINVAL; + } + + target -= FIRST_AES1610_REG; + target *= 2; + return data[target + 1]; +} + +static void generic_write_regv_cb(struct fp_img_dev *dev, int result, + void *user_data) +{ + struct fpi_ssm *ssm = user_data; + if (result == 0) + fpi_ssm_next_state(ssm); + else + fpi_ssm_mark_aborted(ssm, result); +} + + + +/* read the specified number of bytes from the IN endpoint but throw them + * away, then increment the SSM */ +static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes) +{ + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; + int r; + + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } + + data = g_malloc(bytes); + libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes, + generic_ignore_data_cb, ssm, BULK_TIMEOUT); + + r = libusb_submit_transfer(transfer); + if (r < 0) { + g_free(data); + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, r); + } +} + +/****** IMAGE PROCESSING ******/ + +static int sum_histogram_values(unsigned char *data, uint8_t threshold) +{ + int r = 0; + int i; + uint16_t *histogram = (uint16_t *)(data + 1); + + if (*data != 0xde) + return -EILSEQ; + + if (threshold > 0x0f) + return -EINVAL; + + /* FIXME endianness */ + for (i = threshold; i < 16; i++) + r += histogram[i]; + + return r; +} + +/* find overlapping parts of frames */ +static unsigned int find_overlap(unsigned char *first_frame, + unsigned char *second_frame, unsigned int *min_error) +{ + unsigned int dy; + unsigned int not_overlapped_height = 0; + *min_error = 255 * FRAME_SIZE; + for (dy = 0; dy < FRAME_HEIGHT; dy++) { + /* Calculating difference (error) between parts of frames */ + unsigned int i; + unsigned int error = 0; + for (i = 0; i < FRAME_WIDTH * (FRAME_HEIGHT - dy); i++) { + /* Using ? operator to avoid abs function */ + error += first_frame[i] > second_frame[i] ? + (first_frame[i] - second_frame[i]) : + (second_frame[i] - first_frame[i]); + } + + /* Normalize error */ + error *= 15; + error /= i; + if (error < *min_error) { + *min_error = error; + not_overlapped_height = dy; + } + first_frame += FRAME_WIDTH; + } + + return not_overlapped_height; +} + +/* assemble a series of frames into a single image */ +static unsigned int assemble(struct aes1610_dev *aesdev, unsigned char *output, + gboolean reverse, unsigned int *errors_sum) +{ + uint8_t *assembled = output; + int frame; + uint32_t image_height = FRAME_HEIGHT; + unsigned int min_error; + size_t num_strips = aesdev->strips_len; + GSList *list_entry = aesdev->strips; + *errors_sum = 0; + + if (num_strips < 1) + return 0; + + /* Rotating given data by 90 degrees + * Taken from document describing aes1610 image format + * TODO: move reversing detection here */ + + if (reverse) + output += (num_strips - 1) * FRAME_SIZE; + for (frame = 0; frame < num_strips; frame++) { + aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output); + + if (reverse) + output -= FRAME_SIZE; + else + output += FRAME_SIZE; + list_entry = g_slist_next(list_entry); + } + + /* Detecting where frames overlaped */ + output = assembled; + for (frame = 1; frame < num_strips; frame++) { + int not_overlapped; + + output += FRAME_SIZE; + not_overlapped = find_overlap(assembled, output, &min_error); + *errors_sum += min_error; + image_height += not_overlapped; + assembled += FRAME_WIDTH * not_overlapped; + memcpy(assembled, output, FRAME_SIZE); + } + return image_height; +} + +static void assemble_and_submit_image(struct fp_img_dev *dev) +{ + struct aes1610_dev *aesdev = dev->priv; + size_t final_size; + struct fp_img *img; + unsigned int errors_sum, r_errors_sum; + + fp_dbg(""); + + BUG_ON(aesdev->strips_len == 0); + + /* reverse list */ + aesdev->strips = g_slist_reverse(aesdev->strips); + + /* create buffer big enough for max image */ + img = fpi_img_new(aesdev->strips_len * FRAME_SIZE); + + img->flags = FP_IMG_COLORS_INVERTED; + img->height = assemble(aesdev, img->data, FALSE, &errors_sum); + img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum); + + if (r_errors_sum > errors_sum) { + img->height = assemble(aesdev, img->data, FALSE, &errors_sum); + img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED; + fp_dbg("normal scan direction"); + } else { + fp_dbg("reversed scan direction"); + } + + /* now that overlap has been removed, resize output image buffer */ + final_size = img->height * FRAME_WIDTH; + img = fpi_img_resize(img, final_size); + /* FIXME: ugly workaround */ + if (img->height < 12) img->height = 12; + fpi_imgdev_image_captured(dev, img); + + /* free strips and strip list */ + g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL); + g_slist_free(aesdev->strips); + aesdev->strips = NULL; + aesdev->strips_len = 0; + aesdev->blanks_count = 0; +} + + +/****** FINGER PRESENCE DETECTION ******/ + + +static const struct aes_regwrite finger_det_reqs[] = { + { 0x80, 0x01 }, + { 0x80, 0x12 }, + { 0x85, 0x00 }, + { 0x8A, 0x00 }, + { 0x8B, 0x0E }, + { 0x8C, 0x90 }, + { 0x8D, 0x83 }, + { 0x8E, 0x07 }, + { 0x8F, 0x07 }, + { 0x96, 0x00 }, + { 0x97, 0x48 }, + { 0xA1, 0x00 }, + { 0xA2, 0x50 }, + { 0xA6, 0xE4 }, + { 0xAD, 0x08 }, + { 0xAE, 0x5B }, + { 0xAF, 0x54 }, + { 0xB1, 0x28 }, + { 0xB5, 0xAB }, + { 0xB6, 0x0E }, + { 0x1B, 0x2D }, + { 0x81, 0x04 } +}; + +static const struct aes_regwrite finger_det_none[] = { + { 0x80, 0x01 }, + { 0x82, 0x00 }, + { 0x86, 0x00 }, + { 0xB1, 0x28 }, + { 0x1D, 0x00 } +}; + + +static void start_finger_detection(struct fp_img_dev *dev); + +static void finger_det_data_cb(struct libusb_transfer *transfer) +{ + struct fp_img_dev *dev = transfer->user_data; + unsigned char *data = transfer->buffer; + int i; + int sum = 0; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fpi_imgdev_session_error(dev, -EIO); + goto out; + } else if (transfer->length != transfer->actual_length) { + fpi_imgdev_session_error(dev, -EPROTO); + goto out; + } + + /* examine histogram to determine finger presence */ + for (i = 3; i < 17; i++) + sum += (data[i] & 0xf) + (data[i] >> 4); + if (sum > 20) { + /* reset default gain */ + adjust_gain(data,GAIN_STATUS_FIRST); + /* finger present, start capturing */ + fpi_imgdev_report_finger_status(dev, TRUE); + start_capture(dev); + } else { + /* no finger, poll for a new histogram */ + start_finger_detection(dev); + } + +out: + g_free(data); + libusb_free_transfer(transfer); +} + + +static void finger_det_none_cb(struct fp_img_dev *dev, int result, void *user_data) +{ + fpi_imgdev_report_finger_status(dev, FALSE); + start_finger_detection(dev); +} + +static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data) +{ + struct libusb_transfer *transfer; + unsigned char *data; + int r; + + if (result) { + fpi_imgdev_session_error(dev, result); + return; + } + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_imgdev_session_error(dev, -ENOMEM); + return; + } + + + data = g_malloc(19); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 19, + finger_det_data_cb, dev, BULK_TIMEOUT); + + r = libusb_submit_transfer(transfer); + if (r < 0) { + g_free(data); + libusb_free_transfer(transfer); + fpi_imgdev_session_error(dev, r); + } + +} + +static void start_finger_detection(struct fp_img_dev *dev) +{ + struct aes1610_dev *aesdev = dev->priv; + struct libusb_transfer *transfer; + + if (aesdev->deactivating) { + complete_deactivation(dev); + return; + } + + aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs), finger_det_reqs_cb, NULL); + +} + +/****** CAPTURE ******/ + +static struct aes_regwrite capture_reqs[] = { + { 0x80, 0x01 }, + { 0x80, 0x12 }, + { 0x84, 0x01 }, + { 0x85, 0x00 }, + { 0x89, 0x64 }, + { 0x8A, 0x00 }, + { 0x8B, 0x0E }, + { 0x8C, 0x90 }, + { 0xBE, 0x23 }, + { 0x29, 0x04 }, + { 0x2A, 0xFF }, + { 0x96, 0x00 }, + { 0x98, 0x03 }, + { 0x99, 0x00 }, + { 0x9C, 0xA5 }, + { 0x9D, 0x40 }, + { 0x9E, 0xC6 }, + { 0x9F, 0x8E }, + { 0xA2, 0x50 }, + { 0xA3, 0xF0 }, + { 0xAD, 0x08 }, + { 0xBD, 0x4F }, + { 0xAF, 0x54 }, + { 0xB1, 0x08 }, + { 0xB5, 0xAB }, + { 0x1B, 0x2D }, + { 0xB6, 0x4E }, + { 0xB8, 0x70 }, + { 0x2B, 0xB3 }, + { 0x2C, 0x5D }, + { 0x2D, 0x98 }, + { 0x2E, 0xB0 }, + { 0x2F, 0x20 }, + { 0xA2, 0xD0 }, + { 0x1D, 0x21 }, + { 0x1E, 0xBE }, + { 0x1C, 0x00 }, + { 0x1D, 0x30 }, + { 0x1E, 0x29 }, + { 0x1C, 0x01 }, + { 0x1D, 0x00 }, + { 0x1E, 0x9E }, + { 0x1C, 0x02 }, + { 0x1D, 0x30 }, + { 0x1E, 0xBB }, + { 0x1C, 0x03 }, + { 0x1D, 0x00 }, + { 0x1E, 0x9D }, + { 0x1C, 0x04 }, + { 0x1D, 0x22 }, + { 0x1E, 0xFF }, + { 0x1C, 0x05 }, + { 0x1D, 0x1B }, + { 0x1E, 0x4E }, + { 0x1C, 0x06 }, + { 0x1D, 0x16 }, + { 0x1E, 0x28 }, + { 0x1C, 0x07 }, + { 0x1D, 0x22 }, + { 0x1E, 0xFF }, + { 0x1C, 0x08 }, + { 0x1D, 0x15 }, + { 0x1E, 0xF1 }, + { 0x1C, 0x09 }, + { 0x1D, 0x30 }, + { 0x1E, 0xD5 }, + { 0x1C, 0x0A }, + { 0x1D, 0x00 }, + { 0x1E, 0x9E }, + { 0x1C, 0x0B }, + { 0x1D, 0x17 }, + { 0x1E, 0x9D }, + { 0x1C, 0x0C }, + { 0x1D, 0x28 }, + { 0x1E, 0xD7 }, + { 0x1C, 0x0D }, + { 0x1D, 0x17 }, + { 0x1E, 0xD7 }, + { 0x1C, 0x0E }, + { 0x1D, 0x0A }, + { 0x1E, 0xCB }, + { 0x1C, 0x0F }, + { 0x1D, 0x24 }, + { 0x1E, 0x14 }, + { 0x1C, 0x10 }, + { 0x1D, 0x17 }, + { 0x1E, 0x85 }, + { 0x1C, 0x11 }, + { 0x1D, 0x15 }, + { 0x1E, 0x71 }, + { 0x1C, 0x12 }, + { 0x1D, 0x2B }, + { 0x1E, 0x36 }, + { 0x1C, 0x13 }, + { 0x1D, 0x12 }, + { 0x1E, 0x06 }, + { 0x1C, 0x14 }, + { 0x1D, 0x30 }, + { 0x1E, 0x97 }, + { 0x1C, 0x15 }, + { 0x1D, 0x21 }, + { 0x1E, 0x32 }, + { 0x1C, 0x16 }, + { 0x1D, 0x06 }, + { 0x1E, 0xE6 }, + { 0x1C, 0x17 }, + { 0x1D, 0x16 }, + { 0x1E, 0x06 }, + { 0x1C, 0x18 }, + { 0x1D, 0x30 }, + { 0x1E, 0x01 }, + { 0x1C, 0x19 }, + { 0x1D, 0x21 }, + { 0x1E, 0x37 }, + { 0x1C, 0x1A }, + { 0x1D, 0x00 }, + { 0x1E, 0x08 }, + { 0x1C, 0x1B }, + { 0x1D, 0x80 }, + { 0x1E, 0xD5 }, + { 0xA2, 0x50 }, + { 0xA2, 0x50 }, + { 0x81, 0x01 } +}; + +static struct aes_regwrite strip_scan_reqs[] = { + { 0xBE, 0x23 }, + { 0x29, 0x04 }, + { 0x2A, 0xFF }, + { 0xBD, 0x4F }, + { 0xFF, 0x00 } +}; + +static const struct aes_regwrite capture_stop[] = { + { 0x81,0x00 } +}; + + + + + +/* + * The different possible values for 0xBE register */ +static unsigned char list_BE_values[10] = { + 0x23, 0x43, 0x63, 0x64, 0x65, 0x67, 0x6A, 0x6B +}; + +/* + * The different possible values for 0xBD register */ +static unsigned char list_BD_values[10] = { + 0x48, 0x4B, 0x4F, 0x52, 0x57, 0x59, 0x5B +}; + +/* + * Adjust the gain according to the histogram data + * 0xbd, 0xbe, 0x29 and 0x2A registers are affected + * Returns 0 if no problem occured + * TODO: This is a basic support for gain. It needs testing/tweaking. */ +static int adjust_gain(unsigned char *buffer, int status) +{ + // The position in the array of possible values for 0xBE and 0xBD registers + static int pos_list_BE = 0; + static int pos_list_BD = 0; + + // This is the first adjustement (we begin acquisition) + // We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step + if (status == GAIN_STATUS_FIRST) { + if (buffer[1] > 0x78) { // maximum gain needed + strip_scan_reqs[0].value = 0x6B; + strip_scan_reqs[1].value = 0x06; + strip_scan_reqs[2].value = 0x35; + strip_scan_reqs[3].value = 0x5B; + } + else if (buffer[1] > 0x55) { + strip_scan_reqs[0].value = 0x63; + strip_scan_reqs[1].value = 0x15; + strip_scan_reqs[2].value = 0x35; + strip_scan_reqs[3].value = 0x4F; + } + else if (buffer[1] > 0x40 || buffer[16] > 0x19) { + strip_scan_reqs[0].value = 0x43; + strip_scan_reqs[1].value = 0x13; + strip_scan_reqs[2].value = 0x35; + strip_scan_reqs[3].value = 0x4B; + } + else { // minimum gain needed + strip_scan_reqs[0].value = 0x23; + strip_scan_reqs[1].value = 0x07; + strip_scan_reqs[2].value = 0x35; + strip_scan_reqs[3].value = 0x48; + } + + // Now copy this values in capture_reqs + capture_reqs[8].value = strip_scan_reqs[0].value; + capture_reqs[9].value = strip_scan_reqs[1].value; + capture_reqs[10].value = strip_scan_reqs[2].value; + capture_reqs[21].value = strip_scan_reqs[3].value; + + fp_dbg("first gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value); + } + + // Every 2/3 strips + // We try to soften big changes of the gain (at least for 0xBE and 0xBD + // FIXME: This softenning will need testing and tweaking too + else if (status == GAIN_STATUS_NORMAL) { + if (buffer[514] > 0x78) { // maximum gain needed + if (pos_list_BE < 7) + pos_list_BE++; + + if (pos_list_BD < 6) + pos_list_BD++; + + strip_scan_reqs[1].value = 0x04; + strip_scan_reqs[2].value = 0x35; + } + else if (buffer[514] > 0x55) { + if (pos_list_BE < 2) + pos_list_BE++; + else if (pos_list_BE > 2) + pos_list_BE--; + + if (pos_list_BD < 2) + pos_list_BD++; + else if (pos_list_BD > 2) + pos_list_BD--; + + strip_scan_reqs[1].value = 0x15; + strip_scan_reqs[2].value = 0x35; + } + else if (buffer[514] > 0x40 || buffer[529] > 0x19) { + if (pos_list_BE < 1) + pos_list_BE++; + else if (pos_list_BE > 1) + pos_list_BE--; + + if (pos_list_BD < 1) + pos_list_BD++; + else if (pos_list_BD > 1) + pos_list_BD--; + + strip_scan_reqs[1].value = 0x13; + strip_scan_reqs[2].value = 0x35; + } + else { // minimum gain needed + if (pos_list_BE > 0) + pos_list_BE--; + + if (pos_list_BD > 0) + pos_list_BD--; + + strip_scan_reqs[1].value = 0x07; + strip_scan_reqs[2].value = 0x35; + } + + strip_scan_reqs[0].value = list_BE_values[pos_list_BE]; + strip_scan_reqs[3].value = list_BD_values[pos_list_BD]; + + fp_dbg("gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value); + } + + // Unknown status + else { + fp_err("Unexpected gain status."); + return 1; + } + + return 0; +} + +/* + * Restore the default gain values */ +static void restore_gain() +{ + strip_scan_reqs[0].value = list_BE_values[0]; + strip_scan_reqs[1].value = 0x04; + strip_scan_reqs[2].value = 0xFF; + strip_scan_reqs[3].value = list_BD_values[0]; + + capture_reqs[8].value = list_BE_values[0]; + capture_reqs[9].value = 0x04; + capture_reqs[10].value = 0xFF; + capture_reqs[21].value = list_BD_values[0]; +} + + +/* capture SM movement: + * request and read strip, + * jump back to request UNLESS theres no finger, in which case exit SM, + * report lack of finger presence, and move to finger detection */ + +enum capture_states { + CAPTURE_WRITE_REQS, + CAPTURE_READ_DATA, + CAPTURE_REQUEST_STRIP, + CAPTURE_READ_STRIP, + CAPTURE_NUM_STATES, +}; + +static void capture_read_strip_cb(struct libusb_transfer *transfer) +{ + unsigned char *stripdata; + struct fpi_ssm *ssm = transfer->user_data; + struct fp_img_dev *dev = ssm->priv; + struct aes1610_dev *aesdev = dev->priv; + unsigned char *data = transfer->buffer; + int sum, i; + int threshold; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fpi_ssm_mark_aborted(ssm, -EIO); + goto out; + } else if (transfer->length != transfer->actual_length) { + fpi_ssm_mark_aborted(ssm, -EPROTO); + goto out; + } + + /* FIXME: would preallocating strip buffers be a decent optimization? */ + //stripdata = g_malloc(128 * 4); + //memcpy(stripdata, data + 1, 128 * 4); + //aesdev->strips = g_slist_prepend(aesdev->strips, stripdata); + //aesdev->strips_len++; + + /*threshold = regval_from_dump(data + 1 + 128*8 + 1 + 16*2 + 1 + 8, + 0x97); + if (threshold < 0) { + fpi_ssm_mark_aborted(ssm, threshold); + goto out; + }*/ + + sum = 0; + for (i = 516; i < 530; i++) + { + /* histogram[i] = number of pixels of value i + Only the pixel values from 10 to 15 are used to detect finger. */ + sum += data[i]; + } + + if (sum > 0) { + /* FIXME: would preallocating strip buffers be a decent optimization? */ + stripdata = g_malloc(128 * 4); + memcpy(stripdata, data + 1, 128 * 4); + aesdev->strips = g_slist_prepend(aesdev->strips, stripdata); + aesdev->strips_len++; + aesdev->blanks_count = 0; + } + + if (sum < 0) { + fpi_ssm_mark_aborted(ssm, sum); + goto out; + } + fp_dbg("sum=%d", sum); + + /* FIXME: 0 might be too low as a threshold */ + /* FIXME: sometimes we get 0 in the middle of a scan, should we wait for + * a few consecutive zeroes? */ + + /* If sum is 0 for a reasonable # of frames, finger has been removed */ + if (sum == 0) { + aesdev->blanks_count++; + fp_dbg("got blank frame"); + } + + /* use histogram data above for gain calibration (0xbd, 0xbe, 0x29 and 0x2A ) */ + adjust_gain(data, GAIN_STATUS_NORMAL); + + /* stop capturing if MAX_FRAMES is reached */ + if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) { + fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips)); + /* send stop capture bits */ + aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL); + /* assemble image and submit it to library */ + assemble_and_submit_image(dev); + fpi_imgdev_report_finger_status(dev, FALSE); + /* marking machine complete will re-trigger finger detection loop */ + fpi_ssm_mark_completed(ssm); + /* Acquisition finished: restore default gain values */ + restore_gain(); + } else { + /* obtain next strip */ + fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP); + } + +out: + g_free(data); + libusb_free_transfer(transfer); +} + +static void capture_run_state(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + struct aes1610_dev *aesdev = dev->priv; + int r; + + switch (ssm->cur_state) { + case CAPTURE_WRITE_REQS: + fp_dbg("write reqs"); + aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs), + generic_write_regv_cb, ssm); + break; + case CAPTURE_READ_DATA: + fp_dbg("read data"); + generic_read_ignore_data(ssm, 665); + break; + case CAPTURE_REQUEST_STRIP: + fp_dbg("request strip"); + if (aesdev->deactivating) + fpi_ssm_mark_completed(ssm); + else + aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs), + generic_write_regv_cb, ssm); + break; + case CAPTURE_READ_STRIP: ; + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; + + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + break; + } + + data = g_malloc(665); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 665, + capture_read_strip_cb, ssm, BULK_TIMEOUT); + + r = libusb_submit_transfer(transfer); + if (r < 0) { + g_free(data); + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, r); + } + break; + }; +} + +static void capture_sm_complete(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + struct aes1610_dev *aesdev = dev->priv; + + fp_dbg(""); + if (aesdev->deactivating) + complete_deactivation(dev); + else if (ssm->error) + fpi_imgdev_session_error(dev, ssm->error); + else + start_finger_detection(dev); + fpi_ssm_free(ssm); +} + +static void start_capture(struct fp_img_dev *dev) +{ + struct aes1610_dev *aesdev = dev->priv; + struct fpi_ssm *ssm; + + if (aesdev->deactivating) { + complete_deactivation(dev); + return; + } + + ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES); + fp_dbg(""); + ssm->priv = dev; + fpi_ssm_start(ssm, capture_sm_complete); +} + +/****** INITIALIZATION/DEINITIALIZATION ******/ + +static const struct aes_regwrite init[] = { + { 0x82, 0x00 } +}; + +static const struct aes_regwrite stop_reader[] = { + { 0xFF, 0x00 } +}; + + +enum activate_states { + WRITE_INIT, +// READ_DATA, +// READ_REGS, + ACTIVATE_NUM_STATES, +}; + +/* this come from aes2501 and is unused here +void activate_read_regs_cb(struct fp_img_dev *dev, int status, + unsigned char *regs, void *user_data) +{ + struct fpi_ssm *ssm = user_data; + struct aes1610_dev *aesdev = dev->priv; + + if (status != 0) { + fpi_ssm_mark_aborted(ssm, status); + } else { + fpi_ssm_next_state(ssm); + } +} +*/ + +static void activate_run_state(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + + /* activation on aes1610 seems much more straightforward compared to aes2501 */ + /* verify theres anything missing here */ + switch (ssm->cur_state) { + case WRITE_INIT: + fp_dbg("write init"); + aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm); + break; +/* case READ_DATA: + fp_dbg("read data"); + generic_read_ignore_data(ssm, 20); + break; + case READ_REGS: + fp_dbg("read regs"); + read_regs(dev, activate_read_regs_cb, ssm); + break;*/ + } +} + +/* jump to finger detection */ +static void activate_sm_complete(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + fp_dbg("status %d", ssm->error); + fpi_imgdev_activate_complete(dev, ssm->error); + + if (!ssm->error) + start_finger_detection(dev); + fpi_ssm_free(ssm); +} + +static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) +{ + struct aes1610_dev *aesdev = dev->priv; + struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state, + ACTIVATE_NUM_STATES); + ssm->priv = dev; + aesdev->read_regs_retry_count = 0; + fpi_ssm_start(ssm, activate_sm_complete); + return 0; +} + +static void dev_deactivate(struct fp_img_dev *dev) +{ + struct aes1610_dev *aesdev = dev->priv; + /* FIXME: audit cancellation points, probably need more, specifically + * in error handling paths? */ + aesdev->deactivating = TRUE; +} + +static void complete_deactivation(struct fp_img_dev *dev) +{ + struct aes1610_dev *aesdev = dev->priv; + fp_dbg(""); + + /* FIXME: if we're in the middle of a scan, we should cancel the scan. + * maybe we can do this with a master reset, unconditionally? */ + + aesdev->deactivating = FALSE; + g_slist_free(aesdev->strips); + aesdev->strips = NULL; + aesdev->strips_len = 0; + aesdev->blanks_count = 0; + fpi_imgdev_deactivate_complete(dev); +} + +static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) +{ + /* FIXME check endpoints */ + 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 aes1610_dev)); + fpi_imgdev_open_complete(dev, 0); + return 0; +} + +static void dev_deinit(struct fp_img_dev *dev) +{ + g_free(dev->priv); + libusb_release_interface(dev->udev, 0); + fpi_imgdev_close_complete(dev); +} + +static const struct usb_id id_table[] = { + { .vendor = 0x08ff, .product = 0x1600 }, /* AES1600 */ + { 0, 0, 0, }, +}; + +struct fp_img_driver aes1610_driver = { + .driver = { + .id = 6, + .name = FP_COMPONENT, + .full_name = "AuthenTec AES1610", + .id_table = id_table, + .scan_type = FP_SCAN_TYPE_SWIPE, + }, + .flags = 0, + .img_height = -1, + .img_width = 128, + .bz3_threshold = 10, + + .open = dev_init, + .close = dev_deinit, + .activate = dev_activate, + .deactivate = dev_deactivate, +}; +