diff --git a/TODO b/TODO index 62d210b..bbb4cf6 100644 --- a/TODO +++ b/TODO @@ -11,7 +11,6 @@ NEW DRIVERS =========== Sunplus 895 driver AES1610 driver -AES2501 driver AES3400/3500 driver ID Mouse driver Support for 2nd generation MS devices @@ -19,7 +18,11 @@ Support for 2nd generation UPEK devices IMAGING ======= -aes4000 doesn't work very well, maybe due to small minutia count? +ignore first frame or two with aes2501 +aes2501: increase threshold "sum" for end-of-image detection +aes2501 gain calibration +aes4000 gain calibration +aes4000 resampling PPMM parameter to get_minutiae seems to have no effect nbis minutiae should be stored in endian-independent format diff --git a/libfprint/Makefile.am b/libfprint/Makefile.am index 18c0c52..c5ff97b 100644 --- a/libfprint/Makefile.am +++ b/libfprint/Makefile.am @@ -2,9 +2,10 @@ lib_LTLIBRARIES = libfprint.la UPEKTS_SRC = drivers/upekts.c URU4000_SRC = drivers/uru4000.c +AES2501_SRC = drivers/aes2501.c drivers/aes2501.h AES4000_SRC = drivers/aes4000.c -DRIVER_SRC = $(UPEKTS_SRC) $(URU4000_SRC) $(AES4000_SRC) +DRIVER_SRC = $(UPEKTS_SRC) $(URU4000_SRC) $(AES2501_SRC) $(AES4000_SRC) NBIS_SRC = \ nbis/include/bozorth.h \ diff --git a/libfprint/core.c b/libfprint/core.c index 0aa6dd1..928b285 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -334,6 +334,7 @@ static struct fp_driver * const primitive_drivers[] = { static struct fp_img_driver * const img_drivers[] = { &uru4000_driver, + &aes2501_driver, &aes4000_driver, }; diff --git a/libfprint/drivers/aes2501.c b/libfprint/drivers/aes2501.c new file mode 100644 index 0000000..3334df7 --- /dev/null +++ b/libfprint/drivers/aes2501.c @@ -0,0 +1,614 @@ +/* + * AuthenTec AES2501 driver for libfprint + * Copyright (C) 2007 Daniel Drake + * Copyright (C) 2007 Cyrille Bagard + * Copyright (C) 2007 Vasily Khoruzhick + * + * Based on code from http://home.gna.org/aes2501, relicensed with permission + * + * 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 "aes2501" + +#include +#include + +#include + +#include +#include "aes2501.h" + +/* FIXME these need checking */ +#define EP_IN (1 | USB_ENDPOINT_IN) +#define EP_OUT (2 | USB_ENDPOINT_OUT) + +#define BULK_TIMEOUT 4000 + +/* + * The AES2501 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 16 +#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT) +/* maximum number of frames to read during a scan */ +/* FIXME reduce substantially */ +#define MAX_FRAMES 150 + +struct aes2501_regwrite { + unsigned char reg; + unsigned char value; +}; + +static int write_reg(struct fp_img_dev *dev, unsigned char reg, + unsigned char value) +{ + unsigned char data[] = { reg, value }; + int r; + + fp_dbg("%02x=%02x", reg, value); + r = usb_bulk_write(dev->udev, EP_OUT, data, sizeof(data), BULK_TIMEOUT); + if (r < 0) { + fp_err("bulk write error %d", r); + return r; + } else if (r < sizeof(data)) { + fp_err("unexpected short write %d/%d", r, sizeof(data)); + return -EIO; + } + + return 0; +} + +static int write_regv(struct fp_img_dev *dev, struct aes2501_regwrite *regs, + unsigned int num) +{ + unsigned int i; + int r; + + /* FIXME: could combine multiple writes into a single transaction */ + + for (i = 0; i < num; i++) { + r = write_reg(dev, regs[i].reg, regs[i].value); + if (r < 0) + return r; + } + return 0; +} + +static int read_data(struct fp_img_dev *dev, unsigned char *data, size_t len) +{ + int r; + fp_dbg("len=%zd", len); + + r = usb_bulk_read(dev->udev, EP_IN, data, len, BULK_TIMEOUT); + if (r < 0) { + fp_err("bulk read error %d", r); + return r; + } else if (r < len) { + fp_err("unexpected short read %d/%zd", r, len); + return -EIO; + } + return 0; +} + +static int read_regs(struct fp_img_dev *dev, unsigned char *data) +{ + int r; + fp_dbg(""); + + r = write_reg(dev, AES2501_REG_CTRL2, AES2501_CTRL2_READ_REGS); + if (r < 0) + return r; + + return read_data(dev, data, 126); +} + +static const struct aes2501_regwrite init_1[] = { + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { 0xb0, 0x27 }, /* Reserved? */ + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { 0xff, 0x00 }, /* Reserved? */ + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_DETCTRL, + AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS }, + { AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US }, + { AES2501_REG_MEASDRV, + AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE }, + { AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M }, + { AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE }, + { AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE }, + { AES2501_REG_CHANGAIN, + AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X }, + { AES2501_REG_ADREFHI, 0x44 }, + { AES2501_REG_ADREFLO, 0x34 }, + { AES2501_REG_STRTCOL, 0x16 }, + { AES2501_REG_ENDCOL, 0x16 }, + { AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 }, + { AES2501_REG_TREG1, 0x70 }, + { 0xa2, 0x02 }, + { 0xa7, 0x00 }, + { AES2501_REG_TREGC, AES2501_TREGC_ENABLE }, + { AES2501_REG_TREGD, 0x1a }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, + { AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT }, + { AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE }, +}; + +static const struct aes2501_regwrite init_2[] = { + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_AUTOCALOFFSET, 0x41 }, + { AES2501_REG_EXCITCTRL, 0x42 }, + { AES2501_REG_DETCTRL, 0x53 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, +}; + +static const struct aes2501_regwrite init_3[] = { + { 0xff, 0x00 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_AUTOCALOFFSET, 0x41 }, + { AES2501_REG_EXCITCTRL, 0x42 }, + { AES2501_REG_DETCTRL, 0x53 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, +}; + +static const struct aes2501_regwrite init_4[] = { + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { 0xb0, 0x27 }, + { AES2501_REG_ENDROW, 0x0a }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, + { AES2501_REG_DETCTRL, 0x45 }, + { AES2501_REG_AUTOCALOFFSET, 0x41 }, +}; + +static const struct aes2501_regwrite init_5[] = { + { 0xb0, 0x27 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { 0xff, 0x00 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET }, + { AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET }, +}; + +static int do_init(struct fp_img_dev *dev) +{ + unsigned char buffer[128]; + int r; + int i; + + /* part 1, probably not needed */ + r = write_regv(dev, init_1, ARRAY_SIZE(init_1)); + if (r < 0) + return r; + + r = read_data(dev, buffer, 20); + if (r < 0) + return r; + + /* part 2 */ + r = write_regv(dev, init_2, ARRAY_SIZE(init_2)); + if (r < 0) + return r; + + r = read_regs(dev, buffer); + if (r < 0) + return r; + + /* part 3 */ + fp_dbg("reg 0xaf = %x", buffer[0x5f]); + i = 0; + while (buffer[0x5f] == 0x6b) { + r = write_regv(dev, init_3, ARRAY_SIZE(init_3)); + if (r < 0) + return r; + r = read_regs(dev, buffer); + if (r < 0) + return r; + if (++i == 13) + break; + } + + /* part 4 */ + r = write_regv(dev, init_4, ARRAY_SIZE(init_4)); + if (r < 0) + return r; + + /* part 5 */ + return write_regv(dev, init_5, ARRAY_SIZE(init_5)); +} + +static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) +{ + int r; + + r = usb_claim_interface(dev->udev, 0); + if (r < 0) { + fp_err("could not claim interface 0"); + return r; + } + + /* FIXME check endpoints */ + + return do_init(dev); +} + +static void dev_exit(struct fp_img_dev *dev) +{ + usb_release_interface(dev->udev, 0); +} + +static const struct aes2501_regwrite finger_det_reqs[] = { + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_DETCTRL, + AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS }, + { AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US }, + { AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE }, + { AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M }, + { AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE }, + { AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE }, + { AES2501_REG_CHANGAIN, + AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X }, + { AES2501_REG_ADREFHI, 0x44 }, + { AES2501_REG_ADREFLO, 0x34 }, + { AES2501_REG_STRTCOL, 0x16 }, + { AES2501_REG_ENDCOL, 0x16 }, + { AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 }, + { AES2501_REG_TREG1, 0x70 }, + { 0xa2, 0x02 }, + { 0xa7, 0x00 }, + { AES2501_REG_TREGC, AES2501_TREGC_ENABLE }, + { AES2501_REG_TREGD, 0x1a }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, + { AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT }, + { AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE }, +}; + +static int detect_finger(struct fp_img_dev *dev) +{ + unsigned char buffer[22]; + int r; + int i; + int sum = 0; + + r = write_regv(dev, finger_det_reqs, ARRAY_SIZE(finger_det_reqs)); + if (r < 0) + return r; + + r = read_data(dev, buffer, 20); + if (r < 0) + return r; + + for (i = 1; i < 9; i++) + sum += (buffer[i] & 0xf) + (buffer[i] >> 4); + + return sum > 20; +} + +static int await_finger_on(struct fp_img_dev *dev) +{ + int r; + do { + r = detect_finger(dev); + } while (r == 0); + return (r < 0) ? r : 0; +} + +/* Read the value of a specific register from a register dump */ +static int regval_from_dump(unsigned char *data, uint8_t target) +{ + if (*data != FIRST_AES2501_REG) { + fp_err("not a register dump"); + return -EILSEQ; + } + + if (!(FIRST_AES2501_REG <= target && target <= LAST_AES2501_REG)) { + fp_err("out of range"); + return -EINVAL; + } + + target -= FIRST_AES2501_REG; + target *= 2; + return data[target + 1]; +} + +static int sum_histogram_values(unsigned char *data, uint8_t threshold) +{ + int r = 0; + int i; + uint16_t *histogram = (uint16_t *)(data + 1); + + if (*data != 0xde) + return -EILSEQ; + + if (threshold > 0x0f) + return -EINVAL; + + /* FIXME endianness */ + for (i = threshold; i < 16; i++) + r += histogram[i]; + + return r; +} + +/* find overlapping parts of frames */ +static unsigned int find_overlap(unsigned char *first_frame, + unsigned char *second_frame) +{ + unsigned int dy; + unsigned int min_error = 255 * FRAME_SIZE; + unsigned int not_overlapped_height = 0; + 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(unsigned char *input, unsigned char *output, + int num_strips) +{ + uint8_t *assembled = output; + int frame; + uint32_t image_height = FRAME_HEIGHT; + + if (num_strips < 1) + return 0; + + /* Rotating given data by 90 degrees + * Taken from document describing aes2501 image format + * TODO: move reversing detection here */ + + for (frame = 0; frame < num_strips; frame++) { + int column; + for (column = 0; column < FRAME_WIDTH; column++) { + int row; + for (row = 0; row < (FRAME_HEIGHT / 2); row++) { + output[FRAME_WIDTH * ( 2 * row) + column] = *input & 0x0F; + output[FRAME_WIDTH * ( 2 * row + 1) + column] = *input >> 4; + input++; + } + } + + output += FRAME_SIZE; + } + + /* 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); + image_height += not_overlapped; + assembled += FRAME_WIDTH * not_overlapped; + memcpy(assembled, output, FRAME_SIZE); + } + return image_height; +} + +static const struct aes2501_regwrite capture_reqs_1[] = { + { AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET }, + { AES2501_REG_EXCITCTRL, 0x40 }, + { AES2501_REG_DETCTRL, + AES2501_DETCTRL_SDELAY_31_MS | AES2501_DETCTRL_DRATE_CONTINUOUS }, + { AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US }, + { AES2501_REG_DEMODPHASE2, 0x7c }, + { AES2501_REG_MEASDRV, + AES2501_MEASDRV_MEASURE_SQUARE | AES2501_MEASDRV_MDRIVE_0_325 }, + { AES2501_REG_DEMODPHASE1, 0x24 }, + { AES2501_REG_CHWORD1, 0x00 }, + { AES2501_REG_CHWORD2, 0x6c }, + { AES2501_REG_CHWORD3, 0x09 }, + { AES2501_REG_CHWORD4, 0x54 }, + { AES2501_REG_CHWORD5, 0x78 }, + { 0xa2, 0x02 }, + { 0xa7, 0x00 }, + { 0xb6, 0x26 }, + { 0xb7, 0x1a }, + { AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE }, + { AES2501_REG_IMAGCTRL, + AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | + AES2501_IMAGCTRL_IMG_DATA_DISABLE }, + { AES2501_REG_STRTCOL, 0x10 }, + { AES2501_REG_ENDCOL, 0x1f }, + { AES2501_REG_CHANGAIN, + AES2501_CHANGAIN_STAGE1_2X | AES2501_CHANGAIN_STAGE2_2X }, + { AES2501_REG_ADREFHI, 0x70 }, + { AES2501_REG_ADREFLO, 0x20 }, + { AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT }, + { AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE }, +}; + +static const struct aes2501_regwrite capture_reqs_2[] = { + { AES2501_REG_IMAGCTRL, + AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE | + AES2501_IMAGCTRL_IMG_DATA_DISABLE }, + { AES2501_REG_STRTCOL, 0x10 }, + { AES2501_REG_ENDCOL, 0x1f }, + { AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X }, + { AES2501_REG_ADREFHI, 0x70 }, + { AES2501_REG_ADREFLO, 0x20 }, + { AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT }, +}; + +static const struct aes2501_regwrite strip_scan_reqs[] = { + { AES2501_REG_IMAGCTRL, + AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE }, + { AES2501_REG_STRTCOL, 0x00 }, + { AES2501_REG_ENDCOL, 0x2f }, + { AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X }, + { AES2501_REG_ADREFHI, 0x5b }, + { AES2501_REG_ADREFLO, 0x20 }, + { AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT }, +}; + +static int capture(struct fp_img_dev *dev, gboolean unconditional, + struct fp_img **ret) +{ + int r; + struct fp_img *img; + unsigned int nstrips; + unsigned char *cooked; + unsigned char *imgptr; + unsigned char buf[1705]; + int sum; + int i; + + /* FIXME can do better here in terms of buffer management? */ + fp_dbg(""); + + r = write_regv(dev, capture_reqs_1, ARRAY_SIZE(capture_reqs_1)); + if (r < 0) + return r; + + r = read_data(dev, buf, 159); + if (r < 0) + return r; + + r = write_regv(dev, capture_reqs_2, ARRAY_SIZE(capture_reqs_2)); + if (r < 0) + return r; + + r = read_data(dev, buf, 159); + if (r < 0) + return r; + + /* FIXME: use histogram data above for gain calibration (0x8e xx) */ + + img = fpi_img_new((3 * MAX_FRAMES * FRAME_SIZE) / 2); + imgptr = img->data; + cooked = imgptr + (MAX_FRAMES * FRAME_SIZE) / 2; + + for (nstrips = 0; nstrips < MAX_FRAMES; nstrips++) { + int threshold; + + r = write_regv(dev, strip_scan_reqs, ARRAY_SIZE(strip_scan_reqs)); + if (r < 0) + goto err; + r = read_data(dev, buf, 1705); + if (r < 0) + goto err; + memcpy(imgptr, buf + 1, 192*8); + imgptr += 192*8; + + threshold = regval_from_dump((buf + 1 + 192*8 + 1 + 16*2 + 1 + 8), + AES2501_REG_DATFMT); + if (threshold < 0) { + r = threshold; + goto err; + } + + sum = sum_histogram_values((buf + 1 + 192*8), threshold & 0x0f); + if (sum < 0) { + r = sum; + goto err; + } + fp_dbg("sum=%d", sum); + if (sum == 0) + break; + } + if (nstrips == MAX_FRAMES) + fp_warn("swiping finger too slow?"); + + img->height = assemble(img->data, cooked, nstrips); + for (i = 0; i < img->height * FRAME_WIDTH; i++) + img->data[i] = (cooked[i] << 4) | 0xf; + + img = fpi_img_resize(img, img->height * FRAME_WIDTH); + img->flags = FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED | FP_IMG_COLORS_INVERTED; + *ret = img; + return 0; +err: + g_free(img); + return r; +} + +static const struct usb_id id_table[] = { + { .vendor = 0x08ff, .product = 0x2580 }, + { 0, 0, 0, }, +}; + +struct fp_img_driver aes2501_driver = { + .driver = { + .id = 4, + .name = FP_COMPONENT, + .full_name = "AuthenTec AES2501", + .id_table = id_table, + }, + .flags = 0, + .img_height = -1, + .img_width = 192, + + /* temporarily lowered until image quality improves */ + .bz3_threshold = 20, + + .init = dev_init, + .exit = dev_exit, + .await_finger_on = await_finger_on, + .capture = capture, +}; + diff --git a/libfprint/drivers/aes2501.h b/libfprint/drivers/aes2501.h new file mode 100644 index 0000000..a15699f --- /dev/null +++ b/libfprint/drivers/aes2501.h @@ -0,0 +1,170 @@ +/* + * AuthenTec AES2501 driver for libfprint + * Copyright (C) 2007 Cyrille Bagard + * + * Based on code from http://home.gna.org/aes2501, relicensed with permission + * + * 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 __AES2501_H +#define __AES2501_H + +enum aes2501_regs { + AES2501_REG_CTRL1 = 0x80, + AES2501_REG_CTRL2 = 0x81, + AES2501_REG_EXCITCTRL = 0x82, /* excitation control */ + AES2501_REG_DETCTRL = 0x83, /* detect control */ + AES2501_REG_COLSCAN = 0x88, /* column scan rate register */ + AES2501_REG_MEASDRV = 0x89, /* measure drive */ + AES2501_REG_MEASFREQ = 0x8a, /* measure frequency */ + AES2501_REG_DEMODPHASE1 = 0x8d, + AES2501_REG_DEMODPHASE2 = 0x8c, + AES2501_REG_CHANGAIN = 0x8e, /* channel gain */ + AES2501_REG_ADREFHI = 0x91, /* A/D reference high */ + AES2501_REG_ADREFLO = 0x92, /* A/D reference low */ + AES2501_REG_STRTROW = 0x93, /* start row */ + AES2501_REG_ENDROW = 0x94, /* end row */ + AES2501_REG_STRTCOL = 0x95, /* start column */ + AES2501_REG_ENDCOL = 0x96, /* end column */ + AES2501_REG_DATFMT = 0x97, /* data format */ + AES2501_REG_IMAGCTRL = 0x98, /* image data */ + AES2501_REG_STAT = 0x9a, + AES2501_REG_CHWORD1 = 0x9b, /* challenge word 1 */ + AES2501_REG_CHWORD2 = 0x9c, + AES2501_REG_CHWORD3 = 0x9d, + AES2501_REG_CHWORD4 = 0x9e, + AES2501_REG_CHWORD5 = 0x9f, + AES2501_REG_TREG1 = 0xa1, /* test register 1 */ + AES2501_REG_AUTOCALOFFSET = 0xa8, + AES2501_REG_TREGC = 0xac, + AES2501_REG_TREGD = 0xad, + AES2501_REG_LPONT = 0xb4, /* low power oscillator on time */ +}; + +#define FIRST_AES2501_REG AES2501_REG_CTRL1 +#define LAST_AES2501_REG AES2501_REG_CHWORD5 + +#define AES2501_CTRL1_MASTER_RESET (1<<0) +#define AES2501_CTRL1_SCAN_RESET (1<<1) /* stop + restart scan sequencer */ +/* 1 = continuously updated, 0 = updated prior to starting a scan */ +#define AES2501_CTRL1_REG_UPDATE (1<<2) + +/* 1 = continuous scans, 0 = single scans */ +#define AES2501_CTRL2_CONTINUOUS 0x01 +#define AES2501_CTRL2_READ_REGS 0x02 /* dump registers */ +#define AES2501_CTRL2_SET_ONE_SHOT 0x04 +#define AES2501_CTRL2_CLR_ONE_SHOT 0x08 +#define AES2501_CTRL2_READ_ID 0x10 + +enum aes2501_detection_rate { + /* rate of detection cycles: */ + AES2501_DETCTRL_DRATE_CONTINUOUS = 0x00, /* continuously */ + AES2501_DETCTRL_DRATE_16_MS = 0x01, /* every 16.62ms */ + AES2501_DETCTRL_DRATE_31_MS = 0x02, /* every 31.24ms */ + AES2501_DETCTRL_DRATE_62_MS = 0x03, /* every 62.50ms */ + AES2501_DETCTRL_DRATE_125_MS = 0x04, /* every 125.0ms */ + AES2501_DETCTRL_DRATE_250_MS = 0x05, /* every 250.0ms */ + AES2501_DETCTRL_DRATE_500_MS = 0x06, /* every 500.0ms */ + AES2501_DETCTRL_DRATE_1_S = 0x07, /* every 1s */ +}; + +enum aes2501_settling_delay { + AES2501_DETCTRL_SDELAY_31_MS = 0x00, /* 31.25ms */ + AES2501_DETCTRL_SSDELAY_62_MS = 0x10, /* 62.5ms */ + AES2501_DETCTRL_SSDELAY_125_MS = 0x20, /* 125ms */ + AES2501_DETCTRL_SSDELAY_250_MS = 0x30 /* 250ms */ +}; + +enum aes2501_col_scan_rate { + AES2501_COLSCAN_SRATE_32_US = 0x00, /* 32us */ + AES2501_COLSCAN_SRATE_64_US = 0x01, /* 64us */ + AES2501_COLSCAN_SRATE_128_US = 0x02, /* 128us */ + AES2501_COLSCAN_SRATE_256_US = 0x03, /* 256us */ + AES2501_COLSCAN_SRATE_512_US = 0x04, /* 512us */ + AES2501_COLSCAN_SRATE_1024_US = 0x05, /* 1024us */ + AES2501_COLSCAN_SRATE_2048_US = 0x06, /* 2048us */ + +}; + +enum aes2501_mesure_drive { + AES2501_MEASDRV_MDRIVE_0_325 = 0x00, /* 0.325 Vpp */ + AES2501_MEASDRV_MDRIVE_0_65 = 0x01, /* 0.65 Vpp */ + AES2501_MEASDRV_MDRIVE_1_3 = 0x02, /* 1.3 Vpp */ + AES2501_MEASDRV_MDRIVE_2_6 = 0x03 /* 2.6 Vpp */ + +}; + +/* Select (1=square | 0=sine) wave drive during measure */ +#define AES2501_MEASDRV_SQUARE 0x20 +/* 0 = use mesure drive setting, 1 = when sine wave is selected */ +#define AES2501_MEASDRV_MEASURE_SQUARE 0x10 + +enum aes2501_measure_freq { + AES2501_MEASFREQ_125K = 0x01, /* 125 kHz */ + AES2501_MEASFREQ_250K = 0x02, /* 250 kHz */ + AES2501_MEASFREQ_500K = 0x03, /* 500 kHz */ + AES2501_MEASFREQ_1M = 0x04, /* 1 MHz */ + AES2501_MEASFREQ_2M = 0x05 /* 2 MHz */ +}; + +#define DEMODPHASE_NONE 0x00 +#define DEMODPHASE_180_00 0x40 /* 180 degrees */ +#define DEMODPHASE_2_81 0x01 /* 2.8125 degrees */ + +#define AES2501_REG_DEMODPHASE1 0x8d +#define DEMODPHASE_1_40 0x40 /* 1.40625 degrees */ +#define DEMODPHASE_0_02 0x01 /* 0.02197256 degrees */ + +enum aes2501_sensor_gain1 { + AES2501_CHANGAIN_STAGE1_2X = 0x00, /* 2x */ + AES2501_CHANGAIN_STAGE1_4X = 0x01, /* 4x */ + AES2501_CHANGAIN_STAGE1_8X = 0x02, /* 8x */ + AES2501_CHANGAIN_STAGE1_16X = 0x03 /* 16x */ +}; + +enum aes2501_sensor_gain2 { + AES2501_CHANGAIN_STAGE2_2X = 0x00, /* 2x */ + AES2501_CHANGAIN_STAGE2_4X = 0x10, /* 4x */ + AES2501_CHANGAIN_STAGE2_8X = 0x20, /* 8x */ + AES2501_CHANGAIN_STAGE2_16X = 0x30 /* 16x */ +}; + +#define AES2501_DATFMT_EIGHT 0x40 /* 1 = 8-bit data, 0 = 4-bit data */ +#define AES2501_DATFMT_LOW_RES 0x20 +#define AES2501_DATFMT_BIN_IMG 0x10 + +/* don't send image or authentication messages when imaging */ +#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01 +/* send histogram when imaging */ +#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02 +/* send histogram at end of each row rather than each scan */ +#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04 +/* send full image array rather than 64x64 center */ +#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08 +/* return registers before data (rather than after) */ +#define AES2501_IMAGCTRL_REG_FIRST 0x10 +/* return test registers with register dump */ +#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20 + +#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */ + +/* Enable the reading of the register in TREGD */ +#define AES2501_TREGC_ENABLE 0x01 + +#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */ +#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */ + +#endif /* __AES2501_H */ diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 00add02..969c637 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -141,6 +141,7 @@ struct fp_img_driver { extern struct fp_driver upekts_driver; extern struct fp_img_driver uru4000_driver; +extern struct fp_img_driver aes2501_driver; extern struct fp_img_driver aes4000_driver; void fpi_img_driver_setup(struct fp_img_driver *idriver);