diff --git a/libfprint-aes1610-driver.patch b/libfprint-aes1610-driver.patch index e69de29..46b710a 100644 --- a/libfprint-aes1610-driver.patch +++ b/libfprint-aes1610-driver.patch @@ -0,0 +1,1254 @@ +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 +- * Copyright (C) 2007 Daniel Drake ++ * 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 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 + #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 + +-#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, + }; +