libfprint/libfprint-aes1610-driver.patch
2009-12-01 14:33:43 +00:00

1255 lines
33 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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){
+ 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 },
@@ -271,8 +523,8 @@ static const struct aes_regwrite capture_reqs[] = {
{ 0x8B, 0x0E },
{ 0x8C, 0x90 },
{ 0xBE, 0x23 },
- { 0x29, 0x06 },
- { 0x2A, 0x35 },
+ { 0x29, 0x04 },
+ { 0x2A, 0xFF },
{ 0x96, 0x00 },
{ 0x98, 0x03 },
{ 0x99, 0x00 },
@@ -387,10 +639,10 @@ static const struct aes_regwrite capture_reqs[] = {
{ 0x81, 0x01 }
};
-static const struct aes_regwrite strip_scan_reqs[] = {
+static struct aes_regwrite strip_scan_reqs[] = {
{ 0xBE, 0x23 },
- { 0x29, 0x06 },
- { 0x2A, 0x35 },
+ { 0x29, 0x04 },
+ { 0x2A, 0xFF },
{ 0xBD, 0x4F },
{ 0xFF, 0x00 }
};
@@ -399,130 +651,456 @@ static const struct aes_regwrite capture_stop[] = {
{ 0x81,0x00 }
};
-static int capture(struct fp_img_dev *dev, gboolean unconditional,
- struct fp_img **ret)
+/*
+ * 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)
{
- int r;
- struct fp_img *img;
- unsigned int nstrips;
- unsigned int errors_sum, r_errors_sum;
- unsigned char *cooked;
- unsigned char *imgptr;
- unsigned char buf[665];
- int final_size;
- int sum;
- unsigned int count_blank = 0;
- int i;
+ // 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;
- /* FIXME can do better here in terms of buffer management? */
- fp_dbg("");
+ // 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;
+ }
- r = aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs));
- if (r < 0)
- return r;
-
- /* FIXME: use histogram data above for gain calibration (0x8e xx) */
+ // 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;
- img = fpi_img_new((3 * MAX_FRAMES * FRAME_SIZE) / 2);
- imgptr = img->data;
- cooked = imgptr + (MAX_FRAMES * FRAME_SIZE) / 2;
+ 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);
+ }
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
+ // 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++;
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
-
- /* we start at 2 because we captured 2 frames above. the above captures
- * should possibly be moved into the loop below, or discarded altogether */
- for (nstrips = 2; nstrips < MAX_FRAMES - 2; nstrips++) {
- r = aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs));
- if (r < 0)
- goto err;
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
-
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
-
- sum = 0;
- for (i = 515; 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 += buf[i];
+ 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;
}
- if (sum < 0) {
- r = sum;
- goto err;
+ 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;
}
- fp_dbg("sum=%d", sum);
- if (sum == 0)
- count_blank++;
+ 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
- count_blank = 0;
-
- /* if we got 50 blank frames, assume scan has ended. */
- if (count_blank >= 50)
+ 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;
}
-
- r = aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop));
- if (r < 0)
- goto err;
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
- nstrips++;
- r = read_data(dev, buf, 665);
- if (r < 0)
- goto err;
- memcpy(imgptr, buf + 1, 128*4);
- imgptr += 128*4;
- nstrips++;
-
- if (nstrips == MAX_FRAMES)
- fp_warn("swiping finger too slow?");
+ ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
+ fp_dbg("");
+ ssm->priv = dev;
+ fpi_ssm_start(ssm, capture_sm_complete);
+}
- img->flags = FP_IMG_COLORS_INVERTED;
- img->height = assemble(img->data, cooked, nstrips, FALSE, &errors_sum);
- img->height = assemble(img->data, cooked, nstrips, TRUE, &r_errors_sum);
-
- if (r_errors_sum > errors_sum) {
- img->height = assemble(img->data, cooked, nstrips, FALSE, &errors_sum);
- img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
- fp_dbg("normal scan direction");
+/****** 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 {
- fp_dbg("reversed scan direction");
+ fpi_ssm_next_state(ssm);
}
+}
+*/
- final_size = img->height * FRAME_WIDTH;
- memcpy(img->data, cooked, final_size);
- img = fpi_img_resize(img, final_size);
- *ret = img;
+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;
-err:
- fp_img_free(img);
- return r;
+}
+
+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 },
+ { .vendor = 0x08ff, .product = 0x1600 }, /* AES1600 */
{ 0, 0, 0, },
};
@@ -544,9 +1122,9 @@ struct fp_img_driver aes1610_driver = {
* area) */
.bz3_threshold = 10,
- .init = dev_init,
- .exit = dev_exit,
- .await_finger_on = await_finger_on,
- .capture = capture,
+ .open = dev_init,
+ .close = dev_deinit,
+ .activate = dev_activate,
+ .deactivate = dev_deactivate,
};