diff --git a/aes1610.c b/aes1610.c new file mode 100644 index 0000000..318195f --- /dev/null +++ b/aes1610.c @@ -0,0 +1,552 @@ +/* + * AuthenTec AES1610 driver for libfprint + * Copyright (C) 2007 Anthony Bretaudeau + * 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 "aes1610" + +#include +#include + +#include + +#include +#include + +/* 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 + * 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 128 +#define FRAME_HEIGHT 8 +#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) +{ + 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 } +}; + +static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) +{ + int r; + + r = libusb_claim_interface(dev->udev, 0); + if (r < 0) { + fp_err("could not claim interface 0"); + return r; + } + + /* FIXME check endpoints */ + + return aes_write_regv(dev, init, G_N_ELEMENTS(init)); +} + +static int do_exit(struct fp_img_dev *dev) +{ + return aes_write_regv(dev, stop_reader, G_N_ELEMENTS(stop_reader)); +} + +static void dev_exit(struct fp_img_dev *dev) +{ + do_exit(dev); + libusb_release_interface(dev->udev, 0); +} + +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) +{ + unsigned char buffer[19]; + 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; + + r = read_data(dev, buffer, 19); + if (r < 0) + return r; + + 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; + } + + 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; +} + +/* find overlapping parts of frames */ +static unsigned int find_overlap(unsigned char *first_frame, + unsigned char *second_frame, unsigned int *min_error) +{ + unsigned int dy; + unsigned int not_overlapped_height = 0; + *min_error = 255 * FRAME_SIZE; + for (dy = 0; dy < FRAME_HEIGHT; dy++) { + /* Calculating difference (error) between parts of frames */ + unsigned int i; + unsigned int error = 0; + for (i = 0; i < FRAME_WIDTH * (FRAME_HEIGHT - dy); i++) { + /* Using ? operator to avoid abs function */ + error += first_frame[i] > second_frame[i] ? + (first_frame[i] - second_frame[i]) : + (second_frame[i] - first_frame[i]); + } + + /* Normalize error */ + error *= 15; + error /= i; + if (error < *min_error) { + *min_error = error; + not_overlapped_height = dy; + } + first_frame += FRAME_WIDTH; + } + + return not_overlapped_height; +} + +/* assemble a series of frames into a single image */ +static unsigned int assemble(unsigned char *input, unsigned char *output, + int num_strips, gboolean reverse, unsigned int *errors_sum) +{ + uint8_t *assembled = output; + int frame; + uint32_t image_height = FRAME_HEIGHT; + unsigned int min_error; + *errors_sum = 0; + + if (num_strips < 1) + return 0; + + /* 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); + + if (reverse) + output -= FRAME_SIZE; + else + 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, &min_error); + *errors_sum += min_error; + image_height += not_overlapped; + assembled += FRAME_WIDTH * not_overlapped; + memcpy(assembled, output, FRAME_SIZE); + } + return image_height; +} + +static const struct aes_regwrite capture_reqs[] = { + { 0x80, 0x01 }, + { 0x80, 0x12 }, + { 0x84, 0x01 }, + { 0x85, 0x00 }, + { 0x89, 0x64 }, + { 0x8A, 0x00 }, + { 0x8B, 0x0E }, + { 0x8C, 0x90 }, + { 0xBE, 0x23 }, + { 0x29, 0x06 }, + { 0x2A, 0x35 }, + { 0x96, 0x00 }, + { 0x98, 0x03 }, + { 0x99, 0x00 }, + { 0x9C, 0xA5 }, + { 0x9D, 0x40 }, + { 0x9E, 0xC6 }, + { 0x9F, 0x8E }, + { 0xA2, 0x50 }, + { 0xA3, 0xF0 }, + { 0xAD, 0x08 }, + { 0xBD, 0x4F }, + { 0xAF, 0x54 }, + { 0xB1, 0x08 }, + { 0xB5, 0xAB }, + { 0x1B, 0x2D }, + { 0xB6, 0x4E }, + { 0xB8, 0x70 }, + { 0x2B, 0xB3 }, + { 0x2C, 0x5D }, + { 0x2D, 0x98 }, + { 0x2E, 0xB0 }, + { 0x2F, 0x20 }, + { 0xA2, 0xD0 }, + { 0x1D, 0x21 }, + { 0x1E, 0xBE }, + { 0x1C, 0x00 }, + { 0x1D, 0x30 }, + { 0x1E, 0x29 }, + { 0x1C, 0x01 }, + { 0x1D, 0x00 }, + { 0x1E, 0x9E }, + { 0x1C, 0x02 }, + { 0x1D, 0x30 }, + { 0x1E, 0xBB }, + { 0x1C, 0x03 }, + { 0x1D, 0x00 }, + { 0x1E, 0x9D }, + { 0x1C, 0x04 }, + { 0x1D, 0x22 }, + { 0x1E, 0xFF }, + { 0x1C, 0x05 }, + { 0x1D, 0x1B }, + { 0x1E, 0x4E }, + { 0x1C, 0x06 }, + { 0x1D, 0x16 }, + { 0x1E, 0x28 }, + { 0x1C, 0x07 }, + { 0x1D, 0x22 }, + { 0x1E, 0xFF }, + { 0x1C, 0x08 }, + { 0x1D, 0x15 }, + { 0x1E, 0xF1 }, + { 0x1C, 0x09 }, + { 0x1D, 0x30 }, + { 0x1E, 0xD5 }, + { 0x1C, 0x0A }, + { 0x1D, 0x00 }, + { 0x1E, 0x9E }, + { 0x1C, 0x0B }, + { 0x1D, 0x17 }, + { 0x1E, 0x9D }, + { 0x1C, 0x0C }, + { 0x1D, 0x28 }, + { 0x1E, 0xD7 }, + { 0x1C, 0x0D }, + { 0x1D, 0x17 }, + { 0x1E, 0xD7 }, + { 0x1C, 0x0E }, + { 0x1D, 0x0A }, + { 0x1E, 0xCB }, + { 0x1C, 0x0F }, + { 0x1D, 0x24 }, + { 0x1E, 0x14 }, + { 0x1C, 0x10 }, + { 0x1D, 0x17 }, + { 0x1E, 0x85 }, + { 0x1C, 0x11 }, + { 0x1D, 0x15 }, + { 0x1E, 0x71 }, + { 0x1C, 0x12 }, + { 0x1D, 0x2B }, + { 0x1E, 0x36 }, + { 0x1C, 0x13 }, + { 0x1D, 0x12 }, + { 0x1E, 0x06 }, + { 0x1C, 0x14 }, + { 0x1D, 0x30 }, + { 0x1E, 0x97 }, + { 0x1C, 0x15 }, + { 0x1D, 0x21 }, + { 0x1E, 0x32 }, + { 0x1C, 0x16 }, + { 0x1D, 0x06 }, + { 0x1E, 0xE6 }, + { 0x1C, 0x17 }, + { 0x1D, 0x16 }, + { 0x1E, 0x06 }, + { 0x1C, 0x18 }, + { 0x1D, 0x30 }, + { 0x1E, 0x01 }, + { 0x1C, 0x19 }, + { 0x1D, 0x21 }, + { 0x1E, 0x37 }, + { 0x1C, 0x1A }, + { 0x1D, 0x00 }, + { 0x1E, 0x08 }, + { 0x1C, 0x1B }, + { 0x1D, 0x80 }, + { 0x1E, 0xD5 }, + { 0xA2, 0x50 }, + { 0xA2, 0x50 }, + { 0x81, 0x01 } +}; + +static const struct aes_regwrite strip_scan_reqs[] = { + { 0xBE, 0x23 }, + { 0x29, 0x06 }, + { 0x2A, 0x35 }, + { 0xBD, 0x4F }, + { 0xFF, 0x00 } +}; + +static const struct aes_regwrite capture_stop[] = { + { 0x81,0x00 } +}; + +static int capture(struct fp_img_dev *dev, gboolean unconditional, + struct fp_img **ret) +{ + 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; + + /* FIXME can do better here in terms of buffer management? */ + fp_dbg(""); + + 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) */ + + img = fpi_img_new((3 * MAX_FRAMES * FRAME_SIZE) / 2); + imgptr = img->data; + cooked = imgptr + (MAX_FRAMES * FRAME_SIZE) / 2; + + 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; + + /* 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 (sum < 0) { + r = sum; + goto err; + } + fp_dbg("sum=%d", sum); + if (sum == 0) + count_blank++; + else + count_blank = 0; + + /* if we got 50 blank frames, assume scan has ended. */ + if (count_blank >= 50) + break; + } + + 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?"); + + 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"); + } else { + fp_dbg("reversed scan direction"); + } + + final_size = img->height * FRAME_WIDTH; + memcpy(img->data, cooked, final_size); + img = fpi_img_resize(img, final_size); + *ret = img; + return 0; +err: + fp_img_free(img); + return r; +} + +static const struct usb_id id_table[] = { + { .vendor = 0x08ff, .product = 0x1600 }, + { 0, 0, 0, }, +}; + +struct fp_img_driver aes1610_driver = { + .driver = { + .id = 6, + .name = FP_COMPONENT, + .full_name = "AuthenTec AES1610", + .id_table = id_table, + .scan_type = FP_SCAN_TYPE_SWIPE, + }, + .flags = 0, + .img_height = -1, + .img_width = 128, + + /* temporarily lowered until we sort out image processing code + * binarized scan quality is good, minutiae detection is accurate, + * it's just that we get fewer minutiae than other scanners (less scanning + * area) */ + .bz3_threshold = 10, + + .init = dev_init, + .exit = dev_exit, + .await_finger_on = await_finger_on, + .capture = capture, +}; + diff --git a/libfprint-aes1610-driver.patch b/libfprint-aes1610-driver.patch new file mode 100644 index 0000000..e69de29 diff --git a/libfprint.spec b/libfprint.spec index 4a7644d..ecbfd24 100644 --- a/libfprint.spec +++ b/libfprint.spec @@ -1,6 +1,6 @@ Name: libfprint Version: 0.1.0 -Release: 12.pre2%{?dist} +Release: 13.pre2%{?dist} Summary: Tool kit for fingerprint scanner Group: System Environment/Libraries @@ -12,6 +12,9 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Patch1: 0001-Add-udev-rules-to-set-devices-to-autosuspend.patch # https://bugzilla.redhat.com/show_bug.cgi?id=472103 Patch2: 0001-Add-gdk-pixbuf-support.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=499732 +Source1: aes1610.c +Patch3: libfprint-aes1610-driver.patch ExcludeArch: s390 s390x BuildRequires: libusb1-devel glib2-devel gtk2-devel openssl-devel @@ -37,6 +40,8 @@ developing applications that use %{name}. %setup -q -n %{name}-0.1.0-pre2 %patch1 -p1 %patch2 -p1 +cp -a %{SOURCE1} libfprint/drivers +%patch3 -p1 -b .aes1610 %build autoreconf -f -i @@ -76,6 +81,9 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/pkgconfig/%{name}.pc %changelog +* Mon Nov 30 2009 Bastien Nocera 0.1.0-13.pre2 +- Add aes1610 driver (#499732) + * Thu Oct 01 2009 Bastien Nocera 0.1.0-12.pre2 - Update udev autosuspend rules and disable SGS Thomson reader