From 83333bce3f06ca55651a7516666ee63a5892f09f Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 9 Oct 2012 01:20:44 +0300 Subject: [PATCH] lib: Add AES2550/AES2810 driver Initial implementation of AES2550/AES2810 driver. Does not support AES2810 crypto engine and external flash. https://bugs.freedesktop.org/show_bug.cgi?id=50859 --- configure.ac | 14 +- libfprint/Makefile.am | 6 + libfprint/core.c | 3 + libfprint/drivers/aes2550.c | 741 ++++++++++++++++++++++++++++++++++++ libfprint/drivers/aes2550.h | 114 ++++++ libfprint/fp_internal.h | 3 + 6 files changed, 880 insertions(+), 1 deletion(-) create mode 100644 libfprint/drivers/aes2550.c create mode 100644 libfprint/drivers/aes2550.h diff --git a/configure.ac b/configure.ac index a2bd3c2..90cb0f6 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ AC_SUBST(lt_major) AC_SUBST(lt_revision) AC_SUBST(lt_age) -all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000 vfs101 vfs301" +all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes2550 aes4000 vfs101 vfs301" require_imaging='no' require_aeslib='no' @@ -36,6 +36,7 @@ enable_uru4000='no' enable_fdu2000='no' enable_aes1610='no' enable_aes2501='no' +enable_aes2550='no' enable_aes4000='no' enable_vfs101='no' enable_vfs301='no' @@ -82,6 +83,11 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do require_aeslib="yes" enable_aes2501="yes" ;; + aes2550) + AC_DEFINE([ENABLE_AES2550], [], [Build AuthenTec AES2550/AES2810 driver]) + require_aeslib="yes" + enable_aes2550="yes" + ;; aes1610) AC_DEFINE([ENABLE_AES1610], [], [Build AuthenTec AES1610 driver]) require_aeslib="yes" @@ -113,6 +119,7 @@ AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" = "yes"]) AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" = "yes"]) AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" = "yes"]) AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"]) +AM_CONDITIONAL([ENABLE_AES2550], [test "$enable_aes2550" = "yes"]) AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"]) AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"]) AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"]) @@ -294,6 +301,11 @@ if test x$enable_aes2501 != xno ; then else AC_MSG_NOTICE([ aes2501 driver disabled]) fi +if test x$enable_aes2550 != xno ; then + AC_MSG_NOTICE([** aes2550/aes2810 driver enabled]) +else + AC_MSG_NOTICE([ aes2550/aes2810 driver disabled]) +fi if test x$enable_aes4000 != xno ; then AC_MSG_NOTICE([** aes4000 driver enabled]) else diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index 96489e6..5ecf062 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -9,6 +9,7 @@ UPEKSONLY_SRC = drivers/upeksonly.c URU4000_SRC = drivers/uru4000.c AES1610_SRC = drivers/aes1610.c AES2501_SRC = drivers/aes2501.c drivers/aes2501.h +AES2550_SRC = drivers/aes2550.c drivers/aes2550.h AES4000_SRC = drivers/aes4000.c FDU2000_SRC = drivers/fdu2000.c VCOM5S_SRC = drivers/vcom5s.c @@ -23,6 +24,7 @@ EXTRA_DIST = \ $(URU4000_SRC) \ $(AES1610_SRC) \ $(AES2501_SRC) \ + $(AES2550_SRC) \ $(AES4000_SRC) \ $(FDU2000_SRC) \ $(VCOM5S_SRC) \ @@ -124,6 +126,10 @@ if ENABLE_AES2501 DRIVER_SRC += $(AES2501_SRC) endif +if ENABLE_AES2550 +DRIVER_SRC += $(AES2550_SRC) +endif + if ENABLE_AES4000 DRIVER_SRC += $(AES4000_SRC) endif diff --git a/libfprint/core.c b/libfprint/core.c index 3a430b7..9916faa 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -355,6 +355,9 @@ static struct fp_img_driver * const img_drivers[] = { #ifdef ENABLE_AES2501 &aes2501_driver, #endif +#ifdef ENABLE_AES2550 + &aes2550_driver, +#endif #ifdef ENABLE_URU4000 &uru4000_driver, #endif diff --git a/libfprint/drivers/aes2550.c b/libfprint/drivers/aes2550.c new file mode 100644 index 0000000..92c988e --- /dev/null +++ b/libfprint/drivers/aes2550.c @@ -0,0 +1,741 @@ +/* + * AuthenTec AES2550/AES2810 driver for libfprint + * Copyright (C) 2007-2008 Daniel Drake + * Copyright (C) 2007 Cyrille Bagard + * Copyright (C) 2007-2012 Vasily Khoruzhick + * + * Based on 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 "aes2550" + +#include +#include + +#include + +#include +#include +#include "aes2550.h" + +static void start_capture(struct fp_img_dev *dev); +static void complete_deactivation(struct fp_img_dev *dev); + +#define EP_IN (1 | LIBUSB_ENDPOINT_IN) +#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT) +#define BULK_TIMEOUT 4000 + +/* + * The AES2550 is an imaging device using a swipe-type sensor. It samples + * the finger at preprogrammed intervals, sending a 192x16 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 192 +#define FRAME_HEIGHT 8 +#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT) + +struct aes2550_dev { + GSList *strips; + size_t strips_len; + gboolean deactivating; +}; + +/****** IMAGE PROCESSING ******/ + +/* 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; + /* 255 is highest brightness value for an 8bpp image */ + *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 aes2550_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 (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 aes2550_dev *aesdev = dev->priv; + size_t final_size; + struct fp_img *img; + unsigned int errors_sum, r_errors_sum; + + 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); + 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; +} + + +/****** FINGER PRESENCE DETECTION ******/ + +static unsigned char finger_det_reqs[] = { + 0x80, AES2550_REG80_MASTER_RESET, + 0x95, (8 << AES2550_REG95_COL_SCANNED_OFS) | (1 << AES2550_REG95_EPIX_AVG_OFS), + 0xad, 0x00, + 0xbd, (0 << AES2550_REGBD_LPO_IN_15_8_OFS), + 0xbe, (0 << AES2550_REGBE_LPO_IN_7_0_OFS), + 0xcf, AES2550_REGCF_INTERFERENCE_CHK_EN, + AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */ + AES2550_CMD_RUN_FD, +}; + +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; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fp_dbg("data transfer status %d\n", transfer->status); + fpi_imgdev_session_error(dev, -EIO); + goto out; + } + + fp_dbg("transfer completed, len: %.4x, data: %.2x %.2x", + transfer->actual_length, (int)data[0], (int)data[1]); + + /* Check if we got 2 bytes, reg address 0x83 and its value */ + if ((transfer->actual_length >= 2) && (data[0] == 0x83) && (data[1] & AES2550_REG83_FINGER_PRESENT)) { + /* 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_reqs_cb(struct libusb_transfer *t) +{ + struct libusb_transfer *transfer; + unsigned char *data; + int r; + struct fp_img_dev *dev = t->user_data; + + if (t->status != LIBUSB_TRANSFER_COMPLETED) { + fp_dbg("req transfer status %d\n", t->status); + fpi_imgdev_session_error(dev, -EIO); + goto exit_free_transfer; + } else if (t->length != t->actual_length) { + fp_dbg("expected %d, got %d bytes", t->length, t->actual_length); + fpi_imgdev_session_error(dev, -EPROTO); + goto exit_free_transfer; + } + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_imgdev_session_error(dev, -ENOMEM); + goto exit_free_transfer; + } + + /* 2 bytes of result */ + data = g_malloc(AES2550_EP_IN_BUF_SIZE); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE, + 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); + } +exit_free_transfer: + libusb_free_transfer(t); +} + +static void start_finger_detection(struct fp_img_dev *dev) +{ + int r; + struct aes2550_dev *aesdev = dev->priv; + struct libusb_transfer *transfer; + fp_dbg(""); + + if (aesdev->deactivating) { + complete_deactivation(dev); + return; + } + + transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_imgdev_session_error(dev, -ENOMEM); + return; + } + libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, finger_det_reqs, + sizeof(finger_det_reqs), finger_det_reqs_cb, dev, BULK_TIMEOUT); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + fpi_imgdev_session_error(dev, r); + } +} + +/****** CAPTURE ******/ + +static unsigned char capture_reqs[] = { + 0x80, AES2550_REG80_MASTER_RESET, + 0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_HGC_ENABLE), + 0x85, AES2550_REG85_FLUSH_PER_FRAME, + 0x8f, AES2550_REG8F_AUTH_DISABLE | AES2550_REG8F_EHISTO_DISABLE, + 0xbf, AES2550_REGBF_RSR_DIR_UPDOWN_MOTION | AES2550_REGBF_RSR_LEVEL_SUPER_RSR, + 0xcf, (3 << AES2550_REGCF_INTERFERENCE_AVG_OFFS) | AES2550_REGCF_INTERFERENCE_AVG_EN, + 0xdc, (1 << AES2550_REGDC_BP_NUM_REF_SWEEP_OFS), + AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x03, /* Heart beat cmd, 3 * 16 cycles without sending image */ + AES2550_CMD_GET_ENROLL_IMG, +}; + +static unsigned char capture_set_idle_reqs[] = { + 0x80, AES2550_REG80_MASTER_RESET, + AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */ + AES2550_CMD_SET_IDLE_MODE, +}; + +enum capture_states { + CAPTURE_WRITE_REQS, + CAPTURE_READ_DATA, + CAPTURE_SET_IDLE, + CAPTURE_NUM_STATES, +}; + +/* Returns number of processed bytes */ +static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data) +{ + unsigned char *stripdata; + struct fp_img_dev *dev = ssm->priv; + struct aes2550_dev *aesdev = dev->priv; + int len; + + if (data[0] != AES2550_EDATA_MAGIC) { + fp_dbg("Bogus magic: %.2x\n", (int)(data[0])); + return -EPROTO; + } + len = data[1] * 256 + data[2]; + if (len != (AES2550_STRIP_SIZE - 3)) { + fp_dbg("Bogus frame len: %.4x\n", len); + } + stripdata = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2); /* 4 bits per pixel */ + if (!stripdata) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return -ENOMEM; + } + memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2); + aesdev->strips = g_slist_prepend(aesdev->strips, stripdata); + aesdev->strips_len++; + + return 0; +} + +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_next_state(ssm); + } else { + fpi_ssm_mark_aborted(ssm, -EIO); + } + libusb_free_transfer(transfer); +} + +static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer) +{ + struct fpi_ssm *ssm = transfer->user_data; + struct fp_img_dev *dev = ssm->priv; + + if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) && + (transfer->length == transfer->actual_length)) { + 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); + } else { + fpi_ssm_mark_aborted(ssm, -EIO); + } + libusb_free_transfer(transfer); +} + +static void capture_read_data_cb(struct libusb_transfer *transfer) +{ + struct fpi_ssm *ssm = transfer->user_data; + unsigned char *data = transfer->buffer; + int r; + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + fp_dbg("request is not completed, %d", transfer->status); + fpi_ssm_mark_aborted(ssm, -EIO); + goto out; + } + + fp_dbg("request completed, len: %.4x", transfer->actual_length); + if (transfer->actual_length >= 2) + fp_dbg("data: %.2x %.2x", (int)data[0], (int)data[1]); + + switch (transfer->actual_length) { + case AES2550_STRIP_SIZE: + r = process_strip_data(ssm, data); + if (r < 0) { + fp_dbg("Processing strip data failed: %d", r); + fpi_ssm_mark_aborted(ssm, -EPROTO); + goto out; + } + fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA); + break; + case AES2550_HEARTBEAT_SIZE: + if (data[0] == AES2550_HEARTBEAT_MAGIC) { + /* No data for a long time, looks like finger was removed (or no movement) */ + /* assemble image and submit it to library */ + fp_dbg("Got heartbeat => last frame"); + fpi_ssm_next_state(ssm); + } + break; + default: + fp_dbg("Short frame %d, skip", transfer->actual_length); + fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA); + break; + } +out: + g_free(transfer->buffer); + libusb_free_transfer(transfer); +} + +static void capture_run_state(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + int r; + + switch (ssm->cur_state) { + case CAPTURE_WRITE_REQS: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } + libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_reqs, + sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, -ENOMEM); + } + } + break; + case CAPTURE_READ_DATA: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; + + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + break; + } + + data = g_malloc(AES2550_EP_IN_BUF_SIZE); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE, + capture_read_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); + } + } + break; + case CAPTURE_SET_IDLE: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } + libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_set_idle_reqs, + sizeof(capture_set_idle_reqs), capture_set_idle_reqs_cb, ssm, BULK_TIMEOUT); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, -ENOMEM); + } + } + break; + }; +} + +static void capture_sm_complete(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + struct aes2550_dev *aesdev = dev->priv; + + fp_dbg("Capture completed"); + 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 aes2550_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 unsigned char init_reqs[] = { + 0x80, AES2550_REG80_MASTER_RESET, /* Master reset */ + 0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_FORCE_FINGER_PRESENT), + 0x85, AES2550_REG85_FLUSH_PER_FRAME, + 0xa8, AES2550_REGA8_DIG_BIT_EN, + 0x81, AES2550_REG81_NSHOT, +}; + +static unsigned char calibrate_reqs[] = { + 0x80, AES2550_REG80_MASTER_RESET, /* Master reset */ + AES2550_CMD_CALIBRATE, + AES2550_CMD_READ_CALIBRATION_DATA, +}; + +enum activate_states { + WRITE_INIT, + READ_DATA, + CALIBRATE, + READ_CALIB_TABLE, + ACTIVATE_NUM_STATES, +}; + +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); + } + libusb_free_transfer(transfer); +} + +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); + } + g_free(transfer->buffer); + libusb_free_transfer(transfer); +} + +/* TODO: use calibration table, datasheet is rather terse on that + * need more info for implementaion */ +static void calibrate_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); + } + g_free(transfer->buffer); + libusb_free_transfer(transfer); +} + +static void activate_run_state(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = ssm->priv; + int r; + + switch (ssm->cur_state) { + case WRITE_INIT: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } + libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, init_reqs, + sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, -ENOMEM); + } + } + break; + case READ_DATA: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; + + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + break; + } + + data = g_malloc(AES2550_EP_IN_BUF_SIZE); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE, + init_read_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); + } + } + break; + case CALIBRATE: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + return; + } + libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, calibrate_reqs, + sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT); + r = libusb_submit_transfer(transfer); + if (r < 0) { + libusb_free_transfer(transfer); + fpi_ssm_mark_aborted(ssm, -ENOMEM); + } + } + break; + case READ_CALIB_TABLE: + { + struct libusb_transfer *transfer = libusb_alloc_transfer(0); + unsigned char *data; + + if (!transfer) { + fpi_ssm_mark_aborted(ssm, -ENOMEM); + break; + } + + data = g_malloc(AES2550_EP_IN_BUF_SIZE); + libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE, + calibrate_read_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); + } + } + break; + } +} + +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 fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state, + ACTIVATE_NUM_STATES); + ssm->priv = dev; + fpi_ssm_start(ssm, activate_sm_complete); + return 0; +} + +static void dev_deactivate(struct fp_img_dev *dev) +{ + struct aes2550_dev *aesdev = dev->priv; + + aesdev->deactivating = TRUE; +} + +static void complete_deactivation(struct fp_img_dev *dev) +{ + struct aes2550_dev *aesdev = dev->priv; + fp_dbg(""); + + aesdev->deactivating = FALSE; + g_slist_free(aesdev->strips); + aesdev->strips = NULL; + aesdev->strips_len = 0; + fpi_imgdev_deactivate_complete(dev); +} + +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 aes2550_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 = 0x2550 }, /* AES2550 */ + { .vendor = 0x08ff, .product = 0x2810 }, /* AES2810 */ + { 0, 0, 0, }, +}; + +struct fp_img_driver aes2550_driver = { + .driver = { + .id = 4, + .name = FP_COMPONENT, + .full_name = "AuthenTec AES2550/AES2810", + .id_table = id_table, + .scan_type = FP_SCAN_TYPE_SWIPE, + }, + .flags = 0, + .img_height = -1, + .img_width = 192, + + .open = dev_init, + .close = dev_deinit, + .activate = dev_activate, + .deactivate = dev_deactivate, +}; diff --git a/libfprint/drivers/aes2550.h b/libfprint/drivers/aes2550.h new file mode 100644 index 0000000..cd1e22d --- /dev/null +++ b/libfprint/drivers/aes2550.h @@ -0,0 +1,114 @@ +/* + * AuthenTec AES2550/AES2810 driver for libfprint + * Copyright (C) 2012 Vasily Khoruzhick + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AES2550_H +#define __AES2550_H + +/* Registers bits */ + +#define AES2550_REG80_MASTER_RESET (1 << 0) +#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1) +#define AES2550_REG80_LPO_START (1 << 2) +#define AES2550_REG80_HGC_ENABLE (1 << 3) +#define AES2550_REG80_SENSOR_MODE_OFS (4) +#define AES2550_REG80_AUTO_RESTART_FD (1 << 6) +#define AES2550_REG80_EXT_REG_ENABLE (1 << 7) + +#define AES2550_REG81_CONT_SCAN (1 << 0) +#define AES2550_REG81_READ_REG (1 << 1) +#define AES2550_REG81_NSHOT (1 << 2) +#define AES2550_REG81_RUN_FD (1 << 3) +#define AES2550_REG81_READ_ID (1 << 4) +#define AES2550_REG81_RUN_CAL (1 << 5) +#define AES2550_REG81_RUN_TIMER (1 << 6) +#define AES2550_REG81_RUN_BIST (1 << 7) + +#define AES2550_REG83_FINGER_PRESENT (1 << 7) + +#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7) + +#define AES2550_REG8F_EDATA_DISABLE (1 << 1) +#define AES2550_REG8F_AUTH_DISABLE (1 << 2) +#define AES2550_REG8F_EHISTO_DISABLE (1 << 3) +#define AES2550_REG8F_HISTO64 (1 << 4) +#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6) + +#define AES2550_REG95_COL_SCANNED_OFS (0) +#define AES2550_REG95_EPIX_AVG_OFS (4) + +#define AES2550_REGA8_DIG_BIT_DATA_OFS (0) +#define AES2550_REGA8_DIG_BIT_EN (1 << 4) +#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5) +#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6) + +#define AES2550_REGAD_LPFD_AVG_OFS (0) +#define AES2550_REGAD_DETECT_FGROFF (1 << 4) +#define AES2550_REGAD_ADVRANGE_2V (1 << 6) + +#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1) +#define AES2550_REGB1_ANALOG_RESET (1 << 2) +#define AES2550_REGB1_ANALOG_PD (1 << 3) +#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4) +#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5) +#define AES2550_REGB1_RESET_UHSM (1 << 6) +#define AES2550_REGB1_RESET_SENSOR (1 << 7) + +#define AES2550_REGBD_LPO_IN_15_8_OFS (0) +#define AES2550_REGBE_LPO_IN_7_0_OFS (0) + +#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0) +#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0) +#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0) +#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0) +#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2) +#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2) +#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2) +#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4) +#define AES2550_REGBF_QUADRATURE_MODE (1 << 5) + +#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0) +#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1) +#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4) + +#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0) +#define AES2550_REGDC_DEBUG_CTRL2_OFS (3) + +#define AES2550_REGDD_DEBUG_CTRL1_OFS (0) + +/* Commands */ + +enum aes2550_cmds { + AES2550_CMD_SET_IDLE_MODE = 0x00, + AES2550_CMD_RUN_FD = 0x01, + AES2550_CMD_GET_ENROLL_IMG = 0x02, + AES2550_CMD_CALIBRATE = 0x06, + AES2550_CMD_READ_CALIBRATION_DATA = 0x10, + AES2550_CMD_HEARTBEAT = 0x70, +}; + +/* Messages */ + +#define AES2550_STRIP_SIZE (0x31e + 3) +#define AES2550_HEARTBEAT_SIZE (4 + 3) +#define AES2550_EDATA_MAGIC 0xe0 +#define AES2550_HEARTBEAT_MAGIC 0xdb + +#define AES2550_EP_IN_BUF_SIZE 8192 + +#endif diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 44881cd..fdb4002 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -256,6 +256,9 @@ extern struct fp_img_driver aes1610_driver; #ifdef ENABLE_AES2501 extern struct fp_img_driver aes2501_driver; #endif +#ifdef ENABLE_AES2550 +extern struct fp_img_driver aes2550_driver; +#endif #ifdef ENABLE_AES4000 extern struct fp_img_driver aes4000_driver; #endif