diff --git a/configure.ac b/configure.ac index 052ef0e..a892a96 100644 --- a/configure.ac +++ b/configure.ac @@ -89,7 +89,7 @@ AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" != "no"]) 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_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"]) diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index c79012b..8316796 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -100,9 +100,9 @@ endif #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) diff --git a/libfprint/core.c b/libfprint/core.c index 37a4e03..724d5e5 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -361,11 +361,11 @@ static struct fp_img_driver * const img_drivers[] = { #ifdef ENABLE_UPEKSONLY &upeksonly_driver, #endif - /* + #ifdef ENABLE_AES1610 &aes1610_driver, #endif -#ifdef ENABLE_UPEKTC +/*#ifdef ENABLE_UPEKTC &upektc_driver, #endif #ifdef ENABLE_FDU2000 diff --git a/libfprint/drivers/aes1610.c b/libfprint/drivers/aes1610.c index 318195f..8b81a80 100644 --- a/libfprint/drivers/aes1610.c +++ b/libfprint/drivers/aes1610.c @@ -1,11 +1,11 @@ /* * AuthenTec AES1610 driver for libfprint - * Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net> - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> + * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org> * Copyright (C) 2007 Cyrille Bagard * Copyright (C) 2007 Vasily Khoruzhick + * Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com> * - * Based on code from http://home.gna.org/aes2501, relicensed with permission + * 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 @@ -32,15 +32,22 @@ #include <aeslib.h> #include <fp_internal.h> +static void start_capture(struct fp_img_dev *dev); +static void complete_deactivation(struct fp_img_dev *dev); +static int adjust_gain(unsigned char *buffer, int status); + +#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 -#define FIRST_AES1610_REG 0x1B -#define LAST_AES1610_REG 0xFF - /* * The AES1610 is an imaging device using a swipe-type sensor. It samples * the finger at preprogrammed intervals, sending a 128x8 frame to the @@ -53,140 +60,212 @@ * images returned from this driver vary in height. */ -#define FRAME_WIDTH 128 +#define FRAME_WIDTH 128 #define FRAME_HEIGHT 8 -#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT) +#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT) /* maximum number of frames to read during a scan */ /* FIXME reduce substantially */ #define MAX_FRAMES 350 -static int read_data(struct fp_img_dev *dev, unsigned char *data, size_t len) +/****** 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) { - int r; - int transferred; - struct libusb_bulk_transfer msg = { - .endpoint = EP_IN, - .data = data, - .length = len, - }; - fp_dbg("len=%zd", len); - r = libusb_bulk_transfer(dev->udev, &msg, &transferred, BULK_TIMEOUT); - if (r < 0) { - fp_err("bulk read error %d", r); - return r; - } else if (transferred < len) { - fp_err("unexpected short read %d/%zd", r, len); - return -EIO; - } - return 0; } -static const struct aes_regwrite init[] = { - { 0x82, 0x00 } -}; -static const struct aes_regwrite stop_reader[] = { - { 0xFF, 0x00 } -}; +/* 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; -static int dev_init(struct fp_img_dev *dev, unsigned long driver_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; - r = libusb_claim_interface(dev->udev, 0); + 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) { - fp_err("could not claim interface 0"); - return r; + g_free(data); + libusb_free_transfer(transfer); + result = -EIO; + goto err; } - /* FIXME check endpoints */ + return; +err: + rdata->callback(dev, result, NULL, rdata->user_data); + g_free(rdata); +} - return aes_write_regv(dev, init, G_N_ELEMENTS(init)); + +// 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); } -static int do_exit(struct fp_img_dev *dev) +/* Read the value of a specific register from a register dump */ +static int regval_from_dump(unsigned char *data, uint8_t target) { - return aes_write_regv(dev, stop_reader, G_N_ELEMENTS(stop_reader)); + 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 dev_exit(struct fp_img_dev *dev) +static void generic_write_regv_cb(struct fp_img_dev *dev, int result, + void *user_data) { - do_exit(dev); - libusb_release_interface(dev->udev, 0); + struct fpi_ssm *ssm = user_data; + if (result == 0) + fpi_ssm_next_state(ssm); + else + fpi_ssm_mark_aborted(ssm, result); } -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 int detect_finger(struct fp_img_dev *dev) +/* 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) { - unsigned char buffer[19]; + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; int r; - int i; - int sum = 0; - r = aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs)); - if (r < 0) - return r; + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } - r = read_data(dev, buffer, 19); - if (r < 0) - return r; + data = g_malloc(bytes); + libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes, + generic_ignore_data_cb, ssm, BULK_TIMEOUT); - for (i = 3; i < 17; i++) - sum += (buffer[i] & 0xf) + (buffer[i] >> 4); - - /* We need to answer something if no finger has been detected */ - if (sum <= 20) { - r = aes_write_regv(dev, finger_det_none, G_N_ELEMENTS(finger_det_none)); - if (r < 0) - return r; + r = libusb_submit_transfer(transfer); + if (r < 0) { + g_free(data); + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, r); } - - return sum > 20; } -static int await_finger_on(struct fp_img_dev *dev) +/****** IMAGE PROCESSING ******/ + +static int sum_histogram_values(unsigned char *data, uint8_t threshold) { - int r; - do { - r = detect_finger(dev); - } while (r == 0); - return (r < 0) ? r : 0; + 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 */ +/* find overlapping parts of frames */ static unsigned int find_overlap(unsigned char *first_frame, unsigned char *second_frame, unsigned int *min_error) { @@ -199,11 +278,11 @@ static unsigned int find_overlap(unsigned char *first_frame, 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]); + 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; @@ -213,37 +292,39 @@ static unsigned int find_overlap(unsigned char *first_frame, } first_frame += FRAME_WIDTH; } - - return not_overlapped_height; + + return not_overlapped_height; } /* assemble a series of frames into a single image */ -static unsigned int assemble(unsigned char *input, unsigned char *output, - int num_strips, gboolean reverse, unsigned int *errors_sum) +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 + + /* 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(input, FRAME_WIDTH, FRAME_HEIGHT, output); - input += FRAME_WIDTH * (FRAME_HEIGHT / 2); + aes_assemble_image(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output); if (reverse) - output -= FRAME_SIZE; + output -= FRAME_SIZE; else - output += FRAME_SIZE; + output += FRAME_SIZE; + list_entry = g_slist_next(list_entry); } /* Detecting where frames overlaped */ @@ -256,12 +337,183 @@ static unsigned int assemble(unsigned char *input, unsigned char *output, *errors_sum += min_error; image_height += not_overlapped; assembled += FRAME_WIDTH * not_overlapped; - memcpy(assembled, output, FRAME_SIZE); + memcpy(assembled, output, FRAME_SIZE); } return image_height; } -static const struct aes_regwrite capture_reqs[] = { +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)