Compare commits

...

105 Commits

Author SHA1 Message Date
Bastien Nocera
ee32166267 0.6.0 2015-02-03 17:36:32 +01:00
Vasily Khoruzhick
a3c90f2b24 upektc_img: handle 0x28 message better 2015-01-31 13:19:24 +03:00
Arseniy Lartsev
dc537ef2c9 lib: Add VFS5011 driver
New driver for VFS5011 138a:0011 and 138a:0018

https://bugs.freedesktop.org/show_bug.cgi?id=61692

[vasilykh]:
	- use g_get_real_time() instead of non-portable time()
	- use g_free() instead of free()
	- comment out "RECV(VFS5011_IN_ENDPOINT_CTRL2, 8)"
2015-01-31 13:19:11 +03:00
Vasily Khoruzhick
061a457658 aeslib: improve frame-assembling routines
Some scanners provide hardware assistance in frame assemling, i.e.
horizontal and vertical offset to previous frame is provided. This
commit improves code to utilise that assistance. Sensors without
hardware assistance will use software algorithm, which was also
improved to do search in horizontal direction.
2015-01-31 13:18:23 +03:00
Vasily Khoruzhick
82ae7c1c09 aesx660: fix spelling of define name 2015-01-31 13:18:23 +03:00
Rex Dieter
79d79c3c87 lib: Test before applying power saving udev rules
add TEST=="power/control"... to test for the presence of the file
before setting it.

See https://bugzilla.redhat.com/show_bug.cgi?id=950205

https://bugs.freedesktop.org/show_bug.cgi?id=87414
2015-01-28 16:28:57 +01:00
Vasily Khoruzhick
35e356f625 build: add subdir-objects option to make automake happy
Without subdir-object automake throws warnings like this one:

Makefile.am warning: source file 'drivers/aes2550.c' is in a subdirectory,
Makefile.am but option 'subdir-objects' is disabled
2013-08-19 11:13:50 +03:00
Vasily Khoruzhick
948ab02d1a lib: use pixman for imaging utils
pixman is very lightweight library for pixel manipulation, and it
has no dependencies except glibc, so using it instead of gdkpixbuf/imagemagick
makes list for libfprint dependencies a bit shorter.
2013-08-19 11:13:50 +03:00
Patrick Marlier
a6101026d2 Add EgisTec ES603 driver
This driver handles EgisTec ES603 device, ID 1c7a:0603
2013-08-19 11:13:50 +03:00
Vasily Khoruzhick
e0966cb20f vfs101: drop nr_enroll_stages override
Imaging drivers aren't allowed to change number of enroll stages anymore,
all that imaging driver does is providing image to the upper layer.
Upper layer decides if it needs more scans.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e278e8321c vfs301: drop nr_enroll_stages override
Imaging drivers aren't allowed to change number of enroll stages anymore,
all that imaging driver does is providing image to the upper layer.
Upper layer decides if it needs more scans.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e1728e7c25 aes1660: remove unneeded lowering of bz3_threshold
Since 5 scans for enroll was introduced it's not necessary to lower
bz3_threshold anymore, there's a good probability that scan to verify matches
with at least one enrolled sample.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
e215b05094 imgdev: perform 5 scans for enrollment
This feature dramatically improves matching rate on devices with small
sensors.
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
fe3fdd1f50 upeke2: disable by default, device is now handled by upektc_img driver 2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
5ff45658c0 upektc_img: add support for Upek Eikon 2 devices 2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
d12b294783 Add upektc_img driver
Imaging driver, handles UPEK 147e:2020 device
2013-08-19 11:13:49 +03:00
Vasily Khoruzhick
2bba4fb073 Implement image capture (both synchronous and asynchronous variants)
fp_dev_img_capture() is not implemented and returns -ENOTSUPP for all devices
since migration to asynchronous model. This commit implement missing functionality
2013-08-12 11:18:18 +03:00
Juvenn Woo
aeca32fc12 aes3k: add aes3500 driver
This driver supports AES3500 device (08ff:5731) and utilizes common routines from
AES4000.
2013-08-12 11:18:18 +03:00
Juvenn Woo
12c1088777 aes3k: extract common routines aes3k from aes4000
AES3500 and AES4000 are pretty similar devices, have same
command send, the only difference is in image size and init sequence.
Extract common routines from AES4K to be used later in AES3500 driver

https://bugs.freedesktop.org/show_bug.cgi?id=64351
2013-08-12 11:18:18 +03:00
Bastien Nocera
9e2f8b5e75 0.5.1 2013-08-11 17:53:21 +02:00
Bastien Nocera
c14ebd8b63 Add MS keyboard to the suspend blacklist
045e:00bb isn't a stand-alone fingerprint reader, but a keyboard
with a reader integrated.

https://bugs.freedesktop.org/show_bug.cgi?id=66659
2013-07-25 12:29:53 +02:00
Bastien Nocera
a6339a30ef Fix udev rules printing
The blacklisted devices weren't correctly checked for past the first
item, as we weren't using the right index to get the product ID
from the ID table.
2013-07-25 12:29:29 +02:00
Bastien Nocera
f3dd55815e lib: Use g_malloc0 instead of g_malloc+memset 2013-06-26 14:21:33 +02:00
Patrick Marlier
6d65bfcf80 lib: g_malloc never fails
Or rather, it never returns errors and aborts instead if
memory cannot be allocated, so remove code that handled
failures.

https://bugs.freedesktop.org/show_bug.cgi?id=57869
2013-06-26 14:18:40 +02:00
Jeremy Bicha
1acd647b29 build: Fix underlinking against glib
https://bugs.freedesktop.org/show_bug.cgi?id=63755
2013-06-26 14:14:49 +02:00
Vasily Khoruzhick
0e843ad6b3 build: fix autoreconf warnings
Replace AC_LANG_PROGRAM with AC_LANG_SOURCE and INCLUDES with AM_CFLAGS to fix
autoreconf warnings

https://bugs.freedesktop.org/show_bug.cgi?id=62748
2013-06-26 14:13:21 +02:00
Vasily Khoruzhick
7eafca7bab build: glib-2.28 or newer is a requirement
We're using g_slist_free_full which was introduced in glib-2.28

https://bugs.freedesktop.org/show_bug.cgi?id=59790
2013-01-30 17:52:54 +01:00
Vasily Khoruzhick
3b3679c900 upeke2: Add support for 147e:2020 ID
https://bugs.freedesktop.org/show_bug.cgi?id=59320
2013-01-13 19:02:52 +01:00
Vasily Khoruzhick
43eca622cd configure.ac: Use AC_CONFIG_HEADERS
AM_CONFIG_HEADER is obsolete, use AC_CONFIG_HEADERS instead of it.

https://bugs.freedesktop.org/show_bug.cgi?id=59320
2013-01-13 19:02:48 +01:00
Bastien Nocera
02509e1073 build: udev rules must be created when building from git
https://bugs.freedesktop.org/show_bug.cgi?id=59076
2013-01-07 12:36:45 +01:00
Timo Teräs
0b2d33c712 imgdev: fix cancelling of enrollment from stage_completed callback
Re-check device state after fpi_drvcb_enroll_stage_completed().
If enrollment was cancelled after non-completing stage, we must
not restart acquire as it would confuse the internal state machine.

https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:17:30 +01:00
Timo Teräs
7751fcb375 AUTHORS: Update
https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:17:24 +01:00
Timo Teräs
8a87ba448c uru4000: fix cancelling of imaging from error callback
Call error callback before resetting img_transfer to NULL. This
variable is internally used to detect if we are still in imaging
loop and the call to execute_state_change() needs to be postponed.
Since this is the final thing imaging_complete() we can't reset
img_transfer until just before this call.

https://bugs.freedesktop.org/show_bug.cgi?id=57829
2012-12-14 13:16:31 +01:00
Timo Teräs
7e1646c382 uru4000: fix race condition on waiting power up irq
It can come before we finish reading the status register on some
cases. Arm the irq handler early, and fix the state machine to
handle early irq properly.

https://bugs.freedesktop.org/show_bug.cgi?id=57834
2012-12-04 11:55:10 +01:00
Bastien Nocera
a5ec0b30e1 0.5.0 2012-12-03 16:28:00 +01:00
Bastien Nocera
29cf86a02e build: Fix configure-time warning
Due to missing quotes
2012-12-03 16:28:00 +01:00
Bastien Nocera
7892c943e6 build: Create .tar.xz distribution by default 2012-12-03 16:28:00 +01:00
Vasily Khoruzhick
a7d6b7c30a nbis: Fix crash in gen_initial_maps()
If the image is too small some coordinates can become negative.
Handle this to avoid memory corruption.

https://bugs.freedesktop.org/show_bug.cgi?id=57730
2012-12-03 15:36:25 +01:00
Bastien Nocera
258ac2d4da build: Build all the drivers by default 2012-12-01 15:10:13 +01:00
Vasily Khoruzhick
31cf7a9383 build: Add common routines to the dist
Fixes dist when aesX660 drivers are disabled from the build.

https://bugs.freedesktop.org/show_bug.cgi?id=57724
2012-12-01 15:04:47 +01:00
Vasily Khoruzhick
f335256cbe build: Don't build unneeded aesX660 routines
When neither of the AES drivers are built, no need to build the common
routines.

https://bugs.freedesktop.org/show_bug.cgi?id=57724
2012-12-01 15:02:59 +01:00
Vasily Khoruzhick
c02cb3083d AES-drivers: drop redudant type cast in g_slist_free_full call
https://bugs.freedesktop.org/show_bug.cgi?id=57688
2012-11-29 15:32:14 +01:00
Vasily Khoruzhick
2084724115 upektc: add support for Eikon Touch 300
https://bugs.freedesktop.org/show_bug.cgi?id=45197
2012-11-29 12:35:05 +01:00
Vasily Khoruzhick
59925d2027 Update AUTHORS file
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
118c610e29 lib: add AES2660 driver
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
313bfede77 lib: add AES1660 driver
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
c1da647aed lib: add AES1660/AES2660 common routines
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:52 +01:00
Vasily Khoruzhick
22d204cc68 lib: Split fpi_im_resize factor into width/height
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:27 +01:00
Vasily Khoruzhick
bc497f1b26 aeslib: prevent integer overflow
AuthenTec devices send 4bpp images, but current code assumes 3bpp for
some reason.

https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:27 +01:00
Vasily Khoruzhick
8c5f2e6434 aes: unify image processing code for AuthenTec devices
Move overlap detection and assembling code into aeslib to prevent
code duplication

https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:59:22 +01:00
Vasily Khoruzhick
5b20892dd4 Thanks to Greg and Martin from AuthenTec
https://bugs.freedesktop.org/show_bug.cgi?id=57426
2012-11-29 09:51:55 +01:00
Colin Walters
b2a53a459c autogen.sh: Honor NOCONFIGURE=1
See http://people.gnome.org/~walters/docs/build-api.txt
2012-11-16 19:31:30 +01:00
Timo Teräs
45ac0eefb0 uru4000: Fix image capture error on ARM
http://www.mail-archive.com/fprint@lists.freedesktop.org/msg00294.html
2012-11-16 18:38:52 +01:00
Vasily Khoruzhick
ea6d5ba6d6 lib: Fix mess with driver IDs
ID is just a some magic number to make fingerprint from one scanner model
incompatible with another scanner model. Get rid of "magic", declare enum
and use it.

https://bugs.freedesktop.org/show_bug.cgi?id=56956
2012-11-16 17:57:47 +01:00
Vasily Khoruzhick
ed2c75842a upektc: Port to asynchronous model
https://bugs.freedesktop.org/show_bug.cgi?id=56955
2012-11-16 17:56:51 +01:00
Vasily Khoruzhick
b307dd1a6a aes2550: fix bug introduced by last commit
ssm->priv is not aesdev, but fp_img_dev

https://bugs.freedesktop.org/show_bug.cgi?id=56782
2012-11-16 17:46:27 +01:00
Vasily Khoruzhick
39902374ce aes2550: Harden against low finger pressure
Stop scan only after 3rd heartbeat message

https://bugs.freedesktop.org/show_bug.cgi?id=56782
2012-11-06 09:32:07 +01:00
Bastien Nocera
9e92d4cf2c examples: Fix compile-time warnings
A few signedness problems.
2012-11-06 09:30:40 +01:00
Vasily Khoruzhick
6b84c6664f AES1610: driver cleanup, part #2
Remove dead code

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
f569d0bf44 AES1610: driver cleanup, part #1
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
1cfd14b7fe nbis: fix rest of warnings reported by gcc
https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Vasily Khoruzhick
5d32102efe nbis: prefix global variables with "g_"
It fixes a lot of warnings about shadowing global variable

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-06 09:26:17 +01:00
Bastien Nocera
9e10edd422 aes2501: Add year 2008 to the copyright.
https://bugs.freedesktop.org/show_bug.cgi?id=56715#c7
2012-11-05 19:52:46 +01:00
Vasily Khoruzhick
fecf6d6fe5 aes2501: Update copyright line
https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:51:19 +01:00
Vasily Khoruzhick
e32fa8cc38 aes2501: Improve image contrast
When scanning, check the histogram sum, and increase the
ADREFHI register value if the sum is too low, or decrease
it if it's too high.

https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:49:54 +01:00
Vasily Khoruzhick
d8aae30a67 aes2501: Harden against low finger pressure
Wait for 3 empty frames before stopping the scan

The driver used to stop the scan immediately after an empty frame
(by checking for hist sum == 0), but it is possible to get empty
frames in the middle of the scan due to low finger pressure.
Waiting for 3 empty frames stop the driver wrongly aborting the scan
too early.

https://bugs.freedesktop.org/show_bug.cgi?id=56715
2012-11-05 19:47:32 +01:00
Vasily Khoruzhick
84b97ea15b lib: Fix warning when debug logs are disabled
Fix warning in sync.c when debug logs are not enabled

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:55 +01:00
Vasily Khoruzhick
8f98743857 upeksonly: Fix build-time warnings
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:44 +01:00
Vasily Khoruzhick
be29f27e25 upeke2: Fix build-time warnings
Fix warnings reported by gcc

https://bugs.freedesktop.org/show_bug.cgi?id=56439
2012-11-05 19:24:23 +01:00
Timo Teräs
d003f08855 lib: Add support for DigitalPersona URU4500
By adding native encryption support, rather than poking at
the firmware to disable it.

This also makes the URU4000B use native encryption.

https://bugs.freedesktop.org/show_bug.cgi?id=55351
2012-11-05 18:39:16 +01:00
Bastien Nocera
0f7ad00fc4 HACKING: Update with bugzilla link 2012-10-15 11:46:03 +02:00
Vasily Khoruzhick
83333bce3f lib: Add AES2550/AES2810 driver
Initial implementation of AES2550/AES2810 driver.
Does not support AES2810 crypto engine and external flash.

https://bugs.freedesktop.org/show_bug.cgi?id=50859
2012-10-11 15:24:14 +02:00
Bastien Nocera
132b178304 lib: Add driver names to the udev rules 2012-08-28 20:02:17 +01:00
Andrej Krutak
c3689665db lib: Add VFS300/VFS301 driver
https://bugs.freedesktop.org/show_bug.cgi?id=37574
2012-08-28 19:51:59 +01:00
Bastien Nocera
3d222ddda7 lib: Fix whitespace errors 2012-08-28 19:45:08 +01:00
Bastien Nocera
080c414ce9 build: Whitespace fixes in configure.ac 2012-08-28 19:45:08 +01:00
Vasily Khoruzhick
ff02115b0f lib: Fix global variables collision with libusb 2012-08-14 15:25:44 +01:00
Bastien Nocera
3b409c767c build: Allow disabling the update of udev rules
We can only create the udev rules file when we're not cross-compiling,
so allow people to disable updating the file when building from
tarballs.

If you run with --enable-udev-rules=no and the
60-fprint-autosuspend.rules doesn't exist, you're on your own.
2012-08-14 15:19:35 +01:00
Bastien Nocera
d05c69698c build: Fix distcheck'ing not working
We need to be able to override the udev rules directory for
distchecking to work.
2012-08-14 15:12:20 +01:00
Bastien Nocera
2d09b10a27 build: Include udev rules in tarball 2012-08-14 14:52:58 +01:00
Bastien Nocera
bc03d56186 build: Add list of drivers to include when distchecking 2012-08-14 14:51:23 +01:00
Bastien Nocera
96e7224a23 build: Fix distchecking of examples directory 2012-08-14 14:48:43 +01:00
Benedikt Morbach
0f4a75ff61 update udev rules dir location again
Instead of hardcoding whatever the new fashion is every few months, which might
even differ between distributions, just put the rules where udev says we should.

https://bugs.freedesktop.org/show_bug.cgi?id=50426
2012-05-28 14:12:55 +01:00
Bastien Nocera
dfff16f3e3 Don't print duplicate udev rules
https://bugs.freedesktop.org/show_bug.cgi?id=45513
2012-02-02 05:28:17 +00:00
Patrick Marlier
3d2e545264 Fix possible crash without a deactivate callback
We were checking for ->activate existing instead of ->deactivate.
2012-01-20 12:57:27 +00:00
Benedikt Morbach
c96fa32da4 Move udev rules to libdir
This moves the dir from /etc/udev/rules.d to /lib/udev/rules.d,
which is encouraged by upstream, as /etc is for user overrides only

https://bugs.freedesktop.org/show_bug.cgi?id=44507
2012-01-06 12:13:29 +00:00
Matthew Garrett
48ec64f683 Fix udev control path
Newer kernels uses power/control for USB suspend setup, not power/level.
Fix the udev rules generation.
2011-10-04 15:11:08 +01:00
Bastien Nocera
f8aa82a554 0.4.0 2011-04-18 18:02:37 +01:00
Bastien Nocera
1451a2dde2 build: Quiet by default 2011-04-08 15:38:33 +01:00
Sergio Cerlesi
d2e957683f Added support for Validity VFS101
USB ID 138a:0001
2011-04-08 15:37:03 +01:00
Sergio Cerlesi
a3ae96c214 Fix function fpi_im_resize on create new image
The function fpi_im_resize copy the new resized image into old fp_img
instead of newimg so, new image is empty and often it generated a
segmentation fault error.
2011-02-22 19:14:02 +00:00
Sergio Cerlesi
3dd905d4e9 Fix return timeout of fp_get_next_timeout
On function fp_get_next_timeout if exist fprint timeout or libusb
timeout the function return the smaller of they.

But if one of that not exist and have a smaller value the function
return a timeout that not exist.
2011-02-18 13:59:28 +00:00
Bastien Nocera
aab031ce37 0.3.0 2010-09-08 11:11:31 +01:00
Bastien Nocera
6bfc516553 Add test program for C++ support
So that we don't regress
2010-09-08 11:03:58 +01:00
Kunal Gangakhedkar
ff842125c5 Add C++ guards to take care of C++ name-mangling.
The libfprint library is generally built with C calling conventions.
Which makes it difficult to link to C++ programs as it is.

This patch adds the support for linking with C++ code - by telling the C++
compiler to use C calling conventions for libfprint functions.

Reported-by: Guus Ellenkamp <guus@activediscovery.net>
Signed-off-by: Kunal Gangakhedkar <kunal.gangakhedkar@gmail.com>
2010-09-08 11:02:25 +01:00
Hugo Grostabussiat
712ebb6d44 Namespace functions and structs for 147e:2016
Inserted "_2016" in structures, enums and functions names
that are related with device 147e:2016.
2010-08-31 17:51:21 +01:00
Hugo Grostabussiat
a18f318446 Added support for UPEK TCS4C (USB ID 147e:1000)
https://bugs.freedesktop.org/show_bug.cgi?id=29719
2010-08-31 17:49:45 +01:00
Bastien Nocera
15b8e8f376 Remove HAL fdi generation
HAL is deprecated, and the rules are broken when multiple
drivers use the same USB IDs.
2010-08-25 11:20:55 +01:00
Bastien Nocera
9f8dd29a55 Make sure all sources are dist'ed
So that you can compile against ImageMagick even if the
distter doesn't.
2010-08-25 11:17:42 +01:00
Bastien Nocera
f28c579779 Print out summary of build options
At the end of the configure run.
2010-08-25 11:17:19 +01:00
Bastien Nocera
375575b701 Port OpenSSL using code to NSS
As OpenSSL cannot really be used in an LGPLv2+ piece of software
without an exception.

Adapted from the example code at:
http://www.mozilla.org/projects/security/pki/nss/sample-code/sample2.html
and:
http://github.com/bagder/curl/commit/f3b77e5
2010-08-25 10:34:30 +01:00
Alexia Death
25161286f5 Only complete scan with MIN_ROWS in print
Don't consider the scan complete unless theres at least
MIN_ROWS recorded or very long blank read occurred.

Typical problem spot: one brief touch before starting the
actual scan. Happens most commonly if scan is started from
before the first joint resulting in a gap after the inital touch.

http://lists.reactivated.net/pipermail/fprint/2009-December/001406.html
2010-08-19 20:36:16 +01:00
Alexia Death
c575afba9a Make the +2 right shift happen on image handoff.
Rather than when reading the rows. Doing it there seems a lot
more sensible and does not get in the way of minimizing the
impact of lost USB packets.

http://lists.reactivated.net/pipermail/fprint/2009-December/001404.html
2010-08-19 20:29:45 +01:00
Alexia Death
8cd31f6ad8 Try to correct missing packets in image stream
If a packet goes missing, use the data from previous row
to minimize distortion, or use some neutral dummy data.

http://lists.reactivated.net/pipermail/fprint/2009-December/001404.html
2010-08-19 20:27:09 +01:00
Alexia Death
4b518bd230 Fix a segfault if a scan was shorter than 8 lines.
This segfault was caused by proceeding to free the conversion result
without checking for an error condition in image to print
data conversion.

http://lists.reactivated.net/pipermail/fprint/2009-December/001405.html
2010-08-19 20:21:51 +01:00
72 changed files with 25772 additions and 1833 deletions

View File

@@ -4,6 +4,8 @@ Copyright (C) 2006 Pavel Machek <pavel@suse.cz>
Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
Copyright (C) 2007 Cyrille Bagard
Copyright (C) 2007 Vasily Khoruzhick
Copyright (C) 2007-2008,2012 Vasily Khoruzhick <anarsoul@gmail.com>
Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
Copyright (C) 2012 Timo Teräs <timo.teras@iki.fi>

View File

@@ -85,11 +85,11 @@ be reflected by updating the appropriate doxygen comments.
Contributing
============
Patches should be sent to the fprint mailing list detailed on the website.
A subscription is required.
Patches should be sent to the fprint bugzilla:
https://bugs.freedesktop.org/enter_bug.cgi?product=libfprint
Information about libfprint development repositories can be found here:
http://www.reactivated.net/fprint/Libfprint_development
http://www.freedesktop.org/wiki/Software/fprint/libfprint
If you're looking for ideas for things to work on, look at the TODO file or
grep the source code for FIXMEs.

View File

@@ -1,4 +1,3 @@
AUTOMAKE_OPTIONS = dist-bzip2
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
DISTCLEANFILES = ChangeLog libfprint.pc
@@ -9,6 +8,10 @@ if BUILD_EXAMPLES
SUBDIRS += examples
endif
DIST_SUBDIRS = libfprint doc examples
DISTCHECK_CONFIGURE_FLAGS = --with-drivers=all --enable-examples-build --enable-x11-examples-build --with-udev-rules-dir='$${libdir}/udev/rules.d-distcheck'
pkgconfigdir=$(libdir)/pkgconfig
pkgconfig_DATA=libfprint.pc

68
NEWS
View File

@@ -1,6 +1,74 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2015-02-03: v0.6.0 release
* Drivers:
- Reduce duplication between AES3500 and AES4000 drivers and
add support for AES3500 device
- Add support for UPEK 147e:2020 and Upek Eikon 2 devices
- Add EgisTec ES603 driver
- Add VFS5011 driver
- Always perform 5 scans for image enrollment
- Better verification with AES1660 driver
- Better verification for a number of AES drivers
* Library:
- Always use Pixman for image manipulation, gdk-pixbuf and ImageMagick
are not supported any more.
* Udev rules:
- Fix warning when USB hub or system does not support power management
2013-08-11: v0.5.1 release
* Drivers
- Add support for 147e:2020 to upeke2 driver
- Fix possible race condition, and cancellation in uru4000 driver
* Udev rules:
- Add Microsoft keyboard to the suspend blacklist
* Plenty of build fixes
2012-12-03: v0.5.0 release
* Drivers:
- New VFS300/VFS301 driver
- New AES2550/AES2810 drivers
- New AES1660 driver
- New AES2660 driver
- New DigitalPersona URU4500 driver
- Avoid empty capture and improve image contrast in the
AES2501 and AES2550 drivers
- Update upektc driver, add support for Eikon Touch 300
- Fix UrU4000 image capture on ARM
* Library:
- Fix global variable collisions with libusb and other system headers
- Fix possible crash in NBIS image processing with some fingerprints
* Udev rules:
- Fix power control path for newer kernels
- Move udev rules to the correct directory
- Don't print duplicated udev rules
- Include udev rules in the tarball
- Allow disabling of udev rules for cross-compiling
- Add driver names in the generated rules
2011-04-18: v0.4.0 release
* Add support for Validity VFS101 (USB ID 138a:0001)
* Fix crasher when resizing a fingerprint image
* Fix wrong timeout being returned when either of
libusb or libfprint doesn't have a timeout
2010-09-08: v0.3.0 release
* Add support for UPEK TCS4C (USB ID 147e:1000)
* Use NSS instead of OpenSSL for GPL compliance
* upeksonly driver bug fixes
* Fix a crash if a scan was shorter than 8 lines
* Fix compilation with C++ compiler
2010-08-19: v0.2.0 release (since 0.1.0-pre2)
* Add gdk-pixbuf support for image manipulation
* Add udev rules to allow devices to autosuspend

1
THANKS
View File

@@ -8,3 +8,4 @@ Toby Howard (University of Manchester)
Seemant Kulleen
Pavel Herrman
Bastien Nocera
Greg Kerr and Martin Konecny from AuthenTec Inc - hardware donations (AES2550 device), datasheets for AES2550 and AES2810

View File

@@ -4,5 +4,7 @@ aclocal || exit 1
autoheader || exit 1
autoconf || exit 1
automake -a -c || exit 1
./configure --enable-maintainer-mode --enable-examples-build \
if test -z "$NOCONFIGURE"; then
exec ./configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*
fi

View File

@@ -1,14 +1,18 @@
AC_INIT([libfprint], [0.2.0])
AM_INIT_AUTOMAKE([1.11 dist-bzip2 no-dist-gzip check-news])
AC_INIT([libfprint], [0.6.0])
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news subdir-objects])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
AM_CONFIG_HEADER([config.h])
AC_CONFIG_HEADERS([config.h])
# Enable silent build when available (Automake 1.11)
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_PREREQ([2.50])
AC_PROG_CC
AC_PROG_LIBTOOL
AC_C_INLINE
AM_PROG_CC_C_O
AC_PROG_CXX
AC_DEFINE([_GNU_SOURCE], [], [Use GNU extensions])
# Library versioning
@@ -19,10 +23,12 @@ AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes2501 aes4000"
all_drivers="upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes3500 aes4000 vfs101 vfs301 vfs5011 upektc_img etes603"
require_imaging='no'
require_aeslib='no'
require_aesX660='no'
require_aes3k='no'
enable_upeke2='no'
enable_upekts='no'
enable_upektc='no'
@@ -31,14 +37,27 @@ enable_vcom5s='no'
enable_uru4000='no'
enable_fdu2000='no'
enable_aes1610='no'
enable_aes1660='no'
enable_aes2501='no'
enable_aes2550='no'
enable_aes2660='no'
enable_aes3500='no'
enable_aes4000='no'
enable_vfs101='no'
enable_vfs301='no'
enable_vfs5011='no'
enable_upektc_img='no'
enable_etes603='no'
AC_ARG_WITH([drivers],[AS_HELP_STRING([--with-drivers],
[List of drivers to enable])],
[drivers="$withval"],
[drivers="$all_drivers"])
if test "x$drivers" = "xall" ; then
drivers=$all_drivers
fi
for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
case ${driver} in
upekts)
@@ -63,7 +82,8 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
;;
fdu2000)
AC_DEFINE([ENABLE_FDU2000], [], [Build Secugen FDU 2000 driver])
enable_fdu2000="yes"
enable_fdu2000="no"
# Driver not ported
;;
vcom5s)
AC_DEFINE([ENABLE_VCOM5S], [], [Build Veridicom 5thSense driver])
@@ -74,31 +94,87 @@ for driver in `echo ${drivers} | sed -e 's/,/ /g' -e 's/,$//g'`; do
require_aeslib="yes"
enable_aes2501="yes"
;;
aes2550)
AC_DEFINE([ENABLE_AES2550], [], [Build AuthenTec AES2550/AES2810 driver])
require_aeslib="yes"
enable_aes2550="yes"
;;
aes1610)
AC_DEFINE([ENABLE_AES1610], [], [Build AuthenTec AES1610 driver])
require_aeslib="yes"
enable_aes1610="yes"
;;
aes1660)
AC_DEFINE([ENABLE_AES1660], [], [Build AuthenTec AES1660 driver])
require_aeslib="yes"
require_aesX660="yes"
enable_aes1660="yes"
;;
aes2660)
AC_DEFINE([ENABLE_AES2660], [], [Build AuthenTec AES1660 driver])
require_aeslib="yes"
require_aesX660="yes"
enable_aes2660="yes"
;;
aes3500)
AC_DEFINE([ENABLE_AES3500], [], [Build AuthenTec AES3500 driver])
require_aeslib="yes"
require_imaging="yes"
require_aes3k="yes"
enable_aes3500="yes"
;;
aes4000)
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
require_aeslib="yes"
require_imaging="yes"
require_aes3k="yes"
enable_aes4000="yes"
;;
vfs101)
AC_DEFINE([ENABLE_VFS101], [], [Build Validity VFS101 driver])
enable_vfs101="yes"
;;
vfs301)
AC_DEFINE([ENABLE_VFS301], [], [Build Validity VFS301/VFS300 driver])
enable_vfs301="yes"
;;
vfs5011)
AC_DEFINE([ENABLE_VFS5011], [], [Build Validity VFS5011 driver])
enable_vfs5011="yes"
;;
upektc_img)
AC_DEFINE([ENABLE_UPEKTC_IMG], [], [Build Upek TouchChip Fingerprint Coprocessor driver])
enable_upektc_img="yes"
;;
etes603)
AC_DEFINE([ENABLE_ETES603], [], [Build EgisTec ES603 driver])
enable_etes603="yes"
;;
esac
done
AM_CONDITIONAL([ENABLE_UPEKTS], [test "$enable_upekts" != "no"])
AM_CONDITIONAL([ENABLE_UPEKE2], [test "$enable_upeke2" != "no"])
#AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" != "no"])
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_AES2501], [test "$enable_aes2501" != "no"])
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" != "no"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" != "no"])
AM_CONDITIONAL([ENABLE_UPEKTS], [test "$enable_upekts" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKE2], [test "$enable_upeke2" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKTC], [test "$enable_upektc" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKSONLY], [test "$enable_upeksonly" = "yes"])
AM_CONDITIONAL([ENABLE_VCOM5S], [test "$enable_vcom5s" = "yes"])
AM_CONDITIONAL([ENABLE_URU4000], [test "$enable_uru4000" = "yes"])
AM_CONDITIONAL([ENABLE_FDU2000], [test "$enable_fdu2000" = "yes"])
AM_CONDITIONAL([ENABLE_AES1610], [test "$enable_aes1610" = "yes"])
AM_CONDITIONAL([ENABLE_AES1660], [test "$enable_aes1660" = "yes"])
AM_CONDITIONAL([ENABLE_AES2501], [test "$enable_aes2501" = "yes"])
AM_CONDITIONAL([ENABLE_AES2550], [test "$enable_aes2550" = "yes"])
AM_CONDITIONAL([ENABLE_AES2660], [test "$enable_aes2660" = "yes"])
AM_CONDITIONAL([ENABLE_AES3500], [test "$enable_aes3500" = "yes"])
AM_CONDITIONAL([ENABLE_AES4000], [test "$enable_aes4000" = "yes"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
AM_CONDITIONAL([REQUIRE_AES3K], [test "$require_aes3k" = "yes"])
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
AM_CONDITIONAL([ENABLE_VFS5011], [test "$enable_vfs5011" = "yes"])
AM_CONDITIONAL([ENABLE_UPEKTC_IMG], [test "$enable_upektc_img" = "yes"])
AM_CONDITIONAL([ENABLE_ETES603], [test "$enable_etes603" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
@@ -106,32 +182,49 @@ AC_SUBST(LIBUSB_CFLAGS)
AC_SUBST(LIBUSB_LIBS)
# check for OpenSSL's libcrypto
PKG_CHECK_MODULES(CRYPTO, "libcrypto")
PKG_CHECK_MODULES(CRYPTO, nss)
AC_SUBST(CRYPTO_CFLAGS)
AC_SUBST(CRYPTO_LIBS)
PKG_CHECK_MODULES(GLIB, "glib-2.0")
PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28])
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
imagemagick_found=no
gdkpixbuf_found=no
pixman_found=no
if test "$require_imaging" != "no"; then
PKG_CHECK_MODULES(IMAGING, gthread-2.0 gdk-pixbuf-2.0, [gdkpixbuf_found=yes], [gdkpixbuf_found=no])
if test "$gdkpixbuf_found" != "yes"; then
PKG_CHECK_MODULES(IMAGING, ImageMagick, [imagemagick_found=yes], [imagemagick_found=no])
AC_ARG_ENABLE(udev-rules,
AC_HELP_STRING([--enable-udev-rules],[Update the udev rules]),
[case "${enableval}" in
yes) ENABLE_UDEV_RULES=yes ;;
no) ENABLE_UDEV_RULES=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-udev-rules) ;;
esac],
[ENABLE_UDEV_RULES=yes]) dnl Default value
AM_CONDITIONAL(ENABLE_UDEV_RULES, test x$ENABLE_UDEV_RULES = "xyes")
if test $ENABLE_UDEV_RULES = no && test -d .git/ ; then
AC_MSG_ERROR(--disable-udev-rules can only be used when building from tarballs)
fi
AC_ARG_WITH(udev-rules-dir,
AS_HELP_STRING([--with-udev-rules-dir=DIR],[Installation path for udev rules @<:@auto@:>@]),
[ac_with_udev_rules_dir=$withval],
[ac_with_udev_rules_dir=""])
if test "${ac_with_udev_rules_dir}" = ""; then
ac_with_udev_rules_dir=`$PKG_CONFIG --variable=udevdir udev`/rules.d
fi
AC_MSG_NOTICE([installing udev rules in ${ac_with_udev_rules_dir}])
AC_SUBST([udev_rulesdir],[${ac_with_udev_rules_dir}])
if test "$require_imaging" = "yes"; then
PKG_CHECK_MODULES(IMAGING, pixman-1, [pixman_found=yes], [pixman_found=no])
if test "$pixman_found" != "yes"; then
AC_MSG_ERROR([pixman is required for imaging support])
fi
fi
if test "$require_imaging" != "no"; then
if test "$gdkpixbuf_found" != "yes" && test "$imagemagick_found" != "yes"; then
AC_MSG_ERROR([gdk-pixbuf or ImageMagick is required for imaging support])
fi
fi
AM_CONDITIONAL([REQUIRE_GDKPIXBUF], [test "$gdkpixbuf_found" != "no"])
AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$imagemagick_found" != "no"])
AM_CONDITIONAL([REQUIRE_PIXMAN], [test "$pixman_found" = "yes"])
AC_SUBST(IMAGING_CFLAGS)
AC_SUBST(IMAGING_LIBS)
@@ -159,17 +252,17 @@ if test "x$build_x11_examples" != "xno"; then
AC_MSG_CHECKING(for Xv extensions)
AC_TRY_COMPILE([
#include <X11/Xlib.h>
#include <X11/extensions/Xvlib.h>],[
#include <X11/extensions/Xvlib.h>],[
int main(void) { (void) XvGetPortAttribute(0, 0, 0, 0); return 0; }
],xv=yes,xv=no);
AC_MSG_RESULT($xv)
if test x$xv = xyes; then
XV_LIBS="-lXv -lXext"
XV_CFLAGS=""
if test x$xv = xyes; then
XV_LIBS="-lXv -lXext"
XV_CFLAGS=""
AC_DEFINE(HAVE_XV,1,[defined if XV video overlay is available])
else
AC_MSG_ERROR([XV is required for X11 examples])
fi
fi
])
AC_CHECK_XV
fi
@@ -193,13 +286,132 @@ fi
# Restore gnu89 inline semantics on gcc 4.3 and newer
saved_cflags="$CFLAGS"
CFLAGS="$CFLAGS -fgnu89-inline"
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]), inline_cflags="-fgnu89-inline", inline_cflags="")
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[]])], inline_cflags="-fgnu89-inline", inline_cflags="")
CFLAGS="$saved_cflags"
AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default visibility])
AM_CFLAGS="-std=gnu99 $inline_cflags -Wall -Wundef -Wunused -Wstrict-prototypes -Werror-implicit-function-declaration -Wno-pointer-sign -Wshadow"
AC_SUBST(AM_CFLAGS)
if test "$require_imaging" = "yes"; then
if test x$pixman_found != no; then
AC_MSG_NOTICE([** Using pixman for imaging])
fi
else
AC_MSG_NOTICE([ Imaging support disabled])
fi
if test x$enable_upekts != xno ; then
AC_MSG_NOTICE([** upekts driver enabled])
else
AC_MSG_NOTICE([ upekts driver disabled])
fi
if test x$enable_upeke2 != xno ; then
AC_MSG_NOTICE([** upeke2 driver enabled])
else
AC_MSG_NOTICE([ upeke2 driver disabled])
fi
if test x$enable_upektc != xno ; then
AC_MSG_NOTICE([** upektc driver enabled])
else
AC_MSG_NOTICE([ upektc driver disabled])
fi
if test x$enable_upeksonly != xno ; then
AC_MSG_NOTICE([** upeksonly driver enabled])
else
AC_MSG_NOTICE([ upeksonly driver disabled])
fi
if test x$enable_vcom5s != xno ; then
AC_MSG_NOTICE([** vcom5s driver enabled])
else
AC_MSG_NOTICE([ vcom5s driver disabled])
fi
if test x$enable_uru4000 != xno ; then
AC_MSG_NOTICE([** uru4000 driver enabled])
else
AC_MSG_NOTICE([ uru4000 driver disabled])
fi
if test x$enable_fdu2000 != xno ; then
AC_MSG_NOTICE([** fdu2000 driver enabled])
else
AC_MSG_NOTICE([ fdu2000 driver disabled])
fi
if test x$enable_aes1610 != xno ; then
AC_MSG_NOTICE([** aes1610 driver enabled])
else
AC_MSG_NOTICE([ aes1610 driver disabled])
fi
if test x$enable_aes1660 != xno ; then
AC_MSG_NOTICE([** aes1660 driver enabled])
else
AC_MSG_NOTICE([ aes1660 driver disabled])
fi
if test x$enable_aes2501 != xno ; then
AC_MSG_NOTICE([** aes2501 driver enabled])
else
AC_MSG_NOTICE([ aes2501 driver disabled])
fi
if test x$enable_aes2550 != xno ; then
AC_MSG_NOTICE([** aes2550/aes2810 driver enabled])
else
AC_MSG_NOTICE([ aes2550/aes2810 driver disabled])
fi
if test x$enable_aes2660 != xno ; then
AC_MSG_NOTICE([** aes2660 driver enabled])
else
AC_MSG_NOTICE([ aes2660 driver disabled])
fi
if test x$enable_aes3500 != xno ; then
AC_MSG_NOTICE([** aes3500 driver enabled])
else
AC_MSG_NOTICE([ aes3500 driver disabled])
fi
if test x$enable_aes4000 != xno ; then
AC_MSG_NOTICE([** aes4000 driver enabled])
else
AC_MSG_NOTICE([ aes4000 driver disabled])
fi
if test x$enable_vfs101 != xno ; then
AC_MSG_NOTICE([** vfs101 driver enabled])
else
AC_MSG_NOTICE([ vfs101 driver disabled])
fi
if test x$enable_vfs301 != xno ; then
AC_MSG_NOTICE([** vfs301 driver enabled])
else
AC_MSG_NOTICE([ vfs301 driver disabled])
fi
if test x$enable_vfs5011 != xno ; then
AC_MSG_NOTICE([** vfs5011 driver enabled])
else
AC_MSG_NOTICE([ vfs5011 driver disabled])
fi
if test x$enable_upektc_img != xno ; then
AC_MSG_NOTICE([** upektc_img driver enabled])
else
AC_MSG_NOTICE([ upektc_img driver disabled])
fi
if test x$enable_etes603 != xno ; then
AC_MSG_NOTICE([** etes603 driver enabled])
else
AC_MSG_NOTICE([ etes603 driver disabled])
fi
if test x$require_aeslib != xno ; then
AC_MSG_NOTICE([** aeslib helper functions enabled])
else
AC_MSG_NOTICE([ aeslib helper functions disabled])
fi
if test x$require_aesX660 != xno ; then
AC_MSG_NOTICE([** aesX660 common routines enabled])
else
AC_MSG_NOTICE([ aesX660 common routines disabled])
fi
if test x$require_aes3k != xno ; then
AC_MSG_NOTICE([** aes3k common routines enabled])
else
AC_MSG_NOTICE([ aes3k common routines disabled])
fi
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT

View File

@@ -1,5 +1,5 @@
INCLUDES = -I$(top_srcdir)
noinst_PROGRAMS = verify_live enroll verify img_capture
AM_CFLAGS = -I$(top_srcdir)
noinst_PROGRAMS = verify_live enroll verify img_capture cpp-test
verify_live_SOURCES = verify_live.c
verify_live_LDADD = ../libfprint/libfprint.la
@@ -13,6 +13,9 @@ verify_LDADD = ../libfprint/libfprint.la
img_capture_SOURCES = img_capture.c
img_capture_LDADD = ../libfprint/libfprint.la
cpp_test_SOURCES = cpp-test.cpp
cpp_test_LDADD = ../libfprint/libfprint.la
if BUILD_X11_EXAMPLES
noinst_PROGRAMS += img_capture_continuous

11
examples/cpp-test.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libfprint/fprint.h>
int main (int argc, char **argv)
{
fp_init ();
return 0;
}

View File

@@ -29,7 +29,7 @@
#define FORMAT 0x32595559
static int adaptor = -1;
static unsigned char *framebuffer = NULL;
static char *framebuffer = NULL;
static Display *display = NULL;
static Window window=(Window)NULL;
@@ -50,7 +50,7 @@ static int connection = -1;
u = u > 255 ? 255 : u;\
v = v > 255 ? 255 : v
static void grey2yuy2 (unsigned char *grey, unsigned char *YUV, int num) {
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
int i, j;
int y0, y1, u0, u1, v0, v1;
int gval;
@@ -86,7 +86,7 @@ static void display_frame(struct fp_img *img)
static void QueryXv()
{
int num_adaptors;
unsigned int num_adaptors;
int num_formats;
XvImageFormatValues *formats = NULL;
int i,j;

View File

@@ -1,17 +1,55 @@
lib_LTLIBRARIES = libfprint.la
noinst_PROGRAMS = fprint-list-hal-info fprint-list-udev-rules
MOSTLYCLEANFILES = $(hal_fdi_DATA) $(udev_rules_DATA)
noinst_PROGRAMS = fprint-list-udev-rules
MOSTLYCLEANFILES = $(udev_rules_DATA)
UPEKE2_SRC = drivers/upeke2.c
UPEKTS_SRC = drivers/upekts.c
UPEKTC_SRC = drivers/upektc.c
UPEKTC_SRC = drivers/upektc.c drivers/upektc.h
UPEKSONLY_SRC = drivers/upeksonly.c
URU4000_SRC = drivers/uru4000.c
AES1610_SRC = drivers/aes1610.c
AES1660_SRC = drivers/aes1660.c drivers/aes1660.h
AES2501_SRC = drivers/aes2501.c drivers/aes2501.h
AES2550_SRC = drivers/aes2550.c drivers/aes2550.h
AES2660_SRC = drivers/aes2660.c drivers/aes2660.h
AES3500_SRC = drivers/aes3500.c
AES4000_SRC = drivers/aes4000.c
FDU2000_SRC = drivers/fdu2000.c
VCOM5S_SRC = drivers/vcom5s.c
VFS101_SRC = drivers/vfs101.c
VFS301_SRC = drivers/vfs301.c drivers/vfs301_proto.c drivers/vfs301_proto.h drivers/vfs301_proto_fragments.h
VFS5011_SRC = drivers/vfs5011.c drivers/vfs5011_proto.h
UPEKTC_IMG_SRC = drivers/upektc_img.c drivers/upektc_img.h
ETES603_SRC = drivers/etes603.c
EXTRA_DIST = \
$(UPEKE2_SRC) \
$(UPEKTS_SRC) \
$(UPEKTC_SRC) \
$(UPEKSONLY_SRC) \
$(URU4000_SRC) \
$(AES1610_SRC) \
$(AES1660_SRC) \
$(AES2501_SRC) \
$(AES2550_SRC) \
$(AES2660_SRC) \
$(AES3500_SRC) \
$(AES4000_SRC) \
$(FDU2000_SRC) \
$(VCOM5S_SRC) \
$(VFS101_SRC) \
$(VFS301_SRC) \
$(VFS5011_SRC) \
$(UPEKTC_IMG_SRC) \
$(ETES603_SRC) \
drivers/aesx660.c \
drivers/aesx660.h \
drivers/aes3k.c \
drivers/aes3k.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
pixman.c \
60-fprint-autosuspend.rules
DRIVER_SRC =
OTHER_SRC =
@@ -57,25 +95,16 @@ libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLA
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
fprint_list_hal_info_SOURCES = fprint-list-hal-info.c
fprint_list_hal_info_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_hal_info_LDADD = $(builddir)/libfprint.la
hal_fdi_DATA = 10-fingerprint-reader-fprint.fdi
hal_fdidir = $(datadir)/hal/fdi/information/20thirdparty/
$(hal_fdi_DATA): fprint-list-hal-info
$(builddir)/fprint-list-hal-info > $@
fprint_list_udev_rules_SOURCES = fprint-list-udev-rules.c
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la
fprint_list_udev_rules_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
fprint_list_udev_rules_LDADD = $(builddir)/libfprint.la $(GLIB_LIBS)
udev_rules_DATA = 60-fprint-autosuspend.rules
udev_rulesdir = $(sysconfdir)/udev/rules.d/
if ENABLE_UDEV_RULES
$(udev_rules_DATA): fprint-list-udev-rules
$(builddir)/fprint-list-udev-rules > $@
endif
if ENABLE_UPEKE2
DRIVER_SRC += $(UPEKE2_SRC)
@@ -89,9 +118,9 @@ if ENABLE_UPEKSONLY
DRIVER_SRC += $(UPEKSONLY_SRC)
endif
#if ENABLE_UPEKTC
#DRIVER_SRC += $(UPEKTC_SRC)
#endif
if ENABLE_UPEKTC
DRIVER_SRC += $(UPEKTC_SRC)
endif
if ENABLE_URU4000
DRIVER_SRC += $(URU4000_SRC)
@@ -109,22 +138,52 @@ if ENABLE_AES1610
DRIVER_SRC += $(AES1610_SRC)
endif
if ENABLE_AES1660
DRIVER_SRC += $(AES1660_SRC)
endif
if ENABLE_AES2501
DRIVER_SRC += $(AES2501_SRC)
endif
if ENABLE_AES2550
DRIVER_SRC += $(AES2550_SRC)
endif
if ENABLE_AES2660
DRIVER_SRC += $(AES2660_SRC)
endif
if ENABLE_AES3500
DRIVER_SRC += $(AES3500_SRC)
endif
if ENABLE_AES4000
DRIVER_SRC += $(AES4000_SRC)
endif
if REQUIRE_IMAGEMAGICK
OTHER_SRC += imagemagick.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
if ENABLE_VFS101
DRIVER_SRC += $(VFS101_SRC)
endif
if REQUIRE_GDKPIXBUF
OTHER_SRC += gdkpixbuf.c
if ENABLE_VFS301
DRIVER_SRC += $(VFS301_SRC)
endif
if ENABLE_VFS5011
DRIVER_SRC += $(VFS5011_SRC)
endif
if ENABLE_UPEKTC_IMG
DRIVER_SRC += $(UPEKTC_IMG_SRC)
endif
if ENABLE_ETES603
DRIVER_SRC += $(ETES603_SRC)
endif
if REQUIRE_PIXMAN
OTHER_SRC += pixman.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
@@ -133,6 +192,14 @@ if REQUIRE_AESLIB
OTHER_SRC += aeslib.c aeslib.h
endif
if REQUIRE_AESX660
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
endif
if REQUIRE_AES3K
OTHER_SRC += drivers/aes3k.c drivers/aes3k.h
endif
libfprint_la_SOURCES = \
fp_internal.h \
async.c \

View File

@@ -20,6 +20,7 @@
#define FP_COMPONENT "aeslib"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
@@ -157,17 +158,268 @@ void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
continue_write_regv(wdata);
}
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
static inline unsigned char aes_get_pixel(struct aes_stripe *frame,
unsigned int x,
unsigned int y,
unsigned int frame_width,
unsigned int frame_height)
{
size_t row, column;
unsigned char ret;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x07) * 36;
output[width * (row + 1) + column] = ((*input & 0x70) >> 4) * 36;
input++;
ret = frame->data[x * (frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}
static unsigned int calc_error(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
int dx,
int dy,
unsigned int frame_width,
unsigned int frame_height)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = frame_width - (dx > 0 ? dx : -dx);
height = frame_height - dy;
y1 = 0;
y2 = dy;
i = 0;
err = 0;
do {
x1 = dx < 0 ? 0 : dx;
x2 = dx < 0 ? -dx : 0;
j = 0;
do {
unsigned char v1, v2;
v1 = aes_get_pixel(first_frame, x1, y1, frame_width, frame_height);
v2 = aes_get_pixel(second_frame, x2, y2, frame_width, frame_height);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (frame_height * frame_width);
err /= (height * width);
if (err == 0)
return INT_MAX;
return err;
}
/* This function is rather CPU-intensive. It's better to use hardware
* to detect movement direction when possible.
*/
static void find_overlap(struct aes_stripe *first_frame,
struct aes_stripe *second_frame,
unsigned int *min_error,
unsigned int frame_width,
unsigned int frame_height)
{
int dx, dy;
unsigned int err;
*min_error = INT_MAX;
/* Seeking in horizontal and vertical dimensions,
* for horizontal dimension we'll check only 8 pixels
* in both directions. For vertical direction diff is
* rarely less than 2, so start with it.
*/
for (dy = 2; dy < frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(first_frame, second_frame,
dx, dy, frame_width, frame_height);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
unsigned int aes_calc_delta(GSList *stripes, size_t num_stripes,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
int height = 0;
struct aes_stripe *prev_stripe = list_entry->data;
unsigned int min_error;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct aes_stripe *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(prev_stripe, cur_stripe, &min_error,
frame_width, frame_height);
prev_stripe->delta_y = -prev_stripe->delta_y;
prev_stripe->delta_x = -prev_stripe->delta_x;
}
else
find_overlap(cur_stripe, prev_stripe, &min_error,
frame_width, frame_height);
frame++;
height += prev_stripe->delta_y;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
if (height < 0)
height = -height;
height += frame_height;
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return height;
}
static inline void aes_blit_stripe(struct fp_img *img,
struct aes_stripe *stripe,
int x, int y, unsigned int frame_width,
unsigned int frame_height)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = frame_height + y;
} else {
iy = y;
fy = 0;
height = frame_height;
}
if (fx > frame_width)
return;
if (fy > frame_height)
return;
if (ix > img->width)
return;
if (iy > img->height)
return;
if ((iy + height) > img->height)
height = img->height - iy;
for (; fy < height; fy++, iy++) {
if (x < 0) {
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
}
for (; fx < width; fx++, ix++) {
img->data[ix + (iy * img->width)] = aes_get_pixel(stripe, fx, fy, frame_width, frame_height);
}
}
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height, unsigned int img_width)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct aes_stripe *aes_stripe;
BUG_ON(stripes_len == 0);
BUG_ON(img_width < frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
aes_stripe = stripe->data;
aes_stripe->delta_x = 0;
aes_stripe->delta_y = 0;
do {
aes_stripe = stripe->data;
height += aes_stripe->delta_y;
i++;
stripe = g_slist_next(stripe);
} while (i < stripes_len);
fp_dbg("height is %d", height);
if (height < 0) {
reverse = TRUE;
height = -height;
}
/* For last frame */
height += frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(img_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->width = img_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - frame_height) : 0;
x = (img_width - frame_width) / 2;
do {
aes_stripe = stripe->data;
y += aes_stripe->delta_y;
x += aes_stripe->delta_x;
aes_blit_stripe(img, aes_stripe, x, y, frame_width, frame_height);
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}

View File

@@ -27,14 +27,24 @@ struct aes_regwrite {
unsigned char value;
};
struct aes_stripe {
int delta_x;
int delta_y;
unsigned char data[0];
};
typedef void (*aes_write_regv_cb)(struct fp_img_dev *dev, int result,
void *user_data);
void aes_write_regv(struct fp_img_dev *dev, const struct aes_regwrite *regs,
unsigned int num_regs, aes_write_regv_cb callback, void *user_data);
void aes_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output);
unsigned int aes_calc_delta(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height,
gboolean reverse);
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height, unsigned int img_width);
#endif

View File

@@ -412,3 +412,101 @@ void fpi_drvcb_identify_stopped(struct fp_dev *dev)
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
}
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
fp_capture_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
if (!drv->capture_start)
return -ENOTSUP;
dev->state = DEV_STATE_CAPTURE_STARTING;
dev->capture_cb = callback;
dev->capture_cb_data = user_data;
dev->unconditional_capture = unconditional;
r = drv->capture_start(dev);
if (r < 0) {
dev->capture_cb = NULL;
dev->state = DEV_STATE_ERROR;
fp_err("failed to start verification, error %d", r);
}
return r;
}
/* Drivers call this when capture has started */
void fpi_drvcb_capture_started(struct fp_dev *dev, int status)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_CAPTURE_STARTING);
if (status) {
if (status > 0) {
status = -status;
fp_dbg("adjusted to %d", status);
}
dev->state = DEV_STATE_ERROR;
if (dev->capture_cb)
dev->capture_cb(dev, status, NULL, dev->capture_cb_data);
} else {
dev->state = DEV_STATE_CAPTURING;
}
}
/* Drivers call this to report a capture result (which might mark completion) */
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img)
{
fp_dbg("result %d", result);
BUG_ON(dev->state != DEV_STATE_CAPTURING);
if (result < 0 || result == FP_CAPTURE_COMPLETE)
dev->state = DEV_STATE_CAPTURE_DONE;
if (dev->capture_cb)
dev->capture_cb(dev, result, img, dev->capture_cb_data);
else
fp_dbg("ignoring capture result as no callback is subscribed");
}
/* Drivers call this when capture has stopped */
void fpi_drvcb_capture_stopped(struct fp_dev *dev)
{
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_CAPTURE_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->capture_stop_cb)
dev->capture_stop_cb(dev, dev->capture_stop_cb_data);
}
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
fp_capture_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
int r;
fp_dbg("");
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_CAPTURING
&& dev->state != DEV_STATE_CAPTURE_DONE);
dev->capture_cb = NULL;
dev->capture_stop_cb = callback;
dev->capture_stop_cb_data = user_data;
dev->state = DEV_STATE_CAPTURE_STOPPING;
if (!drv->capture_start)
return -ENOTSUP;
if (!drv->capture_stop) {
dev->state = DEV_STATE_INITIALIZED;
fpi_drvcb_capture_stopped(dev);
return 0;
}
r = drv->capture_stop(dev);
if (r < 0) {
fp_err("failed to stop verification");
dev->capture_stop_cb = NULL;
}
return r;
}

View File

@@ -291,25 +291,25 @@ void fpi_log(enum fpi_log_level level, const char *component,
#ifndef ENABLE_DEBUG_LOGGING
if (!log_level)
return;
if (level == LOG_LEVEL_WARNING && log_level < 2)
if (level == FPRINT_LOG_LEVEL_WARNING && log_level < 2)
return;
if (level == LOG_LEVEL_INFO && log_level < 3)
if (level == FPRINT_LOG_LEVEL_INFO && log_level < 3)
return;
#endif
switch (level) {
case LOG_LEVEL_INFO:
case FPRINT_LOG_LEVEL_INFO:
prefix = "info";
break;
case LOG_LEVEL_WARNING:
case FPRINT_LOG_LEVEL_WARNING:
stream = stderr;
prefix = "warning";
break;
case LOG_LEVEL_ERROR:
case FPRINT_LOG_LEVEL_ERROR:
stream = stderr;
prefix = "error";
break;
case LOG_LEVEL_DEBUG:
case FPRINT_LOG_LEVEL_DEBUG:
stream = stderr;
prefix = "debug";
break;
@@ -349,12 +349,18 @@ static struct fp_driver * const primitive_drivers[] = {
};
static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_AES3500
&aes3500_driver,
#endif
#ifdef ENABLE_AES4000
&aes4000_driver,
#endif
#ifdef ENABLE_AES2501
&aes2501_driver,
#endif
#ifdef ENABLE_AES2550
&aes2550_driver,
#endif
#ifdef ENABLE_URU4000
&uru4000_driver,
#endif
@@ -368,10 +374,31 @@ static struct fp_img_driver * const img_drivers[] = {
#ifdef ENABLE_AES1610
&aes1610_driver,
#endif
/*#ifdef ENABLE_UPEKTC
#ifdef ENABLE_AES1660
&aes1660_driver,
#endif
#ifdef ENABLE_AES2660
&aes2660_driver,
#endif
#ifdef ENABLE_VFS101
&vfs101_driver,
#endif
#ifdef ENABLE_VFS301
&vfs301_driver,
#endif
#ifdef ENABLE_VFS5011
&vfs5011_driver,
#endif
#ifdef ENABLE_UPEKTC
&upektc_driver,
#endif
#ifdef ENABLE_FDU2000
#ifdef ENABLE_UPEKTC_IMG
&upektc_img_driver,
#endif
#ifdef ENABLE_ETES603
&etes603_driver,
#endif
/*#ifdef ENABLE_FDU2000
&fdu2000_driver,
#endif
*/
@@ -786,7 +813,7 @@ static struct fp_img_dev *dev_to_img_dev(struct fp_dev *dev)
*/
API_EXPORTED int fp_dev_supports_imaging(struct fp_dev *dev)
{
return dev->drv->type == DRIVER_IMAGING;
return dev->drv->capture_start != NULL;
}
/** \ingroup dev
@@ -801,38 +828,6 @@ API_EXPORTED int fp_dev_supports_identification(struct fp_dev *dev)
return dev->drv->identify_start != NULL;
}
/** \ingroup dev
* Captures an \ref img "image" from a device. The returned image is the raw
* image provided by the device, you may wish to \ref img_std "standardize" it.
*
* If set, the <tt>unconditional</tt> flag indicates that the device should
* capture an image unconditionally, regardless of whether a finger is there
* or not. If unset, this function will block until a finger is detected on
* the sensor.
*
* \param dev the device
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
* \param image a location to return the captured image. Must be freed with
* fp_img_free() after use.
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
* unconditional flag was set but the device does not support this, or that the
* device does not support imaging.
* \sa fp_dev_supports_imaging()
*/
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **image)
{
struct fp_img_dev *imgdev = dev_to_img_dev(dev);
if (!imgdev) {
fp_dbg("image capture on non-imaging device");
return -ENOTSUP;
}
//return fpi_imgdev_capture(imgdev, unconditional, image);
/* FIXME reimplement async */
return -ENOTSUP;
}
/** \ingroup dev
* Gets the expected width of images that will be captured from the device.
* This function will return -1 for devices that are not

View File

@@ -95,22 +95,33 @@ static const char *finger_num_to_str(enum fp_finger finger)
#endif
static struct fp_print_data *print_data_new(uint16_t driver_id,
uint32_t devtype, enum fp_print_data_type type, size_t length)
uint32_t devtype, enum fp_print_data_type type)
{
struct fp_print_data *data = g_malloc(sizeof(*data) + length);
fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
memset(data, 0, sizeof(*data));
struct fp_print_data *data = g_malloc0(sizeof(*data));
fp_dbg("driver=%02x devtype=%04x", driver_id, devtype);
data->driver_id = driver_id;
data->devtype = devtype;
data->type = type;
data->length = length;
return data;
}
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
void fpi_print_data_item_free(struct fp_print_data_item *item)
{
g_free(item);
}
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
{
struct fp_print_data_item *item = g_malloc(sizeof(*item) + length);
item->length = length;
return item;
}
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev)
{
return print_data_new(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), length);
fpi_driver_get_data_type(dev->drv));
}
/** \ingroup print_data
@@ -125,27 +136,115 @@ struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length)
API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
unsigned char **ret)
{
struct fpi_print_data_fp1 *buf;
size_t buflen;
struct fpi_print_data_fp2 *out_data;
struct fpi_print_data_item_fp2 *out_item;
struct fp_print_data_item *item;
size_t buflen = 0;
GSList *list_item;
unsigned char *buf;
fp_dbg("");
buflen = sizeof(*buf) + data->length;
buf = malloc(buflen);
if (!buf)
return 0;
list_item = data->prints;
while (list_item) {
item = list_item->data;
buflen += sizeof(*out_item);
buflen += item->length;
list_item = g_slist_next(list_item);
}
buflen += sizeof(*out_data);
out_data = g_malloc(buflen);
*ret = (unsigned char *) out_data;
buf = out_data->data;
out_data->prefix[0] = 'F';
out_data->prefix[1] = 'P';
out_data->prefix[2] = '2';
out_data->driver_id = GUINT16_TO_LE(data->driver_id);
out_data->devtype = GUINT32_TO_LE(data->devtype);
out_data->data_type = data->type;
list_item = data->prints;
while (list_item) {
item = list_item->data;
out_item = (struct fpi_print_data_item_fp2 *)buf;
out_item->length = GUINT32_TO_LE(item->length);
/* FIXME: fp_print_data_item->data content is not endianess agnostic */
memcpy(out_item->data, item->data, item->length);
buf += sizeof(*out_item);
buf += item->length;
list_item = g_slist_next(list_item);
}
*ret = (unsigned char *) buf;
buf->prefix[0] = 'F';
buf->prefix[1] = 'P';
buf->prefix[2] = '1';
buf->driver_id = GUINT16_TO_LE(data->driver_id);
buf->devtype = GUINT32_TO_LE(data->devtype);
buf->data_type = data->type;
memcpy(buf->data, data->data, data->length);
return buflen;
}
static struct fp_print_data *fpi_print_data_from_fp1_data(unsigned char *buf,
size_t buflen)
{
size_t print_data_len;
struct fp_print_data *data;
struct fp_print_data_item *item;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
print_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type);
item = fpi_print_data_item_new(print_data_len);
/* FIXME: fp_print_data->data content is not endianess agnostic */
memcpy(item->data, raw->data, print_data_len);
data->prints = g_slist_prepend(data->prints, item);
return data;
}
static struct fp_print_data *fpi_print_data_from_fp2_data(unsigned char *buf,
size_t buflen)
{
size_t total_data_len, item_len;
struct fp_print_data *data;
struct fp_print_data_item *item;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
unsigned char *raw_buf;
struct fpi_print_data_item_fp2 *raw_item;
total_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type);
raw_buf = raw->data;
while (total_data_len) {
if (total_data_len < sizeof(*raw_item))
break;
total_data_len -= sizeof(*raw_item);
raw_item = (struct fpi_print_data_item_fp2 *)raw_buf;
item_len = GUINT32_FROM_LE(raw_item->length);
fp_dbg("item len %d, total_data_len %d", item_len, total_data_len);
if (total_data_len < item_len) {
fp_err("corrupted fingerprint data");
break;
}
total_data_len -= item_len;
item = fpi_print_data_item_new(item_len);
/* FIXME: fp_print_data->data content is not endianess agnostic */
memcpy(item->data, raw_item->data, item_len);
data->prints = g_slist_prepend(data->prints, item);
raw_buf += sizeof(*raw_item);
raw_buf += item_len;
}
if (g_slist_length(data->prints) == 0) {
fp_print_data_free(data);
data = NULL;
}
return data;
}
/** \ingroup print_data
* Load a stored print from a data buffer. The contents of said buffer must
* be the untouched contents of a buffer previously supplied to you by the
@@ -158,24 +257,21 @@ API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data,
API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf,
size_t buflen)
{
struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf;
size_t print_data_len;
struct fp_print_data *data;
struct fpi_print_data_fp2 *raw = (struct fpi_print_data_fp2 *) buf;
fp_dbg("buffer size %zd", buflen);
if (buflen < sizeof(*raw))
return NULL;
if (strncmp(raw->prefix, "FP1", 3) != 0) {
if (strncmp(raw->prefix, "FP1", 3) == 0) {
return fpi_print_data_from_fp1_data(buf, buflen);
} else if (strncmp(raw->prefix, "FP2", 3) == 0) {
return fpi_print_data_from_fp2_data(buf, buflen);
} else {
fp_dbg("bad header prefix");
return NULL;
}
print_data_len = buflen - sizeof(*raw);
data = print_data_new(GUINT16_FROM_LE(raw->driver_id),
GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len);
memcpy(data->data, raw->data, print_data_len);
return data;
return NULL;
}
static char *get_path_to_storedir(uint16_t driver_id, uint32_t devtype)
@@ -406,6 +502,8 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
*/
API_EXPORTED void fp_print_data_free(struct fp_print_data *data)
{
if (data)
g_slist_free_full(data->prints, (GDestroyNotify)fpi_print_data_item_free);
g_free(data);
}

View File

@@ -4,6 +4,7 @@
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from libfprint aes2501 driver.
*
@@ -32,6 +33,8 @@
#include <aeslib.h>
#include <fp_internal.h>
#include "driver_ids.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);
@@ -110,103 +113,6 @@ static void generic_ignore_data_cb(struct libusb_transfer *transfer)
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;
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) {
g_free(data);
libusb_free_transfer(transfer);
result = -EIO;
goto err;
}
return;
err:
rdata->callback(dev, result, NULL, rdata->user_data);
g_free(rdata);
}
// 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);
}
/* 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_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 generic_write_regv_cb(struct fp_img_dev *dev, int result,
void *user_data)
{
@@ -217,8 +123,6 @@ static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
fpi_ssm_mark_aborted(ssm, result);
}
/* 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)
@@ -244,150 +148,6 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
}
}
/****** IMAGE PROCESSING ******/
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 *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(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
* 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(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct 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 ******/
@@ -416,15 +176,6 @@ static const struct aes_regwrite finger_det_reqs[] = {
{ 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)
@@ -461,12 +212,6 @@ out:
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;
@@ -500,7 +245,6 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_da
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);
@@ -780,7 +524,7 @@ static int adjust_gain(unsigned char *buffer, int status)
/*
* Restore the default gain values */
static void restore_gain()
static void restore_gain(void)
{
strip_scan_reqs[0].value = list_BE_values[0];
strip_scan_reqs[1].value = 0x04;
@@ -815,7 +559,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
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);
@@ -826,17 +569,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
}
/* 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++)
@@ -876,11 +608,30 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* stop capturing if MAX_FRAMES is reached */
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
struct fp_img *img;
unsigned int height, rev_height;
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);
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
aesdev->blanks_count = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
@@ -987,26 +738,9 @@ static const struct aes_regwrite stop_reader[] = {
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 {
fpi_ssm_next_state(ssm);
}
}
*/
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
@@ -1018,14 +752,6 @@ static void activate_run_state(struct fpi_ssm *ssm)
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;*/
}
}
@@ -1106,7 +832,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes1610_driver = {
.driver = {
.id = 6,
.id = AES1610_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1610",
.id_table = id_table,
@@ -1114,13 +840,9 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 128,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
/* 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,
.bz3_threshold = 50,
.open = dev_init,
.close = dev_deinit,

111
libfprint/drivers/aes1660.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* AuthenTec AES1660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aes1660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "aesx660.h"
#include "aes1660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 128
#define SCALE_FACTOR 2
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
aesdev->init_seqs[0] = aes1660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes1660_init_1);
aesdev->init_seqs[1] = aes1660_init_2;
aesdev->init_seqs_len[1] = array_n_elements(aes1660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes1660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes1660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x1660 },
{ .vendor = 0x08ff, .product = 0x1680 },
{ .vendor = 0x08ff, .product = 0x1681 },
{ .vendor = 0x08ff, .product = 0x1682 },
{ .vendor = 0x08ff, .product = 0x1683 },
{ .vendor = 0x08ff, .product = 0x1684 },
{ .vendor = 0x08ff, .product = 0x1685 },
{ .vendor = 0x08ff, .product = 0x1686 },
{ .vendor = 0x08ff, .product = 0x1687 },
{ .vendor = 0x08ff, .product = 0x1688 },
{ .vendor = 0x08ff, .product = 0x1689 },
{ .vendor = 0x08ff, .product = 0x168a },
{ .vendor = 0x08ff, .product = 0x168b },
{ .vendor = 0x08ff, .product = 0x168c },
{ .vendor = 0x08ff, .product = 0x168d },
{ .vendor = 0x08ff, .product = 0x168e },
{ .vendor = 0x08ff, .product = 0x168f },
{ 0, 0, 0, },
};
struct fp_img_driver aes1660_driver = {
.driver = {
.id = AES1660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES1660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 70,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

1990
libfprint/drivers/aes1660.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* AuthenTec AES2501 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007 Vasily Khoruzhick
* Copyright (C) 2007-2008, 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* Based on code from http://home.gna.org/aes2501, relicensed with permission
*
@@ -30,7 +30,9 @@
#include <aeslib.h>
#include <fp_internal.h>
#include "aes2501.h"
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
@@ -67,6 +69,7 @@ struct aes2501_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int no_finger_cnt;
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
@@ -245,119 +248,6 @@ static int sum_histogram_values(unsigned char *data, uint8_t threshold)
return r;
}
/* 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(struct aes2501_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;
/* Rotating given data by 90 degrees
* Taken from document describing aes2501 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(list_entry->data, FRAME_WIDTH, FRAME_HEIGHT, output);
if (reverse)
output -= FRAME_SIZE;
else
output += FRAME_SIZE;
list_entry = g_slist_next(list_entry);
}
/* Detecting where frames overlaped */
output = assembled;
for (frame = 1; frame < num_strips; frame++) {
int not_overlapped;
output += FRAME_SIZE;
not_overlapped = find_overlap(assembled, output, &min_error);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += FRAME_WIDTH * not_overlapped;
memcpy(assembled, output, FRAME_SIZE);
}
return image_height;
}
static void assemble_and_submit_image(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
size_t final_size;
struct fp_img *img;
unsigned int errors_sum, r_errors_sum;
BUG_ON(aesdev->strips_len == 0);
/* reverse list */
aesdev->strips = g_slist_reverse(aesdev->strips);
/* create buffer big enough for max image */
img = fpi_img_new(aesdev->strips_len * FRAME_SIZE);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->height = assemble(aesdev, img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(aesdev, img->data, FALSE, &errors_sum);
img->flags |= FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
fp_dbg("normal scan direction");
} else {
fp_dbg("reversed scan direction");
}
/* now that overlap has been removed, resize output image buffer */
final_size = img->height * FRAME_WIDTH;
img = fpi_img_resize(img, final_size);
fpi_imgdev_image_captured(dev, img);
/* free strips and strip list */
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
}
/****** FINGER PRESENCE DETECTION ******/
static const struct aes_regwrite finger_det_reqs[] = {
@@ -514,13 +404,13 @@ static const struct aes_regwrite capture_reqs_2[] = {
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
static const struct aes_regwrite strip_scan_reqs[] = {
static struct aes_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_ADREFHI, AES2501_ADREFHI_MAX_VALUE },
{ AES2501_REG_ADREFLO, 0x20 },
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
};
@@ -559,12 +449,6 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
goto out;
}
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
memcpy(stripdata, data + 1, 192*8);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
threshold = regval_from_dump(data + 1 + 192*8 + 1 + 16*2 + 1 + 8,
AES2501_REG_DATFMT);
if (threshold < 0) {
@@ -572,27 +456,68 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
goto out;
}
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
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? */
/* FIXME: we should have an upper limit on the number of strips */
if (sum < AES2501_SUM_LOW_THRESH) {
strip_scan_reqs[4].value -= 0x8;
if (strip_scan_reqs[4].value < AES2501_ADREFHI_MIN_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MIN_VALUE;
} else if (sum > AES2501_SUM_HIGH_THRESH) {
strip_scan_reqs[4].value += 0x8;
if (strip_scan_reqs[4].value > AES2501_ADREFHI_MAX_VALUE)
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
}
fp_dbg("ADREFHI is %.2x", strip_scan_reqs[4].value);
/* If sum is 0, finger has been removed */
/* Sum is 0, maybe finger was removed? Wait for 3 empty frames
* to ensure
*/
if (sum == 0) {
/* 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);
aesdev->no_finger_cnt++;
if (aesdev->no_finger_cnt == 3) {
struct fp_img *img;
unsigned int height, rev_height;
aesdev->strips = g_slist_reverse(aesdev->strips);
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
rev_height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, TRUE);
fp_dbg("heights: %d rev: %d", height, rev_height);
if (rev_height < height) {
fp_dbg("Reversed direction");
height = aes_calc_delta(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FALSE);
}
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct aes_stripe *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, 192*8);
aesdev->no_finger_cnt = 0;
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
}
@@ -677,6 +602,9 @@ static void start_capture(struct fp_img_dev *dev)
return;
}
aesdev->no_finger_cnt = 0;
/* Reset gain */
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
@@ -945,7 +873,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes2501_driver = {
.driver = {
.id = 4,
.id = AES2501_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2501",
.id_table = id_table,
@@ -953,7 +881,7 @@ struct fp_img_driver aes2501_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

View File

@@ -167,4 +167,10 @@ enum aes2501_sensor_gain2 {
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
#define AES2501_ADREFHI_MIN_VALUE 0x28
#define AES2501_ADREFHI_MAX_VALUE 0x58
#define AES2501_SUM_HIGH_THRESH 1000
#define AES2501_SUM_LOW_THRESH 700
#endif /* __AES2501_H */

653
libfprint/drivers/aes2550.c Normal file
View File

@@ -0,0 +1,653 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2012 Vasily Khoruzhick
*
* Based on AES2501 driver
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aes2550"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aes2550.h"
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
/*
* The AES2550 is an imaging device using a swipe-type sensor. It samples
* the finger at preprogrammed intervals, sending a 192x16 frame to the
* computer.
* Unless the user is scanning their finger unreasonably fast, the frames
* *will* overlap. The implementation below detects this overlap and produces
* a contiguous image as the end result.
* The fact that the user determines the length of the swipe (and hence the
* number of useful frames) and also the fact that overlap varies means that
* images returned from this driver vary in height.
*/
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
struct aes2550_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
int heartbeat_cnt;
};
/****** FINGER PRESENCE DETECTION ******/
static unsigned char finger_det_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x95, (8 << AES2550_REG95_COL_SCANNED_OFS) | (1 << AES2550_REG95_EPIX_AVG_OFS),
0xad, 0x00,
0xbd, (0 << AES2550_REGBD_LPO_IN_15_8_OFS),
0xbe, (0 << AES2550_REGBE_LPO_IN_7_0_OFS),
0xcf, AES2550_REGCF_INTERFERENCE_CHK_EN,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_RUN_FD,
};
static void start_finger_detection(struct fp_img_dev *dev);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
unsigned char *data = transfer->buffer;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
}
fp_dbg("transfer completed, len: %.4x, data: %.2x %.2x",
transfer->actual_length, (int)data[0], (int)data[1]);
/* Check if we got 2 bytes, reg address 0x83 and its value */
if ((transfer->actual_length >= 2) && (data[0] == 0x83) && (data[1] & AES2550_REG83_FINGER_PRESENT)) {
/* finger present, start capturing */
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
} else {
/* no finger, poll for a new histogram */
start_finger_detection(dev);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_reqs_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, got %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
goto exit_free_transfer;
}
/* 2 bytes of result */
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct aes2550_dev *aesdev = dev->priv;
struct libusb_transfer *transfer;
fp_dbg("");
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, finger_det_reqs,
sizeof(finger_det_reqs), finger_det_reqs_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
static unsigned char capture_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_HGC_ENABLE),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0x8f, AES2550_REG8F_AUTH_DISABLE | AES2550_REG8F_EHISTO_DISABLE,
0xbf, AES2550_REGBF_RSR_DIR_UPDOWN_MOTION | AES2550_REGBF_RSR_LEVEL_SUPER_RSR,
0xcf, (3 << AES2550_REGCF_INTERFERENCE_AVG_OFFS) | AES2550_REGCF_INTERFERENCE_AVG_EN,
0xdc, (1 << AES2550_REGDC_BP_NUM_REF_SWEEP_OFS),
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x03, /* Heart beat cmd, 3 * 16 cycles without sending image */
AES2550_CMD_GET_ENROLL_IMG,
};
static unsigned char capture_set_idle_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET,
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
AES2550_CMD_SET_IDLE_MODE,
};
enum capture_states {
CAPTURE_WRITE_REQS,
CAPTURE_READ_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
{
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
struct aes_stripe *stripe;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
fp_dbg("Bogus magic: %.2x\n", (int)(data[0]));
return -EPROTO;
}
len = data[1] * 256 + data[2];
if (len != (AES2550_STRIP_SIZE - 3)) {
fp_dbg("Bogus frame len: %.4x\n", len);
}
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bits per pixel */
stripe->delta_x = (int8_t)data[6];
stripe->delta_y = -(int8_t)data[7];
stripdata = stripe->data;
memcpy(stripdata, data + 33, FRAME_WIDTH * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
return 0;
}
static void capture_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length) &&
aesdev->strips_len) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT, FRAME_WIDTH + FRAME_WIDTH / 2);
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
/* marking machine complete will re-trigger finger detection loop */
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
int r;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length >= 2)
fp_dbg("data: %.2x %.2x", (int)data[0], (int)data[1]);
switch (transfer->actual_length) {
case AES2550_STRIP_SIZE:
r = process_strip_data(ssm, data);
if (r < 0) {
fp_dbg("Processing strip data failed: %d", r);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
aesdev->heartbeat_cnt = 0;
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
case AES2550_HEARTBEAT_SIZE:
if (data[0] == AES2550_HEARTBEAT_MAGIC) {
/* No data for a long time => finger was removed or there's no movement */
aesdev->heartbeat_cnt++;
if (aesdev->heartbeat_cnt == 3) {
/* Got 3 heartbeat message, that's enough to consider that finger was removed,
* assemble image and submit it to the library */
fp_dbg("Got 3 heartbeats => finger removed");
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
}
}
break;
default:
fp_dbg("Short frame %d, skip", transfer->actual_length);
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int r;
switch (ssm->cur_state) {
case CAPTURE_WRITE_REQS:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_reqs,
sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
case CAPTURE_SET_IDLE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_set_idle_reqs,
sizeof(capture_set_idle_reqs), capture_set_idle_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
fp_dbg("Capture completed");
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
aesdev->heartbeat_cnt = 0;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
static unsigned char init_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_FORCE_FINGER_PRESENT),
0x85, AES2550_REG85_FLUSH_PER_FRAME,
0xa8, AES2550_REGA8_DIG_BIT_EN,
0x81, AES2550_REG81_NSHOT,
};
static unsigned char calibrate_reqs[] = {
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
AES2550_CMD_CALIBRATE,
AES2550_CMD_READ_CALIBRATION_DATA,
};
enum activate_states {
WRITE_INIT,
READ_DATA,
CALIBRATE,
READ_CALIB_TABLE,
ACTIVATE_NUM_STATES,
};
static void init_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void init_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/* TODO: use calibration table, datasheet is rather terse on that
* need more info for implementaion */
static void calibrate_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int r;
switch (ssm->cur_state) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, init_reqs,
sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
init_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
case CALIBRATE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, calibrate_reqs,
sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_CALIB_TABLE:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
calibrate_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
if (!ssm->error)
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
fp_dbg("");
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
dev->priv = g_malloc0(sizeof(struct aes2550_dev));
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2550 }, /* AES2550 */
{ .vendor = 0x08ff, .product = 0x2810 }, /* AES2810 */
{ 0, 0, 0, },
};
struct fp_img_driver aes2550_driver = {
.driver = {
.id = AES2550_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2550/AES2810",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

114
libfprint/drivers/aes2550.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* AuthenTec AES2550/AES2810 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AES2550_H
#define __AES2550_H
/* Registers bits */
#define AES2550_REG80_MASTER_RESET (1 << 0)
#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1)
#define AES2550_REG80_LPO_START (1 << 2)
#define AES2550_REG80_HGC_ENABLE (1 << 3)
#define AES2550_REG80_SENSOR_MODE_OFS (4)
#define AES2550_REG80_AUTO_RESTART_FD (1 << 6)
#define AES2550_REG80_EXT_REG_ENABLE (1 << 7)
#define AES2550_REG81_CONT_SCAN (1 << 0)
#define AES2550_REG81_READ_REG (1 << 1)
#define AES2550_REG81_NSHOT (1 << 2)
#define AES2550_REG81_RUN_FD (1 << 3)
#define AES2550_REG81_READ_ID (1 << 4)
#define AES2550_REG81_RUN_CAL (1 << 5)
#define AES2550_REG81_RUN_TIMER (1 << 6)
#define AES2550_REG81_RUN_BIST (1 << 7)
#define AES2550_REG83_FINGER_PRESENT (1 << 7)
#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7)
#define AES2550_REG8F_EDATA_DISABLE (1 << 1)
#define AES2550_REG8F_AUTH_DISABLE (1 << 2)
#define AES2550_REG8F_EHISTO_DISABLE (1 << 3)
#define AES2550_REG8F_HISTO64 (1 << 4)
#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6)
#define AES2550_REG95_COL_SCANNED_OFS (0)
#define AES2550_REG95_EPIX_AVG_OFS (4)
#define AES2550_REGA8_DIG_BIT_DATA_OFS (0)
#define AES2550_REGA8_DIG_BIT_EN (1 << 4)
#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5)
#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6)
#define AES2550_REGAD_LPFD_AVG_OFS (0)
#define AES2550_REGAD_DETECT_FGROFF (1 << 4)
#define AES2550_REGAD_ADVRANGE_2V (1 << 6)
#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1)
#define AES2550_REGB1_ANALOG_RESET (1 << 2)
#define AES2550_REGB1_ANALOG_PD (1 << 3)
#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4)
#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5)
#define AES2550_REGB1_RESET_UHSM (1 << 6)
#define AES2550_REGB1_RESET_SENSOR (1 << 7)
#define AES2550_REGBD_LPO_IN_15_8_OFS (0)
#define AES2550_REGBE_LPO_IN_7_0_OFS (0)
#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0)
#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0)
#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0)
#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0)
#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2)
#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2)
#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2)
#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4)
#define AES2550_REGBF_QUADRATURE_MODE (1 << 5)
#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0)
#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1)
#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4)
#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0)
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
/* Commands */
enum aes2550_cmds {
AES2550_CMD_SET_IDLE_MODE = 0x00,
AES2550_CMD_RUN_FD = 0x01,
AES2550_CMD_GET_ENROLL_IMG = 0x02,
AES2550_CMD_CALIBRATE = 0x06,
AES2550_CMD_READ_CALIBRATION_DATA = 0x10,
AES2550_CMD_HEARTBEAT = 0x70,
};
/* Messages */
#define AES2550_STRIP_SIZE (0x31e + 3)
#define AES2550_HEARTBEAT_SIZE (4 + 3)
#define AES2550_EDATA_MAGIC 0xe0
#define AES2550_HEARTBEAT_MAGIC 0xdb
#define AES2550_EP_IN_BUF_SIZE 8192
#endif

111
libfprint/drivers/aes2660.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* AuthenTec AES2660 driver for libfprint
* Copyright (C) 2012 Vasily Khoruzhick
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aes2660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "aesx660.h"
#include "aes2660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 192
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
/* No scaling for AES2660 */
aesdev->init_seqs[0] = aes2660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes2660_init_1);
aesdev->init_seqs[1] = aes2660_init_2;
aesdev->init_seqs_len[1] = array_n_elements(aes2660_init_2);
aesdev->start_imaging_cmd = (unsigned char *)aes2660_start_imaging_cmd;
aesdev->start_imaging_cmd_len = sizeof(aes2660_start_imaging_cmd);
aesdev->frame_width = FRAME_WIDTH;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x2660 },
{ .vendor = 0x08ff, .product = 0x2680 },
{ .vendor = 0x08ff, .product = 0x2681 },
{ .vendor = 0x08ff, .product = 0x2682 },
{ .vendor = 0x08ff, .product = 0x2683 },
{ .vendor = 0x08ff, .product = 0x2684 },
{ .vendor = 0x08ff, .product = 0x2685 },
{ .vendor = 0x08ff, .product = 0x2686 },
{ .vendor = 0x08ff, .product = 0x2687 },
{ .vendor = 0x08ff, .product = 0x2688 },
{ .vendor = 0x08ff, .product = 0x2689 },
{ .vendor = 0x08ff, .product = 0x268a },
{ .vendor = 0x08ff, .product = 0x268b },
{ .vendor = 0x08ff, .product = 0x268c },
{ .vendor = 0x08ff, .product = 0x268d },
{ .vendor = 0x08ff, .product = 0x268e },
{ .vendor = 0x08ff, .product = 0x268f },
{ .vendor = 0x08ff, .product = 0x2691 },
{ 0, 0, 0, },
};
struct fp_img_driver aes2660_driver = {
.driver = {
.id = AES2660_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES2660",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,
.activate = aesX660_dev_activate,
.deactivate = aesX660_dev_deactivate,
};

1964
libfprint/drivers/aes2660.h Normal file

File diff suppressed because it is too large Load Diff

188
libfprint/drivers/aes3500.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* AuthenTec AES3500 driver for libfprint
*
* AES3500 is a press-typed sensor, which captures image in 128x128
* pixels.
*
* Thanks Rafael Toledo for the Windows driver and the help.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2011-2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "aes3500"
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aes3k.h"
#include "driver_ids.h"
#define DATA_BUFLEN 0x2089
/* image size = FRAME_WIDTH x FRAME_WIDTH */
#define FRAME_WIDTH 128
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
#define ENLARGE_FACTOR 2
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0x80, 0x00 },
{ 0, 0 },
/* scan reset */
{ 0x80, 0x02 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
/* disable register buffering */
{ 0x80, 0x04 },
{ 0, 0 },
{ 0x80, 0x00 },
{ 0, 0 },
{ 0x81, 0x00 },
{ 0, 0 },
/* windows driver reads registers now (81 02) */
{ 0x80, 0x00 },
{ 0x81, 0x00 },
/* set excitation bias current: 2mhz drive ring frequency,
* 4V drive ring voltage, 16.5mA excitation bias */
{ 0x82, 0x04 },
/* continuously sample drive ring for finger detection,
* 62.50ms debounce delay */
{ 0x83, 0x13 },
{ 0x84, 0x07 }, /* set calibration resistance to 12 kiloohms */
{ 0x85, 0x3d }, /* set calibration capacitance */
{ 0x86, 0x03 }, /* detect drive voltage */
{ 0x87, 0x01 }, /* set detection frequency to 125khz */
{ 0x88, 0x02 }, /* set column scan period */
{ 0x89, 0x02 }, /* set measure drive */
{ 0x8a, 0x33 }, /* set measure frequency and sense amplifier bias */
{ 0x8b, 0x33 }, /* set matrix pattern */
{ 0x8c, 0x0f }, /* set demodulation phase 1 */
{ 0x8d, 0x04 }, /* set demodulation phase 2 */
{ 0x8e, 0x23 }, /* set sensor gain */
{ 0x8f, 0x07 }, /* set image parameters */
{ 0x90, 0x00 }, /* carrier offset null */
{ 0x91, 0x1c }, /* set A/D reference high */
{ 0x92, 0x08 }, /* set A/D reference low */
{ 0x93, 0x00 }, /* set start row to 0 */
{ 0x94, 0x07 }, /* set end row */
{ 0x95, 0x00 }, /* set start column to 0 */
{ 0x96, 0x1f }, /* set end column */
{ 0x97, 0x04 }, /* data format and thresholds */
{ 0x98, 0x28 }, /* image data control */
{ 0x99, 0x00 }, /* disable general purpose outputs */
{ 0x9a, 0x0b }, /* set initial scan state */
{ 0x9b, 0x00 }, /* clear challenge word bits */
{ 0x9c, 0x00 }, /* clear challenge word bits */
{ 0x9d, 0x09 }, /* set some challenge word bits */
{ 0x9e, 0x53 }, /* clear challenge word bits */
{ 0x9f, 0x6b }, /* set some challenge word bits */
{ 0, 0 },
{ 0x80, 0x00 },
{ 0x81, 0x00 },
{ 0, 0 },
{ 0x81, 0x04 },
{ 0, 0 },
{ 0x81, 0x00 },
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
if (!aesdev)
return -ENOMEM;
if (r == 0)
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = dev->priv;
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5731 },
{ 0, 0, 0, },
};
struct fp_img_driver aes3500_driver = {
.driver = {
.id = AES3500_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES3500",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
.activate = aes3k_dev_activate,
.deactivate = aes3k_dev_deactivate,
};

169
libfprint/drivers/aes3k.c Normal file
View File

@@ -0,0 +1,169 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 "aes3k"
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aes3k.h"
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
static void do_capture(struct fp_img_dev *dev);
static void aes3k_assemble_image(unsigned char *input, size_t width, size_t height,
unsigned char *output)
{
size_t row, column;
for (column = 0; column < width; column++) {
for (row = 0; row < height; row += 2) {
output[width * row + column] = (*input & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct aes3k_dev *aesdev = dev->priv;
unsigned char *ptr = transfer->buffer;
struct fp_img *tmp;
struct fp_img *img;
int i;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
goto err;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto err;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto err;
}
fpi_imgdev_report_finger_status(dev, TRUE);
tmp = fpi_img_new(aesdev->frame_width * aesdev->frame_width);
tmp->width = aesdev->frame_width;
tmp->height = aesdev->frame_width;
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
for (i = 0; i < aesdev->frame_number; i++) {
fp_dbg("frame header byte %02x", *ptr);
ptr++;
aes3k_assemble_image(ptr, aesdev->frame_width, AES3K_FRAME_HEIGHT, tmp->data + (i * aesdev->frame_width * AES3K_FRAME_HEIGHT));
ptr += aesdev->frame_size;
}
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_im_resize(tmp, aesdev->enlarge_factor, aesdev->enlarge_factor);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */
fpi_imgdev_report_finger_status(dev, FALSE);
do_capture(dev);
err:
g_free(transfer->buffer);
aesdev->img_trf = NULL;
libusb_free_transfer(transfer);
}
static void do_capture(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = dev->priv;
unsigned char *data;
int r;
aesdev->img_trf = libusb_alloc_transfer(0);
if (!aesdev->img_trf) {
fpi_imgdev_session_error(dev, -EIO);
return;
}
data = g_malloc(aesdev->data_buflen);
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
aesdev->data_buflen, img_cb, dev, 0);
r = libusb_submit_transfer(aesdev->img_trf);
if (r < 0) {
g_free(data);
libusb_free_transfer(aesdev->img_trf);
aesdev->img_trf = NULL;
fpi_imgdev_session_error(dev, r);
}
}
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
fpi_imgdev_activate_complete(dev, result);
if (result == 0)
do_capture(dev);
}
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct aes3k_dev *aesdev = dev->priv;
aes_write_regv(dev, aesdev->init_reqs, aesdev->init_reqs_len, init_reqs_cb, NULL);
return 0;
}
void aes3k_dev_deactivate(struct fp_img_dev *dev)
{
struct aes3k_dev *aesdev = dev->priv;
/* FIXME: should wait for cancellation to complete before returning
* from deactivation, otherwise app may legally exit before we've
* cleaned up */
if (aesdev->img_trf)
libusb_cancel_transfer(aesdev->img_trf);
fpi_imgdev_deactivate_complete(dev);
}

58
libfprint/drivers/aes3k.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* AuthenTec AES3500/AES4000 common routines
*
* The AES3500 and AES4000 sensors are press-typed, and could capture
* fingerprint images in 128x128 and 96x96 pixels respectively. They
* share a same communication interface: a number of frames are
* transferred and captured, from which a final image could be
* assembled. Each frame has fixed height of 16 pixels.
*
* As the imaging area is a bit small, only a part of finger could be
* captured, the detected minutiae are not so many that the NBIS
* matching works not so good. The verification rate is very low at the
* moment.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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 __AES3K_H
#define __AES3K_H
#define AES3K_FRAME_HEIGHT 16
struct aes3k_dev {
struct libusb_transfer *img_trf;
size_t frame_width; /* image size = frame_width x frame_width */
size_t frame_size; /* 4 bits/pixel: frame_width x AES3K_FRAME_HEIGHT / 2 */
size_t frame_number; /* number of frames */
size_t enlarge_factor;
size_t data_buflen; /* buffer length of usb bulk transfer */
struct aes_regwrite *init_reqs; /* initial values sent to device */
size_t init_reqs_len;
};
int aes3k_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
void aes3k_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -1,5 +1,12 @@
/*
* AuthenTec AES4000 driver for libfprint
*
* AES4000 is a press-typed sensor, which captures image in 96x96
* pixels.
*
* This work is derived from Daniel Drake's AES4000 driver.
*
* Copyright (C) 2013 Juvenn Woo <machese@gmail.com>
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* This library is free software; you can redistribute it and/or
@@ -27,22 +34,19 @@
#include <aeslib.h>
#include <fp_internal.h>
#define CTRL_TIMEOUT 1000
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define DATA_BUFLEN 0x1259
#define NR_SUBARRAYS 6
#define SUBARRAY_LEN 768
#include "aes3k.h"
#include "driver_ids.h"
#define IMG_HEIGHT 96
#define IMG_WIDTH 96
#define ENLARGE_FACTOR 3
#define DATA_BUFLEN 0x1259
struct aes4k_dev {
struct libusb_transfer *img_trf;
};
/* image size = FRAME_WIDTH x FRAME_WIDTH */
#define FRAME_WIDTH 96
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
#define ENLARGE_FACTOR 3
static const struct aes_regwrite init_reqs[] = {
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
@@ -117,119 +121,28 @@ static const struct aes_regwrite init_reqs[] = {
{ 0x81, 0x00 },
};
static void do_capture(struct fp_img_dev *dev);
static void img_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct aes4k_dev *aesdev = dev->priv;
unsigned char *ptr = transfer->buffer;
struct fp_img *tmp;
struct fp_img *img;
int i;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
goto err;
} else if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_imgdev_session_error(dev, -EIO);
goto err;
} else if (transfer->length != transfer->actual_length) {
fpi_imgdev_session_error(dev, -EPROTO);
goto err;
}
fpi_imgdev_report_finger_status(dev, TRUE);
tmp = fpi_img_new(IMG_WIDTH * IMG_HEIGHT);
tmp->width = IMG_WIDTH;
tmp->height = IMG_HEIGHT;
tmp->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED;
for (i = 0; i < NR_SUBARRAYS; i++) {
fp_dbg("subarray header byte %02x", *ptr);
ptr++;
aes_assemble_image(ptr, 96, 16, tmp->data + (i * 96 * 16));
ptr += SUBARRAY_LEN;
}
/* FIXME: this is an ugly hack to make the image big enough for NBIS
* to process reliably */
img = fpi_im_resize(tmp, ENLARGE_FACTOR);
fp_img_free(tmp);
fpi_imgdev_image_captured(dev, img);
/* FIXME: rather than assuming finger has gone, we should poll regs until
* it really has, then restart the capture */
fpi_imgdev_report_finger_status(dev, FALSE);
do_capture(dev);
err:
g_free(transfer->buffer);
aesdev->img_trf = NULL;
libusb_free_transfer(transfer);
}
static void do_capture(struct fp_img_dev *dev)
{
struct aes4k_dev *aesdev = dev->priv;
unsigned char *data;
int r;
aesdev->img_trf = libusb_alloc_transfer(0);
if (!aesdev->img_trf) {
fpi_imgdev_session_error(dev, -EIO);
return;
}
data = g_malloc(DATA_BUFLEN);
libusb_fill_bulk_transfer(aesdev->img_trf, dev->udev, EP_IN, data,
DATA_BUFLEN, img_cb, dev, 0);
r = libusb_submit_transfer(aesdev->img_trf);
if (r < 0) {
g_free(data);
libusb_free_transfer(aesdev->img_trf);
aesdev->img_trf = NULL;
fpi_imgdev_session_error(dev, r);
}
}
static void init_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
{
fpi_imgdev_activate_complete(dev, result);
if (result == 0)
do_capture(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
aes_write_regv(dev, init_reqs, G_N_ELEMENTS(init_reqs), init_reqs_cb, NULL);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes4k_dev *aesdev = dev->priv;
/* FIXME: should wait for cancellation to complete before returning
* from deactivation, otherwise app may legally exit before we've
* cleaned up */
if (aesdev->img_trf)
libusb_cancel_transfer(aesdev->img_trf);
fpi_imgdev_deactivate_complete(dev);
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
struct aes3k_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
fp_err("could not claim interface 0");
dev->priv = g_malloc0(sizeof(struct aes4k_dev));
aesdev = dev->priv = g_malloc0(sizeof(struct aes3k_dev));
if (!aesdev)
return -ENOMEM;
if (r == 0)
aesdev->data_buflen = DATA_BUFLEN;
aesdev->frame_width = FRAME_WIDTH;
aesdev->frame_size = FRAME_SIZE;
aesdev->frame_number = FRAME_NUMBER;
aesdev->enlarge_factor = ENLARGE_FACTOR;
aesdev->init_reqs = init_reqs;
aesdev->init_reqs_len = G_N_ELEMENTS(init_reqs);
fpi_imgdev_open_complete(dev, 0);
return r;
@@ -237,11 +150,13 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_deinit(struct fp_img_dev *dev)
{
g_free(dev->priv);
struct aes3k_dev *aesdev = dev->priv;
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5501 },
{ 0, 0, 0, },
@@ -249,22 +164,22 @@ static const struct usb_id id_table[] = {
struct fp_img_driver aes4000_driver = {
.driver = {
.id = 3,
.id = AES4000_ID,
.name = FP_COMPONENT,
.full_name = "AuthenTec AES4000",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = 0,
.img_height = IMG_HEIGHT * ENLARGE_FACTOR,
.img_width = IMG_WIDTH * ENLARGE_FACTOR,
.img_height = FRAME_WIDTH * ENLARGE_FACTOR,
.img_width = FRAME_WIDTH * ENLARGE_FACTOR,
/* temporarily lowered until image quality improves */
.bz3_threshold = 9,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
.activate = aes3k_dev_activate,
.deactivate = aes3k_dev_deactivate,
};

632
libfprint/drivers/aesx660.c Normal file
View File

@@ -0,0 +1,632 @@
/*
* AuthenTec AES1660/AES2660 common routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2007 Cyrille Bagard
* Copyright (C) 2007-2008,2012 Vasily Khoruzhick
*
* Based on AES2550 driver
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "aesX660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "aesx660.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
#define FRAME_HEIGHT 8
#define min(a, b) (((a) < (b)) ? (a) : (b))
static void aesX660_send_cmd_timeout(struct fpi_ssm *ssm, const unsigned char *cmd,
size_t cmd_len, libusb_transfer_cb_fn callback, int timeout)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT,
(unsigned char *)cmd, cmd_len,
callback, ssm, timeout);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("failed to submit transfer\n");
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
static void aesX660_send_cmd(struct fpi_ssm *ssm, const unsigned char *cmd,
size_t cmd_len, libusb_transfer_cb_fn callback)
{
return aesX660_send_cmd_timeout(ssm, cmd, cmd_len, callback, BULK_TIMEOUT);
}
static void aesX660_read_response(struct fpi_ssm *ssm, size_t buf_len,
libusb_transfer_cb_fn callback)
{
struct fp_img_dev *dev = ssm->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
data = g_malloc(buf_len);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
data, buf_len,
callback, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_dbg("Failed to submit rx transfer: %d\n", r);
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
static void aesX660_send_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fp_dbg("tx transfer status: %d, actual_len: %.4x\n",
transfer->status, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void aesX660_read_calibrate_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* Calibrate response was read correctly? */
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE) {
fp_dbg("Bogus calibrate response: %.2x\n", data[0]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
fpi_ssm_next_state(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
/****** FINGER PRESENCE DETECTION ******/
enum finger_det_states {
FINGER_DET_SEND_LED_CMD,
FINGER_DET_SEND_FD_CMD,
FINGER_DET_READ_FD_DATA,
FINGER_DET_SET_IDLE,
FINGER_DET_NUM_STATES,
};
static void finger_det_read_fd_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
aesdev->fd_data_transfer = NULL;
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
fp_dbg("Cancelling transfer...\n");
fpi_ssm_next_state(ssm);
goto out;
}
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("Failed to read FD data\n");
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE) {
fp_dbg("Bogus FD response: %.2x\n", data[0]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
if (data[AESX660_FINGER_PRESENT_OFFSET] == AESX660_FINGER_PRESENT || aesdev->deactivating) {
/* Finger present or we're deactivating... */
fpi_ssm_next_state(ssm);
} else {
fp_dbg("Wait for finger returned %.2x as result\n",
data[AESX660_FINGER_PRESENT_OFFSET]);
fpi_ssm_jump_to_state(ssm, FINGER_DET_SEND_FD_CMD);
}
out:
g_free(data);
libusb_free_transfer(transfer);
}
static void finger_det_set_idle_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void finger_det_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
int err = ssm->error;
fp_dbg("Finger detection completed");
fpi_imgdev_report_finger_status(dev, TRUE);
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else {
fpi_imgdev_report_finger_status(dev, TRUE);
start_capture(dev);
}
}
static void finger_det_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case FINGER_DET_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_blink_cmd, sizeof(led_blink_cmd),
aesX660_send_cmd_cb);
break;
case FINGER_DET_SEND_FD_CMD:
aesX660_send_cmd_timeout(ssm, wait_for_finger_cmd, sizeof(wait_for_finger_cmd),
aesX660_send_cmd_cb, 0);
break;
case FINGER_DET_READ_FD_DATA:
/* Should return 4 byte of response */
aesX660_read_response(ssm, 4, finger_det_read_fd_data_cb);
break;
case FINGER_DET_SET_IDLE:
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
finger_det_set_idle_cmd_cb);
break;
}
}
static void start_finger_detection(struct fp_img_dev *dev)
{
struct fpi_ssm *ssm;
struct aesX660_dev *aesdev = dev->priv;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, finger_det_run_state, FINGER_DET_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, finger_det_sm_complete);
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_SEND_LED_CMD,
CAPTURE_SEND_CAPTURE_CMD,
CAPTURE_READ_STRIPE_DATA,
CAPTURE_SET_IDLE,
CAPTURE_NUM_STATES,
};
/* Returns number of processed bytes */
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
{
struct aes_stripe *stripe;
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
stripe = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2 + sizeof(struct aes_stripe)); /* 4 bpp */
stripdata = stripe->data;
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
data[AESX660_LAST_FRAME_OFFSET]);
stripe->delta_x = (int8_t)data[AESX660_FRAME_DELTA_X_OFFSET];
stripe->delta_y = -(int8_t)data[AESX660_FRAME_DELTA_Y_OFFSET];
fp_dbg("Offset to previous frame: %d %d", stripe->delta_x, stripe->delta_y);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->frame_width * FRAME_HEIGHT / 2);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
return (data[AESX660_LAST_FRAME_OFFSET] & AESX660_LAST_FRAME_BIT);
} else {
return 0;
}
}
static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT, aesdev->frame_width + aesdev->frame_width / 2);
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
int finger_missing = 0;
size_t copied, actual_len = transfer->actual_length;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
fp_dbg("Got %d bytes of data", actual_len);
do {
copied = min(aesdev->buffer_max - aesdev->buffer_size, actual_len);
memcpy(aesdev->buffer + aesdev->buffer_size,
data,
copied);
actual_len -= copied;
data += copied;
aesdev->buffer_size += copied;
fp_dbg("Copied %.4x bytes into internal buffer",
copied);
if (aesdev->buffer_size == aesdev->buffer_max) {
if (aesdev->buffer_max == AESX660_HEADER_SIZE) {
aesdev->buffer_max = aesdev->buffer[AESX660_RESPONSE_SIZE_LSB_OFFSET] +
(aesdev->buffer[AESX660_RESPONSE_SIZE_MSB_OFFSET] << 8) + AESX660_HEADER_SIZE;
fp_dbg("Got frame, type %.2x size %.4x",
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
aesdev->buffer_max);
continue;
} else {
finger_missing |= process_stripe_data(ssm, aesdev->buffer);
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesdev->buffer_size = 0;
}
}
} while (actual_len);
fp_dbg("finger %s\n", finger_missing ? "missing" : "present");
if (finger_missing) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_STRIPE_DATA);
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
switch (ssm->cur_state) {
case CAPTURE_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_solid_cmd, sizeof(led_solid_cmd),
aesX660_send_cmd_cb);
break;
case CAPTURE_SEND_CAPTURE_CMD:
aesdev->buffer_size = 0;
aesdev->buffer_max = AESX660_HEADER_SIZE;
aesX660_send_cmd(ssm, aesdev->start_imaging_cmd,
aesdev->start_imaging_cmd_len,
aesX660_send_cmd_cb);
break;
case CAPTURE_READ_STRIPE_DATA:
aesX660_read_response(ssm, AESX660_BULK_TRANSFER_SIZE,
capture_read_stripe_data_cb);
break;
case CAPTURE_SET_IDLE:
fp_dbg("Got %d frames\n", aesdev->strips_len);
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
capture_set_idle_cmd_cb);
break;
}
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
int err = ssm->error;
fp_dbg("Capture completed");
fpi_ssm_free(ssm);
if (aesdev->deactivating)
complete_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else
start_finger_detection(dev);
}
static void start_capture(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
enum activate_states {
ACTIVATE_SET_IDLE,
ACTIVATE_SEND_READ_ID_CMD,
ACTIVATE_READ_ID,
ACTIVATE_SEND_CALIBRATE_CMD,
ACTIVATE_READ_CALIBRATE_DATA,
ACTIVATE_SEND_INIT_CMD,
ACTIVATE_READ_INIT_RESPONSE,
ACTIVATE_NUM_STATES,
};
static void activate_read_id_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_id cmd failed\n");
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] == 0x07) {
fp_dbg("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x\n",
data[4], data[3], data[5], data[6], data[7]);
} else {
fp_dbg("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
switch (aesdev->init_seq_idx) {
case 0:
aesdev->init_seq = aesdev->init_seqs[0];
aesdev->init_seq_len = aesdev->init_seqs_len[0];
aesdev->init_seq_idx = 1;
aesdev->init_cmd_idx = 0;
/* Do calibration only after 1st init sequence */
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
break;
case 1:
aesdev->init_seq = aesdev->init_seqs[1];
aesdev->init_seq_len = aesdev->init_seqs_len[1];
aesdev->init_seq_idx = 2;
aesdev->init_cmd_idx = 0;
fpi_ssm_next_state(ssm);
break;
default:
fp_dbg("Failed to init device! init status: %.2x\n", data[7]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
break;
}
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_read_init_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
unsigned char *data = transfer->buffer;
fp_dbg("read_init_cb\n");
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
(transfer->length != transfer->actual_length)) {
fp_dbg("read_init transfer status: %d, actual_len: %d\n", transfer->status, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* ID was read correctly */
if (data[0] != 0x42 || data[3] != 0x01) {
fp_dbg("Bogus read init response: %.2x %.2x\n", data[0],
data[3]);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
aesdev->init_cmd_idx++;
if (aesdev->init_cmd_idx == aesdev->init_seq_len) {
if (aesdev->init_seq_idx < 2)
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_READ_ID_CMD);
else
fpi_ssm_mark_completed(ssm);
goto out;
}
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
switch (ssm->cur_state) {
case ACTIVATE_SET_IDLE:
aesdev->init_seq_idx = 0;
fp_dbg("Activate: set idle\n");
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_SEND_READ_ID_CMD:
fp_dbg("Activate: read ID\n");
aesX660_send_cmd(ssm, read_id_cmd, sizeof(read_id_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_ID:
/* Should return 8-byte response */
aesX660_read_response(ssm, 8, activate_read_id_cb);
break;
case ACTIVATE_SEND_INIT_CMD:
fp_dbg("Activate: send init seq #%d cmd #%d\n",
aesdev->init_seq_idx,
aesdev->init_cmd_idx);
aesX660_send_cmd(ssm,
aesdev->init_seq[aesdev->init_cmd_idx].cmd,
aesdev->init_seq[aesdev->init_cmd_idx].len,
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_INIT_RESPONSE:
fp_dbg("Activate: read init response\n");
/* Should return 4-byte response */
aesX660_read_response(ssm, 4, activate_read_init_cb);
break;
case ACTIVATE_SEND_CALIBRATE_CMD:
aesX660_send_cmd(ssm, calibrate_cmd, sizeof(calibrate_cmd),
aesX660_send_cmd_cb);
break;
case ACTIVATE_READ_CALIBRATE_DATA:
/* Should return 4-byte response */
aesX660_read_response(ssm, 4, aesX660_read_calibrate_data_cb);
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int err = ssm->error;
fp_dbg("status %d", err);
fpi_imgdev_activate_complete(dev, err);
fpi_ssm_free(ssm);
if (!err)
start_finger_detection(dev);
}
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
void aesX660_dev_deactivate(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
if (aesdev->fd_data_transfer)
libusb_cancel_transfer(aesdev->fd_data_transfer);
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
fp_dbg("");
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
fpi_imgdev_deactivate_complete(dev);
}

120
libfprint/drivers/aesx660.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* AuthenTec AES1660/AES2660 common definitions
* Copyright (c) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 __AESX660_H
#define __AESX660_H
#define AESX660_HEADER_SIZE 3
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
#define AESX660_RESPONSE_SIZE_LSB_OFFSET 0x01
#define AESX660_RESPONSE_SIZE_MSB_OFFSET 0x02
#define AESX660_CALIBRATE_RESPONSE 0x06
#define AESX660_FINGER_DET_RESPONSE 0x40
#define AESX660_FINGER_PRESENT_OFFSET 0x03
#define AESX660_FINGER_PRESENT 0x01
#define AESX660_IMAGE_OK_OFFSET 0x03
#define AESX660_IMAGE_OK 0x0d
#define AESX660_LAST_FRAME_OFFSET 0x04
#define AESX660_LAST_FRAME_BIT 0x01
#define AESX660_FRAME_DELTA_X_OFFSET 16
#define AESX660_FRAME_DELTA_Y_OFFSET 17
#define AESX660_IMAGE_OFFSET 43
#define AESX660_BULK_TRANSFER_SIZE 4096
struct aesX660_dev {
GSList *strips;
size_t strips_len;
gboolean deactivating;
struct aesX660_cmd *init_seq;
size_t init_seq_len;
unsigned int init_cmd_idx;
unsigned int init_seq_idx;
struct libusb_transfer *fd_data_transfer;
unsigned char *buffer;
size_t buffer_size;
size_t buffer_max;
/* Device-specific stuff */
int h_scale_factor;
struct aesX660_cmd *init_seqs[2];
size_t init_seqs_len[2];
unsigned char *start_imaging_cmd;
size_t start_imaging_cmd_len;
unsigned int frame_width;
};
struct aesX660_cmd {
const unsigned char *cmd;
size_t len;
};
/* 0x77 cmd seems to control LED, this sequence
* makes LED blink
*/
static const unsigned char led_blink_cmd[] = {
0x77, 0x18, 0x00,
0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x01, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf3,
0x01, 0x00, 0x7f
};
/* This sequence makes LED light solid
*/
static const unsigned char led_solid_cmd[] = {
0x77, 0x18, 0x00, 0x00, 0x3f, 0x00, 0xff, 0x00,
0x01, 0x01, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f
};
static const unsigned char wait_for_finger_cmd[] = {
0x20,
0x40, 0x04, 0x00, 0x02, 0x1e, 0x00, 0x32
};
/* 0x40 cmd response
*
static const unsigned char pkt1371[] = {
0x40, 0x01, 0x00, 0x01
};
*/
static const unsigned char set_idle_cmd[] = {
0x0d, /* Reset or "set idle"? */
};
static const unsigned char read_id_cmd[] = {
0x44, 0x02, 0x00, 0x08, 0x00, /* Max transfer size is 8 */
0x07, /* Read ID? */
};
static const unsigned char calibrate_cmd[] = {
0x44, 0x02, 0x00, 0x04, 0x00,
0x06,
};
int aesX660_dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state);
void aesX660_dev_deactivate(struct fp_img_dev *dev);
#endif

View File

@@ -0,0 +1,45 @@
/*
* Driver IDs
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 __DRIVER_IDS
#define __DRIVER_IDS
enum {
UPEKTS_ID = 1,
URU4000_ID = 2,
AES4000_ID = 3,
AES2501_ID = 4,
UPEKTC_ID = 5,
AES1610_ID = 6,
FDU2000_ID = 7,
VCOM5S_ID = 8,
UPEKSONLY_ID = 9,
VFS101_ID = 10,
VFS301_ID = 11,
AES2550_ID = 12,
UPEKE2_ID = 13,
AES1660_ID = 14,
AES2660_ID = 15,
AES3500_ID = 16,
UPEKTC_IMG_ID = 17,
ETES603_ID = 18,
VFS5011_ID = 19,
};
#endif

1513
libfprint/drivers/etes603.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,8 @@
#define FP_COMPONENT "fdu2000"
#include <fp_internal.h>
#include "driver_ids.h"
#ifndef HAVE_MEMMEM
gpointer
memmem(const gpointer haystack, size_t haystack_len, const gpointer needle, size_t needle_len) {
@@ -305,7 +307,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver fdu2000_driver = {
.driver = {
.id = 7,
.id = FDU2000_ID,
.name = FP_COMPONENT,
.full_name = "Secugen FDU 2000",
.id_table = id_table,

View File

@@ -37,6 +37,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define TIMEOUT 5000
@@ -44,6 +46,10 @@
#define MSG_READ_BUF_SIZE 0x40
#define MAX_DATA_IN_READ_BUF (MSG_READ_BUF_SIZE - 9)
enum {
UPEKE2_2016,
};
struct upeke2_dev {
gboolean enroll_passed;
gboolean first_verify_iteration;
@@ -738,14 +744,14 @@ static void initsm_run_state(struct fpi_ssm *ssm)
break;
case SEND28_51: ;
unsigned char dummy28_51[] = { 0x04, 0x0a, 0x00, 0x00, 0x00 };
initsm_send_msg28_handler(ssm, 0x51, &dummy28_51, 5);
initsm_send_msg28_handler(ssm, 0x51, dummy28_51, 5);
break;
case READ28_51:
initsm_read_msg_handler(ssm, read28_51_cb);
break;
case SEND28_07: ;
unsigned char dummy28_07[] = { 0x04, 0x20, 0x00, 0x00, 0x00 };
initsm_send_msg28_handler(ssm, 0x07, &dummy28_07, 5);
initsm_send_msg28_handler(ssm, 0x07, dummy28_07, 5);
break;
case READ28_07:
initsm_read_msg_handler(ssm, read28_07_cb);
@@ -846,8 +852,7 @@ static struct fpi_ssm *deinitsm_new(struct fp_dev *dev)
static int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
{
/* Revision 2 is what we're interested in */
if (dsc->bcdDevice == 2)
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
return 1;
return 0;
@@ -1067,6 +1072,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct fp_print_data *fdata = NULL;
struct fp_print_data_item *item = NULL;
int result = -EPROTO;
if (data_len < sizeof(scan_comp)) {
@@ -1075,9 +1081,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
fp_err("unrecognised data prefix %x %x %x %x %x",
data[0], data[1], data[2], data[3], data[4]);
} else {
fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
memcpy(fdata->data, data + sizeof(scan_comp),
fdata = fpi_print_data_new(dev);
item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
memcpy(item->data, data + sizeof(scan_comp),
data_len - sizeof(scan_comp));
fdata->prints = g_slist_prepend(fdata->prints, item);
result = FP_ENROLL_COMPLETE;
}
@@ -1239,12 +1247,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
break;
case VERIFY_INIT: ;
struct fp_print_data *print = dev->verify_data;
size_t data_len = sizeof(verify_hdr) + print->length;
struct fp_print_data_item *item = print->prints->data;
size_t data_len = sizeof(verify_hdr) + item->length;
unsigned char *data = g_malloc(data_len);
struct libusb_transfer *transfer;
memcpy(data, verify_hdr, sizeof(verify_hdr));
memcpy(data + sizeof(verify_hdr), print->data, print->length);
memcpy(data + sizeof(verify_hdr), item->data, item->length);
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
verify_init_2803_cb, ssm);
g_free(data);
@@ -1451,12 +1460,12 @@ static int verify_stop(struct fp_dev *dev, gboolean iterating)
}
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016 },
{ .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKE2_2016 },
{ 0, 0, 0, }, /* terminating entry */
};
struct fp_driver upeke2_driver = {
.id = 1,
.id = UPEKE2_ID,
.name = FP_COMPONENT,
.full_name = "UPEK Eikon 2",
.id_table = id_table,

View File

@@ -2,6 +2,9 @@
* UPEK TouchStrip Sensor-Only driver for libfprint
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
*
* TCS4C (USB ID 147e:1000) support:
* Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
*
* 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
@@ -27,10 +30,18 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_TIMEOUT 1000
#define IMG_WIDTH 288
#define NUM_BULK_TRANSFERS 24
#define MAX_ROWS 700
#define MIN_ROWS 64
enum {
UPEKSONLY_2016,
UPEKSONLY_1000,
};
struct img_transfer_data {
int idx;
@@ -60,6 +71,8 @@ struct sonly_dev {
gboolean deactivating;
uint8_t read_reg_result;
int dev_model;
struct fpi_ssm *loopsm;
struct libusb_transfer *img_transfer[NUM_BULK_TRANSFERS];
struct img_transfer_data *img_transfer_data;
@@ -168,8 +181,12 @@ static void handoff_img(struct fp_img_dev *dev)
fp_dbg("%d rows", sdev->num_rows);
img->height = sdev->num_rows;
/* The scans from this device are rolled right by two colums
* It feels a lot smarter to correct here than mess with it at
* read time*/
do {
memcpy(img->data + offset, elem->data, IMG_WIDTH);
memcpy(img->data + offset, elem->data + 2, IMG_WIDTH - 2);
memcpy(img->data + offset + IMG_WIDTH - 2, elem->data, 2);
g_free(elem->data);
offset += IMG_WIDTH;
} while ((elem = g_slist_next(elem)) != NULL);
@@ -214,13 +231,22 @@ static void row_complete(struct fp_img_dev *dev)
int total;
compute_rows(lastrow, sdev->rowbuf, &diff, &total);
if (total < 52000) {
sdev->num_blank = 0;
} else {
sdev->num_blank++;
if (sdev->num_blank > 500) {
/* Don't consider the scan complete unless theres at least
* MIN_ROWS recorded or very long blank read occurred.
*
* Typical problem spot: one brief touch before starting the
* actual scan. Happens most commonly if scan is started
* from before the first joint resulting in a gap after the inital touch.
*/
if ((sdev->num_blank > 500)
&& ((sdev->num_rows > MIN_ROWS) || (sdev->num_blank > 5000))) {
sdev->finger_removed = 1;
fp_dbg("detected finger removal");
fp_dbg("detected finger removal. Blank rows: %d, Full rows: %d", sdev->num_blank, sdev->num_rows);
handoff_img(dev);
return;
}
@@ -248,14 +274,14 @@ static void add_to_rowbuf(struct fp_img_dev *dev, unsigned char *data, int size)
sdev->rowbuf_offset += size;
if (sdev->rowbuf_offset >= IMG_WIDTH)
row_complete(dev);
}
static void start_new_row(struct sonly_dev *sdev, unsigned char *data, int size)
{
if (!sdev->rowbuf)
sdev->rowbuf = g_malloc(IMG_WIDTH);
memcpy(sdev->rowbuf + IMG_WIDTH - 2, data, 2);
memcpy(sdev->rowbuf, data + 2, size - 2);
memcpy(sdev->rowbuf, data, size);
sdev->rowbuf_offset = size;
}
@@ -282,11 +308,56 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data)
int for_rowbuf;
int next_row_addr;
int diff;
unsigned char dummy_data[62];
/* Init dummy data to something neutral */
memset (dummy_data, 204, 62);
data += 2; /* skip sequence number */
if (seqnum != sdev->last_seqnum + 1) {
if (seqnum != 0 && sdev->last_seqnum != 16383)
fp_warn("lost some data");
if (seqnum != 0 && sdev->last_seqnum != 16383) {
int missing_data = seqnum - sdev->last_seqnum;
int i;
fp_warn("lost %d packets of data between %d and %d", missing_data, sdev->last_seqnum, seqnum );
/* Minimize distortions for readers that lose a lot of packets */
for (i =1; i < missing_data; i++) {
abs_base_addr = (sdev->last_seqnum + 1) * 62;
/* If possible take the replacement data from last row */
if (sdev->num_rows > 1) {
int row_left = IMG_WIDTH - sdev->rowbuf_offset;
unsigned char *last_row = g_slist_nth_data (sdev->rows, 0);
if (row_left >= 62) {
memcpy(dummy_data, last_row + sdev->rowbuf_offset, 62);
} else {
memcpy(dummy_data, last_row + sdev->rowbuf_offset, row_left);
memcpy(dummy_data + row_left, last_row , 62 - row_left);
}
}
fp_warn("adding dummy input for %d, i=%d", sdev->last_seqnum + i, i);
for_rowbuf = rowbuf_remaining(sdev);
if (for_rowbuf != -1) {
add_to_rowbuf(dev, dummy_data, for_rowbuf);
/* row boundary */
if (for_rowbuf < 62) {
start_new_row(sdev, dummy_data + for_rowbuf, 62 - for_rowbuf);
}
} else if (abs_base_addr % IMG_WIDTH == 0) {
start_new_row(sdev, dummy_data, 62);
} else {
/* does the data in the packet reside on a row boundary?
* if so capture it */
next_row_addr = ((abs_base_addr / IMG_WIDTH) + 1) * IMG_WIDTH;
diff = next_row_addr - abs_base_addr;
if (diff < 62)
start_new_row(sdev, dummy_data + diff, 62 - diff);
}
sdev->last_seqnum = sdev->last_seqnum + 1;
}
}
}
if (seqnum <= sdev->last_seqnum) {
fp_dbg("detected wraparound");
@@ -302,7 +373,10 @@ static void handle_packet(struct fp_img_dev *dev, unsigned char *data)
for_rowbuf = rowbuf_remaining(sdev);
if (for_rowbuf != -1) {
add_to_rowbuf(dev, data, for_rowbuf);
/* FIXME: we drop a row here */
/*row boundary*/
if (for_rowbuf < 62) {
start_new_row(sdev, data + for_rowbuf, 62 - for_rowbuf);
}
return;
}
@@ -395,7 +469,6 @@ static void write_regs_finished(struct write_regs_data *wrdata, int result)
static void write_regs_iterate(struct write_regs_data *wrdata)
{
struct fpi_ssm *ssm;
struct libusb_control_setup *setup;
const struct sonly_regwrite *regwrite;
int r;
@@ -406,7 +479,6 @@ static void write_regs_iterate(struct write_regs_data *wrdata)
}
regwrite = &wrdata->regs[wrdata->regs_written];
ssm = wrdata->ssm;
fp_dbg("set %02x=%02x", regwrite->reg, regwrite->value);
setup = libusb_control_transfer_get_setup(wrdata->transfer);
@@ -591,22 +663,42 @@ static void sm_await_intr(struct fpi_ssm *ssm)
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_writev_1[] = {
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
{ 0x0a, 0x00 }, { 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x3b },
{ 0x00, 0x67 }, { 0x00, 0x67 },
};
static const struct sonly_regwrite awfsm_writev_2[] = {
static const struct sonly_regwrite awfsm_1000_writev_1[] = {
/* Initialize sensor settings */
{ 0x0a, 0x00 }, { 0x09, 0x20 }, { 0x03, 0x37 }, { 0x00, 0x5f },
{ 0x01, 0x6e }, { 0x01, 0xee }, { 0x0c, 0x13 }, { 0x0d, 0x0d },
{ 0x0e, 0x0e }, { 0x0f, 0x0d },
{ 0x13, 0x05 }, { 0x13, 0x45 },
/* Initialize finger detection registers (not enabling yet) */
{ 0x30, 0xe0 }, { 0x15, 0x26 },
{ 0x12, 0x01 }, { 0x20, 0x01 }, { 0x07, 0x10 },
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
};
static const struct sonly_regwrite awfsm_writev_3[] = {
static const struct sonly_regwrite awfsm_1000_writev_2[] = {
/* Enable finger detection */
{ 0x30, 0xe1 }, { 0x15, 0x06 }, { 0x15, 0x86 },
};
static const struct sonly_regwrite awfsm_2016_writev_3[] = {
{ 0x13, 0x45 }, { 0x30, 0xe0 }, { 0x12, 0x01 }, { 0x20, 0x01 },
{ 0x09, 0x20 }, { 0x0a, 0x00 }, { 0x30, 0xe0 }, { 0x20, 0x01 },
};
static const struct sonly_regwrite awfsm_writev_4[] = {
static const struct sonly_regwrite awfsm_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
@@ -616,91 +708,150 @@ static const struct sonly_regwrite awfsm_writev_4[] = {
{ 0x15, 0x84 },
};
enum awfsm_states {
AWFSM_WRITEV_1,
AWFSM_READ_01,
AWFSM_WRITE_01,
AWFSM_WRITEV_2,
AWFSM_READ_13,
AWFSM_WRITE_13,
AWFSM_WRITEV_3,
AWFSM_READ_07,
AWFSM_WRITE_07,
AWFSM_WRITEV_4,
AWFSM_NUM_STATES,
enum awfsm_2016_states {
AWFSM_2016_WRITEV_1,
AWFSM_2016_READ_01,
AWFSM_2016_WRITE_01,
AWFSM_2016_WRITEV_2,
AWFSM_2016_READ_13,
AWFSM_2016_WRITE_13,
AWFSM_2016_WRITEV_3,
AWFSM_2016_READ_07,
AWFSM_2016_WRITE_07,
AWFSM_2016_WRITEV_4,
AWFSM_2016_NUM_STATES,
};
static void awfsm_run_state(struct fpi_ssm *ssm)
enum awfsm_1000_states {
AWFSM_1000_WRITEV_1,
AWFSM_1000_WRITEV_2,
AWFSM_1000_NUM_STATES,
};
static void awfsm_2016_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case AWFSM_WRITEV_1:
sm_write_regs(ssm, awfsm_writev_1, G_N_ELEMENTS(awfsm_writev_1));
case AWFSM_2016_WRITEV_1:
sm_write_regs(ssm, awfsm_2016_writev_1, G_N_ELEMENTS(awfsm_2016_writev_1));
break;
case AWFSM_READ_01:
case AWFSM_2016_READ_01:
sm_read_reg(ssm, 0x01);
break;
case AWFSM_WRITE_01:
case AWFSM_2016_WRITE_01:
if (sdev->read_reg_result != 0xc6)
sm_write_reg(ssm, 0x01, 0x46);
else
sm_write_reg(ssm, 0x01, 0xc6);
break;
case AWFSM_WRITEV_2:
sm_write_regs(ssm, awfsm_writev_2, G_N_ELEMENTS(awfsm_writev_2));
case AWFSM_2016_WRITEV_2:
sm_write_regs(ssm, awfsm_2016_writev_2, G_N_ELEMENTS(awfsm_2016_writev_2));
break;
case AWFSM_READ_13:
case AWFSM_2016_READ_13:
sm_read_reg(ssm, 0x13);
break;
case AWFSM_WRITE_13:
case AWFSM_2016_WRITE_13:
if (sdev->read_reg_result != 0x45)
sm_write_reg(ssm, 0x13, 0x05);
else
sm_write_reg(ssm, 0x13, 0x45);
break;
case AWFSM_WRITEV_3:
sm_write_regs(ssm, awfsm_writev_3, G_N_ELEMENTS(awfsm_writev_3));
case AWFSM_2016_WRITEV_3:
sm_write_regs(ssm, awfsm_2016_writev_3, G_N_ELEMENTS(awfsm_2016_writev_3));
break;
case AWFSM_READ_07:
case AWFSM_2016_READ_07:
sm_read_reg(ssm, 0x07);
break;
case AWFSM_WRITE_07:
case AWFSM_2016_WRITE_07:
if (sdev->read_reg_result != 0x10 && sdev->read_reg_result != 0x90)
fp_warn("odd reg7 value %x", sdev->read_reg_result);
sm_write_reg(ssm, 0x07, sdev->read_reg_result);
break;
case AWFSM_WRITEV_4:
sm_write_regs(ssm, awfsm_writev_4, G_N_ELEMENTS(awfsm_writev_4));
case AWFSM_2016_WRITEV_4:
sm_write_regs(ssm, awfsm_2016_writev_4, G_N_ELEMENTS(awfsm_2016_writev_4));
break;
}
}
static void awfsm_1000_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case AWFSM_1000_WRITEV_1:
sm_write_regs(ssm, awfsm_1000_writev_1, G_N_ELEMENTS(awfsm_1000_writev_1));
break;
case AWFSM_1000_WRITEV_2:
sm_write_regs(ssm, awfsm_1000_writev_2, G_N_ELEMENTS(awfsm_1000_writev_2));
break;
}
}
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_writev[] = {
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
enum capsm_states {
CAPSM_INIT,
CAPSM_WRITE_15,
CAPSM_WRITE_30,
CAPSM_FIRE_BULK,
CAPSM_WRITEV,
CAPSM_NUM_STATES,
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
static void capsm_run_state(struct fpi_ssm *ssm)
enum capsm_2016_states {
CAPSM_2016_INIT,
CAPSM_2016_WRITE_15,
CAPSM_2016_WRITE_30,
CAPSM_2016_FIRE_BULK,
CAPSM_2016_WRITEV,
CAPSM_2016_NUM_STATES,
};
enum capsm_1000_states {
CAPSM_1000_INIT,
CAPSM_1000_FIRE_BULK,
CAPSM_1000_WRITEV,
CAPSM_1000_NUM_STATES,
};
static void capsm_fire_bulk(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
int i;
for (i = 0; i < NUM_BULK_TRANSFERS; i++) {
int r = libusb_submit_transfer(sdev->img_transfer[i]);
if (r < 0) {
if (i == 0) {
/* first one failed: easy peasy */
fpi_ssm_mark_aborted(ssm, r);
return;
}
/* cancel all flying transfers, and request that the SSM
* gets aborted when the last transfer has dropped out of
* the sky */
sdev->killing_transfers = ABORT_SSM;
sdev->kill_ssm = ssm;
sdev->kill_status_code = r;
cancel_img_transfers(dev);
return;
}
sdev->img_transfer_data[i].flying = TRUE;
sdev->num_flying++;
}
sdev->capturing = TRUE;
fpi_ssm_next_state(ssm);
}
static void capsm_2016_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case CAPSM_INIT:
case CAPSM_2016_INIT:
sdev->rowbuf_offset = -1;
sdev->num_rows = 0;
sdev->wraparounds = -1;
@@ -710,68 +861,91 @@ static void capsm_run_state(struct fpi_ssm *ssm)
sdev->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_WRITE_15:
case CAPSM_2016_WRITE_15:
sm_write_reg(ssm, 0x15, 0x20);
break;
case CAPSM_WRITE_30:
case CAPSM_2016_WRITE_30:
sm_write_reg(ssm, 0x30, 0xe0);
break;
case CAPSM_FIRE_BULK: ;
int i;
for (i = 0; i < NUM_BULK_TRANSFERS; i++) {
int r = libusb_submit_transfer(sdev->img_transfer[i]);
if (r < 0) {
if (i == 0) {
/* first one failed: easy peasy */
fpi_ssm_mark_aborted(ssm, r);
return;
}
case CAPSM_2016_FIRE_BULK: ;
capsm_fire_bulk (ssm);
break;
case CAPSM_2016_WRITEV:
sm_write_regs(ssm, capsm_2016_writev, G_N_ELEMENTS(capsm_2016_writev));
break;
}
}
/* cancel all flying transfers, and request that the SSM
* gets aborted when the last transfer has dropped out of
* the sky */
sdev->killing_transfers = ABORT_SSM;
sdev->kill_ssm = ssm;
sdev->kill_status_code = r;
cancel_img_transfers(dev);
return;
}
sdev->img_transfer_data[i].flying = TRUE;
sdev->num_flying++;
}
sdev->capturing = TRUE;
static void capsm_1000_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case CAPSM_1000_INIT:
sdev->rowbuf_offset = -1;
sdev->num_rows = 0;
sdev->wraparounds = -1;
sdev->num_blank = 0;
sdev->finger_removed = 0;
sdev->last_seqnum = 16383;
sdev->killing_transfers = 0;
fpi_ssm_next_state(ssm);
break;
case CAPSM_WRITEV:
sm_write_regs(ssm, capsm_writev, G_N_ELEMENTS(capsm_writev));
case CAPSM_1000_FIRE_BULK: ;
capsm_fire_bulk (ssm);
break;
case CAPSM_1000_WRITEV:
sm_write_regs(ssm, capsm_1000_writev, G_N_ELEMENTS(capsm_1000_writev));
break;
}
}
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_writev[] = {
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
enum deinitsm_states {
DEINITSM_WRITEV,
DEINITSM_NUM_STATES,
static const struct sonly_regwrite deinitsm_1000_writev[] = {
{ 0x15, 0x26 }, { 0x30, 0xe0 }, /* Disable finger detection */
{ 0x0b, 0x00 }, { 0x13, 0x45 }, { 0x08, 0x00 }, /* Disable capture mode */
};
static void deinitsm_run_state(struct fpi_ssm *ssm)
enum deinitsm_2016_states {
DEINITSM_2016_WRITEV,
DEINITSM_2016_NUM_STATES,
};
enum deinitsm_1000_states {
DEINITSM_1000_WRITEV,
DEINITSM_1000_NUM_STATES,
};
static void deinitsm_2016_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case DEINITSM_WRITEV:
sm_write_regs(ssm, deinitsm_writev, G_N_ELEMENTS(deinitsm_writev));
case DEINITSM_2016_WRITEV:
sm_write_regs(ssm, deinitsm_2016_writev, G_N_ELEMENTS(deinitsm_2016_writev));
break;
}
}
static void deinitsm_1000_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case DEINITSM_1000_WRITEV:
sm_write_regs(ssm, deinitsm_1000_writev, G_N_ELEMENTS(deinitsm_1000_writev));
break;
}
}
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_writev_1[] = {
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
@@ -784,47 +958,74 @@ static const struct sonly_regwrite initsm_writev_1[] = {
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
enum initsm_states {
INITSM_WRITEV_1,
INITSM_READ_09,
INITSM_WRITE_09,
INITSM_READ_13,
INITSM_WRITE_13,
INITSM_WRITE_04,
INITSM_WRITE_05,
INITSM_NUM_STATES,
static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x49, 0x00 }, /* Encryption disabled */
/* Setting encryption key. Doesn't need to be random since we don't use any
* encryption. */
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f }, { 0x3e, 0x7f },
{ 0x04, 0x00 }, { 0x05, 0x00 },
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
static void initsm_run_state(struct fpi_ssm *ssm)
enum initsm_2016_states {
INITSM_2016_WRITEV_1,
INITSM_2016_READ_09,
INITSM_2016_WRITE_09,
INITSM_2016_READ_13,
INITSM_2016_WRITE_13,
INITSM_2016_WRITE_04,
INITSM_2016_WRITE_05,
INITSM_2016_NUM_STATES,
};
enum initsm_1000_states {
INITSM_1000_WRITEV_1,
INITSM_1000_NUM_STATES,
};
static void initsm_2016_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct sonly_dev *sdev = dev->priv;
switch (ssm->cur_state) {
case INITSM_WRITEV_1:
sm_write_regs(ssm, initsm_writev_1, G_N_ELEMENTS(initsm_writev_1));
case INITSM_2016_WRITEV_1:
sm_write_regs(ssm, initsm_2016_writev_1, G_N_ELEMENTS(initsm_2016_writev_1));
break;
case INITSM_READ_09:
case INITSM_2016_READ_09:
sm_read_reg(ssm, 0x09);
break;
case INITSM_WRITE_09:
case INITSM_2016_WRITE_09:
sm_write_reg(ssm, 0x09, sdev->read_reg_result & ~0x08);
break;
case INITSM_READ_13:
case INITSM_2016_READ_13:
sm_read_reg(ssm, 0x13);
break;
case INITSM_WRITE_13:
case INITSM_2016_WRITE_13:
sm_write_reg(ssm, 0x13, sdev->read_reg_result & ~0x10);
break;
case INITSM_WRITE_04:
case INITSM_2016_WRITE_04:
sm_write_reg(ssm, 0x04, 0x00);
break;
case INITSM_WRITE_05:
case INITSM_2016_WRITE_05:
sm_write_reg(ssm, 0x05, 0x00);
break;
}
}
static void initsm_1000_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
case INITSM_1000_WRITEV_1:
sm_write_regs(ssm, initsm_1000_writev_1, G_N_ELEMENTS(initsm_1000_writev_1));
break;
}
}
/***** CAPTURE LOOP *****/
enum loopsm_states {
@@ -847,8 +1048,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
if (sdev->deactivating) {
fpi_ssm_mark_completed(ssm);
} else {
struct fpi_ssm *awfsm = fpi_ssm_new(dev->dev, awfsm_run_state,
AWFSM_NUM_STATES);
struct fpi_ssm *awfsm = NULL;
switch (sdev->dev_model) {
case UPEKSONLY_2016:
awfsm = fpi_ssm_new(dev->dev, awfsm_2016_run_state,
AWFSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
awfsm = fpi_ssm_new(dev->dev, awfsm_1000_run_state,
AWFSM_1000_NUM_STATES);
break;
}
awfsm->priv = dev;
fpi_ssm_start_subsm(ssm, awfsm);
}
@@ -857,8 +1067,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
sm_await_intr(ssm);
break;
case LOOPSM_RUN_CAPSM: ;
struct fpi_ssm *capsm = fpi_ssm_new(dev->dev, capsm_run_state,
CAPSM_NUM_STATES);
struct fpi_ssm *capsm = NULL;
switch (sdev->dev_model) {
case UPEKSONLY_2016:
capsm = fpi_ssm_new(dev->dev, capsm_2016_run_state,
CAPSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
capsm = fpi_ssm_new(dev->dev, capsm_1000_run_state,
CAPSM_1000_NUM_STATES);
break;
}
capsm->priv = dev;
fpi_ssm_start_subsm(ssm, capsm);
break;
@@ -867,8 +1086,17 @@ static void loopsm_run_state(struct fpi_ssm *ssm)
* to push us into next state */
break;
case LOOPSM_RUN_DEINITSM: ;
struct fpi_ssm *deinitsm = fpi_ssm_new(dev->dev, deinitsm_run_state,
DEINITSM_NUM_STATES);
struct fpi_ssm *deinitsm = NULL;
switch (sdev->dev_model) {
case UPEKSONLY_2016:
deinitsm = fpi_ssm_new(dev->dev, deinitsm_2016_run_state,
DEINITSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
deinitsm = fpi_ssm_new(dev->dev, deinitsm_1000_run_state,
DEINITSM_1000_NUM_STATES);
break;
}
sdev->capturing = FALSE;
deinitsm->priv = dev;
fpi_ssm_start_subsm(ssm, deinitsm);
@@ -952,7 +1180,7 @@ static void initsm_complete(struct fpi_ssm *ssm)
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct sonly_dev *sdev = dev->priv;
struct fpi_ssm *ssm;
struct fpi_ssm *ssm = NULL;
int i;
sdev->deactivating = FALSE;
@@ -977,7 +1205,14 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
4096, img_data_cb, &sdev->img_transfer_data[i], 0);
}
ssm = fpi_ssm_new(dev->dev, initsm_run_state, INITSM_NUM_STATES);
switch (sdev->dev_model) {
case UPEKSONLY_2016:
ssm = fpi_ssm_new(dev->dev, initsm_2016_run_state, INITSM_2016_NUM_STATES);
break;
case UPEKSONLY_1000:
ssm = fpi_ssm_new(dev->dev, initsm_1000_run_state, INITSM_1000_NUM_STATES);
break;
}
ssm->priv = dev;
fpi_ssm_start(ssm, initsm_complete);
return 0;
@@ -1000,6 +1235,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
}
dev->priv = g_malloc0(sizeof(struct sonly_dev));
((struct sonly_dev*)dev->priv)->dev_model = (int)driver_data;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
@@ -1013,21 +1249,27 @@ static void dev_deinit(struct fp_img_dev *dev)
static int dev_discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
{
/* Revision 1 is what we're interested in */
if (dsc->bcdDevice == 1)
return 1;
if (dsc->idProduct == 0x2016) {
if (dsc->bcdDevice == 1) /* Revision 1 is what we're interested in */
return 1;
}
if (dsc->idProduct == 0x1000) {
if (dsc->bcdDevice == 0x0033) /* Looking for revision 0.33 */
return 1;
}
return 0;
}
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016 },
{ .vendor = 0x147e, .product = 0x2016, .driver_data = UPEKSONLY_2016 },
{ .vendor = 0x147e, .product = 0x1000, .driver_data = UPEKSONLY_1000 },
{ 0, 0, 0, },
};
struct fp_img_driver upeksonly_driver = {
.driver = {
.id = 9,
.id = UPEKSONLY_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip Sensor-Only",
.id_table = id_table,

View File

@@ -1,6 +1,7 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -17,400 +18,488 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "upektc"
#define FP_COMPONENT "upektc"
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#define SENSOR_FULL_IMAGE 59904
#define WAIT_COUNT 5
#include "upektc.h"
#include "driver_ids.h"
typedef char sint8;
typedef unsigned char uint8;
typedef int sint32;
typedef unsigned int uint32;
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
#define UPEKET_EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define UPEKET_EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define BULK_TIMEOUT 4000
/** scan command */
static const unsigned char anScanCommand[ 0x40 ] = {
0x0e, 0x00, 0x03, 0xa8, 0x00, 0xb6, 0xbb, 0xbb,
0xb8, 0xb7, 0xb8, 0xb5, 0xb8, 0xb9, 0xb8, 0xb9,
0xbb, 0xbb, 0xbe, 0xbb, 0x4e, 0x16, 0xf4, 0x77,
0xa8, 0x07, 0x32, 0x00, 0x6a, 0x16, 0xf4, 0x77,
0x78, 0x24, 0x61, 0x00, 0xc8, 0x00, 0xec, 0x00,
0x01, 0x00, 0x00, 0x00, 0x3c, 0xf3, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x77, 0x84, 0xf5, 0x2f, 0x01,
0x05, 0x90, 0xf6, 0x00, 0xc8, 0x00, 0xec, 0x00
struct upektc_dev {
gboolean deactivating;
const struct setup_cmd *setup_commands;
size_t setup_commands_len;
int ep_in;
int ep_out;
int init_idx;
int sum_threshold;
};
/** init command */
static const unsigned char anInitCommand[ 0x40 ] = {
0x03, 0x00, 0x00, 0x00, 0x02, 0xfb, 0x0f, 0x00,
0xc4, 0xf9, 0x2f, 0x01, 0x6d, 0x4f, 0x01, 0x10,
0x44, 0xf9, 0x2f, 0x01, 0x40, 0x00, 0x00, 0x00,
0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
enum upektc_driver_data {
UPEKTC_2015,
UPEKTC_3001,
};
/**
* \brief Common interaktion routine for the sensor device
* \param dev fingerprint image device pointer
* \param pnRawString raw data string
* \param nLen length we want to read, if 0 do not read at all
* \param pnBuffer buffer pointer we want to store the read buffer
* \return error code
*/
static sint32 askScanner( struct fp_img_dev *dev, const unsigned char *pnRawString, sint32 nLen, sint8 *pnBuffer ) {
sint8 anBuf[ 65535 ];
sint32 nRet;
int transferred;
struct libusb_bulk_transfer msg1 = {
.endpoint = 3,
.data = pnRawString,
.length = 0x40,
};
struct libusb_bulk_transfer msg2 = {
.endpoint = 0x82,
.data = anBuf,
.length = nLen,
};
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
static void start_finger_detection(struct fp_img_dev *dev);
nRet = libusb_bulk_transfer(dev->udev, &msg1, &transferred, 1003);
if (transferred != 0x40) {
return -1;
}
/****** INITIALIZATION/DEINITIALIZATION ******/
if ( !nLen ) {
return 0;
}
enum activate_states {
WRITE_INIT,
READ_DATA,
ACTIVATE_NUM_STATES,
};
nRet = libusb_bulk_transfer(dev->udev, &msg2, &transferred, 1003);
if ( ( transferred == nLen ) && ( pnBuffer != NULL ) ) {
memcpy( pnBuffer, anBuf, nLen );
return transferred;
}
static void upektc_next_init_cmd(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
return nRet;
upekdev->init_idx += 1;
if (upekdev->init_idx == upekdev->setup_commands_len)
fpi_ssm_mark_completed(ssm);
else
fpi_ssm_jump_to_state(ssm, WRITE_INIT);
}
/**
* \brief Quick test if finger is on sensor
* \param pnImage image pointer
* \return 1 on yes, 0 on no
*/
static sint32 ValidScan( sint8 *pnImage ) {
sint32 nIndex, nSum;
static void write_init_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
nSum = 0;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
if (upekdev->setup_commands[upekdev->init_idx].response_len)
fpi_ssm_next_state(ssm);
else
upektc_next_init_cmd(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
for ( nIndex = 0; nIndex < SENSOR_FULL_IMAGE; nIndex++ ) {
if ( ( uint8 ) pnImage[ nIndex ] < 160 ) {
nSum++;
static void read_init_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
upektc_next_init_cmd(ssm);
else
fpi_ssm_mark_aborted(ssm, -EIO);
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char*)upekdev->setup_commands[upekdev->init_idx].cmd,
UPEKTC_CMD_LEN, write_init_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data,
upekdev->setup_commands[upekdev->init_idx].response_len,
read_init_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
if (!ssm->error)
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
/****** FINGER PRESENCE DETECTION ******/
static int finger_present(unsigned char *img, size_t len, int sum_threshold)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++) {
if (img[i] < 160) {
sum++;
}
}
return nSum < 500 ? 0 : 1;
fp_dbg("finger_present: sum is %d\n", sum);
return sum < sum_threshold ? 0 : 1;
}
/**
* \brief Setup Sensor device
* \param dev fingerprint image device pointer
* \return error code
*/
static sint32 SetupSensor( struct fp_img_dev *dev ) {
libusb_claim_interface(dev->udev, 0);
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct upektc_dev *upekdev = dev->priv;
unsigned char *data = transfer->buffer;
/* setup sensor */
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xcc\xf8\x2f\x01\x09\x48\xe7\x77\xf0\xfa\x2f\x01\x09\x48\xe7\x77\xe0\x3a\xe6\x77", 0x00, NULL ) < 0 ) {
return -1;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("data transfer status %d\n", transfer->status);
fpi_imgdev_session_error(dev, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, got %d bytes", transfer->length,
transfer->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
}
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\x58\xf9\x2f\x01\xe9\x4f\x01\x10\xd8\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\xfe\x00\x01\xc0\xbd\xf0\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f", 0x40, NULL ) < 0 ) {
return -2;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xcd\x00\x2c\xf9\x2f\x01\x6d\x4f\x01\x10\xac\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -3;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf8\x00\x00\x02\xfe\x16\x10\x03\xee\x00\x37\x01\x09\x02\x0e\x03\x18\x03\x1a\x03\x20\x10\x2f\x11\x3f\x12\x44\x01\x01\x07\x08\x0c\x00\x6c\x6c\x00\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -4;
};
if ( askScanner( dev, "\x8b\x00\x00\x00\x3a\x50\xf9\x2f\x01\x18\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x88\xf9\x2f\x01\x91\x99\x00\x10\xf8\x00\x00\x00\xbe\x99\x00\x10\xa0\xa6\x04\x10\x01\x9b\x00\x10\x18\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00", 0x40, NULL ) < 0 ) {
return -5;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -6;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -7;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -8;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x0c\x37\x6a\x3d\x73\x3d\x71\x0e\x01\x0e\x81\x3d\x51\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x13\x10\x00\x00\x00\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xf0\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -9;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -10;
};
if ( askScanner( dev, "\x8b\x00\x01\x7c\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x14\xf5\x2f\x01\xa0\x20\x14\x00\x40\xf8\x2f\x01\x05\x90\xf6\x77\x04\x00\x00\x00\x08\x00\x00\x00\x50\xf8\x2f\x01\x40\x39\xf4\x77\xa8\x20\x14\x00\x1c\xf6\x2f\x01\x2c\x20\xf4\x77\x80\x4d\xfb\x77", 0x40, NULL ) < 0 ) {
return -11;
};
if ( askScanner( dev, "\x8b\x00\x03\xc8\x3a\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x40\xf9\x2f\x01\x4f\x9d\x00\x10\x3a\x00\x00\x00\x04\xf9\x01", 0x40, NULL ) < 0 ) {
return -12;
};
if ( askScanner( dev, "\x8b\x00\x04\x02\x06\x0b\x07\x13\x0e\x55\x56\x01\x44\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\x40\x40\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -13;
};
if ( askScanner( dev, "\x07\x00\x20\x00\x3a\x0e\x13\x07\x0f\x14\x07\x10\x15\x07\x12\x16\x07\x13\x17\x07\x14\x18\x07\x15\x18\x07\x16\x19\x07\x17\x1a\x07\x19\x1b\x07\x1a\x1c\x07\x1b\x1d\x07\x1c\x1e\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -14;
};
if ( askScanner( dev, "\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21\x07\x20\x22\x07\x21\x23\x07\x23\x23\x07\x24\x55", 0x00, NULL ) < 0 ) {
return -15;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x0e\x81\x0e\x81\x09\x4d\x00\x07\x00\x20\x3a\x26\x24\x07\x25\x25\x07\x26\x25\x07\x27\x26\x07\x28\x27\x07\x29\x27\x07\x2a\x28\x07\x2b\x29\x07\x2d\x29\x07\x2e\x2a\x07\x2f\x2b\x07\x30\x2b\x07\x31\x2c\x07\x07\x1d\x1f\x07\x1e\x20\x07\x1f\x21", 0x00, NULL ) < 0 ) {
return -16;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -17;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0e\x85\x36\xd8\xf8\x2f\x01\xf1\x9d\x00\x10\xf8\xf8\x2f\x01\x99\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -18;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\x0d\x00\x00\x02\x9e\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -19;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62\x51\x6c\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -20;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf7\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\x6c\x6c\x40\x40\x40\x40\x40\x40\x40\x40\x40\x40\x6c\x00\x00\x00\x00\x00\x60\x62\x62\x62\x62\x62", 0x40, NULL ) < 0 ) {
return -21;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf7\xf4\x00\x14\xf9\x2f\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -22;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x20\x6c\x01\x6d\x4f\x01\x10\x94\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\xf9\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01", 0x00, NULL ) < 0 ) {
return -23;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf9\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c\xe8\xf8\x2f\x01\xec\xf8\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -24;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf9\x01\x00\x1c\xf9\x2f\x01\x6d\x4f\x01\x10\x9c\xf8\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09\x0f\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -25;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x12\x1c\x0c\x1b\x08\x1a\x07\x30\x08\x09\x6d\x08\x27\x00\x9e\x00\x1e\x23\x47\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x6c\xbf\x85\x85\x02\x05\x26\x25\x4d\x10\x10\x00\xff\x81\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x09\x09", 0x00, NULL ) < 0 ) {
return -26;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -27;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0d\xff\x36\xdc\xf8\x2f\x01\xf1\x9d\x00\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -28;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x10\xfc\xf8\x2f\x01\x9d\xf8\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -29;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x08\x0e\x85\x09\xed\x09\x6d\x09\xed\x1e\x3f\x05\x05\x02\x05\x26\x27\x6d\x10\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01", 0x00, NULL ) < 0 ) {
return -30;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf3\x6c\x6c\xf0\xf8\x2f\x01\x97\x40\x01\x10\x08\x00\x00\x00\x00\x00\x00\x00\x3e\xf9\x2f\x01\x04\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\x00\x46\x03\x10\x08\x00\x00\x00\x08\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -31;
};
if ( askScanner( dev, "\x84\x00\x00\x00\x32\x02\xa3\x04\x10\x3b\xa3\x04\x10\x1a\xa3\x04\x10\xf9\xa2\x04\x10\xd8\xa2\x00\xb9\x19\xe2\x87\xba\x56\x78\x72\x68\x9e\x7a\xf4\x65\x6d\xd9\xde\xf6\x33\xa2\x04\x10\x12\xa2\x04\x10\xf1\xa1\x04\x10\x04\x00\x00\x00\x00\x00\x00\xb4\x2d\x6c\xe9", 0x40, NULL ) < 0 ) {
return -32;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x06\x1a\x07\x1b\x08\x1c\x0c\x77\x21\xac\xe5\x77\x00\x00\x00\x00\xaa\x4e\x01\x10\x3c\x01\x00\x00\xc4\xf8\x2f\x01\xdc\xf8\x2f\x01\x00\x00\x00\x00\x40\x00\x00\x00\xb9\x19\xe2\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -33;
};
if ( askScanner( dev, "\x08\x00\x00\x00\x0a\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x00\x40\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00\x01\x00\x00\x00\xcc\xf8\x2f\x01\x8b\x41\x01\x10\x8c\xf8\x2f\x01\x40\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -34;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x04\x3d\x51\x0a\x00\x01\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfc\xf9\x2f\x01\x31\x10\x01\x10\xd0\xf9\x2f\x01\x00\x00\x00\x00\x1a\x07\x1b\x08\x1c\x0c\xc6\xf8\x66\xbc\xc4\xbe\x0b\x25\xc5\x4c\xf4\x03\x10\x2f\x11\x3f\x12\x44", 0x00, NULL ) < 0 ) {
return -35;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x3a\x00\x01\x02\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1f\x20\x21\x22\x23\x24\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3d\x3f\xff\x00", 0x40, NULL ) < 0 ) {
return -36;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\x0a\x10\x36\x88\xf9\x2f\x01\xf1\x9d\x00\x10\xa8\xf9\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c", 0x00, NULL ) < 0 ) {
return -37;
};
if ( askScanner( dev, "\x8b\x00\x00\xbc\x3a\x40\xd3\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8\xf4\x2f\x01\x80\x69\x67\xff\xff\xff\xff\xff\x00\xf0\xfd\x7f\x00\x60\xfd\x7f\x3c\x01\x00\x00\xa0\xf5\x2f\x01\x03\x01\x00\x00\x9a\x11\xf4\x77\x9f\x11\xf4\x77\x3c\x01\x00\x00\xa0\xf5\x01", 0x40, NULL ) < 0 ) {
return -38;
};
if ( askScanner( dev, "\x8b\x00\x00\xf6\x3a\x0b\x07\xa5\x03\x2f\x63\x97\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -39;
};
if ( askScanner( dev, "\x8b\x00\x01\x30\x3a\x0b\x00\x00\x00\x00\x00\x00\x12\xcd\xa6\x3c\x36\xec\x6a\x73\x00\x64\x75\xdf\x2e\x13\xec\xca\x3c\x03\x00\x00\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -40;
};
if ( askScanner( dev, "\x8b\x00\x01\x6a\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -41;
};
if ( askScanner( dev, "\x8b\x00\x01\xa4\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xa5\x83\x1b\x8e\xac\x00\x00\x0b\xa5\x08\x08\x03\x00\x00\x01\x02\x03\x06\x00\x00\x00\x00\x00\x8d\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -42;
};
if ( askScanner( dev, "\x8b\x00\x01\xde\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -43;
};
if ( askScanner( dev, "\x8b\x00\x02\x18\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -44;
};
if ( askScanner( dev, "\x8b\x00\x02\x52\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -45;
};
if ( askScanner( dev, "\x8b\x00\x02\x8c\x3a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -46;
};
if ( askScanner( dev, "\x8b\x00\x02\xc6\x2a\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -47;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xf1\x2f\x01\x49\xf9\x2f\x01\x3a\x00\x00\x00\x00\x00\x00\x00\x02\x1e\x3f\x05\x05\x02\x05\x26\x27\xed\x00\x10\x00\xff\x85\x6c\x00\x00\xcf\x00\x01\x00\x00\x1f\x01\x01\x07\x08\x0c\x00\x6c\x6c\x9c\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -48;
};
if ( askScanner( dev, "\x03\x00\x00\x00\x02\xf1\x01\x00\xb4\xf9\x2f\x01\x6d\x4f\x01\x10\x34\xf9\x2f\x01\x40\x00\x00\x00\xe8\x03\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x00, NULL ) < 0 ) {
return -49;
};
if ( askScanner( dev, "\x8b\x00\x01\x10\x3a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -50;
};
if ( askScanner( dev, "\x8b\x00\x01\x4a\x2e\x0b\x06\xa5\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x01\x00\x00\xc8\x01\x00\x00\x40\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
if ( askScanner( dev, "\x82\x00\x00\x00\x01\xfb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\xf9\x2f\x01\x97\x40\x01\x10\x03\x00\x00\x00\x00\x00\x00\x00\xfa\x45\x03\x10\x02\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0x40, NULL ) < 0 ) {
return -51;
};
/* enable sensor */
if ( askScanner( dev, anInitCommand, 0x00, NULL ) < 0 ) {
return -52;
if (finger_present(data, IMAGE_SIZE, upekdev->sum_threshold)) {
/* 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_cmd_cb(struct libusb_transfer *t)
{
struct libusb_transfer *transfer;
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
struct upektc_dev *upekdev = dev->priv;
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
fpi_imgdev_session_error(dev, -EIO);
goto exit_free_transfer;
} else if (t->length != t->actual_length) {
fp_dbg("expected %d, sent %d bytes", t->length, t->actual_length);
fpi_imgdev_session_error(dev, -EPROTO);
goto exit_free_transfer;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
goto exit_free_transfer;
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
exit_free_transfer:
libusb_free_transfer(t);
}
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct upektc_dev *upekdev = dev->priv;
struct libusb_transfer *transfer;
fp_dbg("");
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
finger_det_cmd_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_imgdev_session_error(dev, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_WRITE_CMD,
CAPTURE_READ_DATA,
CAPTURE_NUM_STATES,
};
static void capture_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
libusb_free_transfer(transfer);
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
unsigned char *data = transfer->buffer;
struct fp_img *img;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
} else if (transfer->length != transfer->actual_length) {
fp_dbg("expected %d, sent %d bytes", transfer->length, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -EPROTO);
goto out;
}
img = fpi_img_new(IMAGE_SIZE);
memcpy(img->data, data, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
out:
g_free(transfer->buffer);
libusb_free_transfer(transfer);
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
case CAPTURE_WRITE_CMD:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
capture_cmd_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
}
}
break;
case CAPTURE_READ_DATA:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
fp_dbg("Capture completed");
if (upekdev->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 upektc_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
if (upekdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct upektc_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
upekdev->init_idx = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static int DetectFinger( struct fp_img_dev *dev ) {
sint32 nRet = 0;
uint8 *pnData = NULL;
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet != SENSOR_FULL_IMAGE ) {
nRet = 0;
goto end;
}
nRet = ValidScan( pnData );
end:
g_free( pnData );
return nRet;
upekdev->deactivating = TRUE;
}
static int awaitFingerOn( struct fp_img_dev *dev ) {
int nRet = 0;
int nCount = WAIT_COUNT;
static void complete_deactivation(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
fp_dbg("");
/* wait until a finger is present */
do {
nRet = DetectFinger( dev );
} while ( nRet == 0 );
/* give user time to scan his full finger */
while ( nCount-- ) {
nRet = DetectFinger( dev );
}
return nRet != 1 ? nRet : 0;
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static int capture( struct fp_img_dev *dev, gboolean unconditional, struct fp_img **ppsRet ) {
struct fp_img *psImg = NULL;
uint8 *pnData = NULL;
sint32 nRet = 0;
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
struct upektc_dev *upekdev;
psImg = fpi_img_new_for_imgdev( dev );
pnData = g_malloc( SENSOR_FULL_IMAGE );
nRet = askScanner( dev, anScanCommand, SENSOR_FULL_IMAGE, pnData );
if ( nRet == SENSOR_FULL_IMAGE ) {
memcpy( psImg -> data, pnData, SENSOR_FULL_IMAGE );
*ppsRet = psImg;
nRet = 0;
} else {
nRet = -1;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
g_free( pnData );
return nRet;
}
static int dev_init( struct fp_img_dev *dev, unsigned long driver_data ) {
int nResult;
nResult = libusb_claim_interface(dev->udev, 0);
if ( nResult < 0 ) {
fp_err( "could not claim interface 0" );
return nResult;
dev->priv = upekdev = g_malloc0(sizeof(struct upektc_dev));
switch (driver_data) {
case UPEKTC_2015:
upekdev->ep_in = UPEKTC_EP_IN;
upekdev->ep_out = UPEKTC_EP_OUT;
upekdev->setup_commands = upektc_setup_commands;
upekdev->setup_commands_len = array_n_elements(upektc_setup_commands);
upekdev->sum_threshold = UPEKTC_SUM_THRESHOLD;
break;
case UPEKTC_3001:
upekdev->ep_in = UPEKET_EP_IN;
upekdev->ep_out = UPEKET_EP_OUT;
upekdev->setup_commands = upeket_setup_commands;
upekdev->setup_commands_len = array_n_elements(upeket_setup_commands);
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
break;
default:
fp_err("Device variant %d is not known\n", driver_data);
g_free(upekdev);
dev->priv = NULL;
return -ENODEV;
break;
}
nResult = SetupSensor( dev );
return nResult;
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_exit( struct fp_img_dev *dev ) {
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 = 0x0483, .product = 0x2015 },
{ .vendor = 0x0483, .product = 0x2015, .driver_data = UPEKTC_2015 },
{ .vendor = 0x147e, .product = 0x3001, .driver_data = UPEKTC_3001 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_driver = {
.driver = {
.id = 5,
.id = UPEKTC_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchChip",
.full_name = "UPEK TouchChip/Eikon Touch 300",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_PRESS,
},
.flags = FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE,
.img_height = 288,
.img_width = 208,
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 30,
.init = dev_init,
.exit = dev_exit,
.await_finger_on = awaitFingerOn,
.capture = capture,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

1939
libfprint/drivers/upektc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,674 @@
/*
* UPEK TouchChip driver for libfprint
* Copyright (C) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 "upekts_img"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "upektc_img.h"
#include "driver_ids.h"
static void start_capture(struct fp_img_dev *dev);
static void start_deactivation(struct fp_img_dev *dev);
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define CTRL_TIMEOUT 4000
#define BULK_TIMEOUT 4000
#define IMAGE_WIDTH 144
#define IMAGE_HEIGHT 384
#define IMAGE_SIZE (IMAGE_WIDTH * IMAGE_HEIGHT)
#define MAX_CMD_SIZE 64
#define MAX_RESPONSE_SIZE 2052
#define SHORT_RESPONSE_SIZE 64
struct upekts_img_dev {
unsigned char cmd[MAX_CMD_SIZE];
unsigned char response[MAX_RESPONSE_SIZE];
unsigned char image_bits[IMAGE_SIZE * 2];
unsigned char seq;
size_t image_size;
size_t response_rest;
gboolean deactivating;
};
/****** HELPERS ******/
static const uint16_t crc_table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
static uint16_t udf_crc(unsigned char *buffer, size_t size)
{
uint16_t crc = 0;
while (size--)
crc = (uint16_t) ((crc << 8) ^
crc_table[((crc >> 8) & 0x00ff) ^ *buffer++]);
return crc;
}
static void upektc_img_cmd_fix_seq(unsigned char *cmd_buf, unsigned char seq)
{
uint8_t byte;
byte = cmd_buf[5];
byte &= 0x0f;
byte |= (seq << 4);
cmd_buf[5] = byte;
}
static void upektc_img_cmd_update_crc(unsigned char *cmd_buf, size_t size)
{
/* CRC does not cover Ciao prefix (4 bytes) and CRC location (2 bytes) */
uint16_t crc = udf_crc(cmd_buf + 4, size - 6);
cmd_buf[size - 2] = (crc & 0x00ff);
cmd_buf[size - 1] = (crc & 0xff00) >> 8;
}
static void upektc_img_submit_req(struct fpi_ssm *ssm,
const unsigned char *buf, size_t buf_size, unsigned char seq,
libusb_transfer_cb_fn cb)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
BUG_ON(buf_size > MAX_CMD_SIZE);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
memcpy(upekdev->cmd, buf, buf_size);
upektc_img_cmd_fix_seq(upekdev->cmd, seq);
upektc_img_cmd_update_crc(upekdev->cmd, buf_size);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, upekdev->cmd, buf_size,
cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
static void upektc_img_read_data(struct fpi_ssm *ssm, size_t buf_size, size_t buf_offset, libusb_transfer_cb_fn cb)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
int r;
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
BUG_ON(buf_size > MAX_RESPONSE_SIZE);
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, upekdev->response + buf_offset, buf_size,
cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
/****** CAPTURE ******/
enum capture_states {
CAPTURE_INIT_CAPTURE,
CAPTURE_READ_DATA,
CAPTURE_ACK_00_28,
CAPTURE_ACK_08,
CAPTURE_ACK_FRAME,
CAPTURE_NUM_STATES,
};
static void capture_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static int upektc_img_process_image_frame(unsigned char *image_buf, unsigned char *cmd_res)
{
int offset = 8;
int len = ((cmd_res[5] & 0x0f) << 8) | (cmd_res[6]);
len -= 1;
if (cmd_res[7] == 0x2c) {
len -= 10;
offset += 10;
}
if (cmd_res[7] == 0x20) {
len -= 4;
}
memcpy(image_buf, cmd_res + offset, len);
return len;
}
static void capture_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
unsigned char *data = upekdev->response;
struct fp_img *img;
size_t response_size;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("request is not completed, %d", transfer->status);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
if (upekdev->deactivating) {
fp_dbg("Deactivate requested\n");
fpi_ssm_mark_completed(ssm);
return;
}
fp_dbg("request completed, len: %.4x", transfer->actual_length);
if (transfer->actual_length == 0) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
return;
}
if (!upekdev->response_rest) {
response_size = ((data[5] & 0x0f) << 8) + data[6];
response_size += 9; /* 7 bytes for header, 2 for CRC */
if (response_size > transfer->actual_length) {
fp_dbg("response_size is %d, actual_length is %d\n",
response_size, transfer->actual_length);
fp_dbg("Waiting for rest of transfer");
BUG_ON(upekdev->response_rest);
upekdev->response_rest = response_size - transfer->actual_length;
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
return;
}
}
upekdev->response_rest = 0;
switch (data[4]) {
case 0x00:
switch (data[7]) {
/* No finger */
case 0x28:
fp_dbg("18th byte is %.2x\n", data[18]);
switch (data[18]) {
case 0x0c:
/* no finger */
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
break;
case 0x00:
/* finger is present! */
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28);
break;
default:
/* some error happened, cancel scan */
fp_err("something bad happened, aborting scan :(\n");
fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY_REMOVE_FINGER);
break;
}
break;
/* Image frame with additional info */
case 0x2c:
fpi_imgdev_report_finger_status(dev, TRUE);
/* Plain image frame */
case 0x24:
upekdev->image_size +=
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
data);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_FRAME);
break;
/* Last image frame */
case 0x20:
upekdev->image_size +=
upektc_img_process_image_frame(upekdev->image_bits + upekdev->image_size,
data);
BUG_ON(upekdev->image_size != IMAGE_SIZE);
fp_dbg("Image size is %d\n", upekdev->image_size);
img = fpi_img_new(IMAGE_SIZE);
memcpy(img->data, upekdev->image_bits, IMAGE_SIZE);
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
break;
default:
fp_err("Uknown response!\n");
fpi_ssm_mark_aborted(ssm, -EIO);
break;
}
break;
case 0x08:
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_08);
break;
default:
fp_err("Not handled response!\n");
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
switch (ssm->cur_state) {
case CAPTURE_INIT_CAPTURE:
upektc_img_submit_req(ssm, upek2020_init_capture, sizeof(upek2020_init_capture),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
case CAPTURE_READ_DATA:
if (!upekdev->response_rest)
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
else
upektc_img_read_data(ssm, MAX_RESPONSE_SIZE - SHORT_RESPONSE_SIZE,
SHORT_RESPONSE_SIZE, capture_read_data_cb);
break;
case CAPTURE_ACK_00_28:
upektc_img_submit_req(ssm, upek2020_ack_00_28, sizeof(upek2020_ack_00_28),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
case CAPTURE_ACK_08:
upektc_img_submit_req(ssm, upek2020_ack_08, sizeof(upek2020_ack_08),
0, capture_reqs_cb);
break;
case CAPTURE_ACK_FRAME:
upektc_img_submit_req(ssm, upek2020_ack_frame, sizeof(upek2020_ack_frame),
upekdev->seq, capture_reqs_cb);
upekdev->seq++;
break;
};
}
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
int err = ssm->error;
fp_dbg("Capture completed, %d", err);
fpi_ssm_free(ssm);
if (upekdev->deactivating)
start_deactivation(dev);
else if (err)
fpi_imgdev_session_error(dev, err);
else
start_capture(dev);
}
static void start_capture(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, capture_sm_complete);
}
/****** INITIALIZATION/DEINITIALIZATION ******/
enum deactivate_states {
DEACTIVATE_DEINIT,
DEACTIVATE_READ_DEINIT_DATA,
DEACTIVATE_NUM_STATES,
};
static void deactivate_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* TODO: process response properly */
static void deactivate_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_completed(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static void deactivate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
switch (ssm->cur_state) {
case DEACTIVATE_DEINIT:
upektc_img_submit_req(ssm, upek2020_deinit, sizeof(upek2020_deinit),
upekdev->seq, deactivate_reqs_cb);
upekdev->seq++;
break;
case DEACTIVATE_READ_DEINIT_DATA:
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, deactivate_read_data_cb);
break;
};
}
static void deactivate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
int err = ssm->error;
fp_dbg("Deactivate completed");
fpi_ssm_free(ssm);
if (err) {
fpi_imgdev_session_error(dev, err);
return;
}
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
}
static void start_deactivation(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(dev->dev, deactivate_run_state, DEACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_start(ssm, deactivate_sm_complete);
}
enum activate_states {
ACTIVATE_CONTROL_REQ_1,
ACTIVATE_READ_CTRL_RESP_1,
ACTIVATE_INIT_1,
ACTIVATE_READ_INIT_1_RESP,
ACTIVATE_INIT_2,
ACTIVATE_READ_INIT_2_RESP,
ACTIVATE_CONTROL_REQ_2,
ACTIVATE_READ_CTRL_RESP_2,
ACTIVATE_INIT_3,
ACTIVATE_READ_INIT_3_RESP,
ACTIVATE_INIT_4,
ACTIVATE_READ_INIT_4_RESP,
ACTIVATE_NUM_STATES,
};
static void init_reqs_ctrl_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static void init_reqs_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* TODO: process response properly */
static void init_read_data_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_next_state(ssm);
} else {
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static void activate_run_state(struct fpi_ssm *ssm)
{
struct libusb_transfer *transfer;
struct fp_img_dev *dev = ssm->priv;
struct upekts_img_dev *upekdev = dev->priv;
int r;
switch (ssm->cur_state) {
case ACTIVATE_CONTROL_REQ_1:
case ACTIVATE_CONTROL_REQ_2:
{
unsigned char *data;
transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
break;
}
transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER |
LIBUSB_TRANSFER_FREE_TRANSFER;
data = g_malloc0(LIBUSB_CONTROL_SETUP_SIZE + 1);
libusb_fill_control_setup(data,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x0c, 0x100, 0x0400, 1);
libusb_fill_control_transfer(transfer, ssm->dev->udev, data,
init_reqs_ctrl_cb, ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
g_free(data);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, r);
}
}
break;
case ACTIVATE_INIT_1:
upektc_img_submit_req(ssm, upek2020_init_1, sizeof(upek2020_init_1),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_2:
upektc_img_submit_req(ssm, upek2020_init_2, sizeof(upek2020_init_2),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_3:
upektc_img_submit_req(ssm, upek2020_init_3, sizeof(upek2020_init_3),
0, init_reqs_cb);
break;
case ACTIVATE_INIT_4:
upektc_img_submit_req(ssm, upek2020_init_4, sizeof(upek2020_init_4),
upekdev->seq, init_reqs_cb);
/* Seq should be updated after 4th init */
upekdev->seq++;
break;
case ACTIVATE_READ_CTRL_RESP_1:
case ACTIVATE_READ_CTRL_RESP_2:
case ACTIVATE_READ_INIT_1_RESP:
case ACTIVATE_READ_INIT_2_RESP:
case ACTIVATE_READ_INIT_3_RESP:
case ACTIVATE_READ_INIT_4_RESP:
upektc_img_read_data(ssm, SHORT_RESPONSE_SIZE, 0, init_read_data_cb);
break;
}
}
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int err = ssm->error;
fpi_ssm_free(ssm);
fp_dbg("%s status %d", __func__, err);
fpi_imgdev_activate_complete(dev, err);
if (!err)
start_capture(dev);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct upekts_img_dev *upekdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
upekdev->seq = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upekts_img_dev *upekdev = dev->priv;
upekdev->deactivating = TRUE;
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* TODO check that device has endpoints we're using */
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
fp_err("could not claim interface 0");
return r;
}
dev->priv = g_malloc0(sizeof(struct upekts_img_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 int discover(struct libusb_device_descriptor *dsc, uint32_t *devtype)
{
if (dsc->idProduct == 0x2020 && dsc->bcdDevice == 1)
return 1;
#ifndef ENABLE_UPEKE2
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
return 1;
#endif
return 0;
}
static const struct usb_id id_table[] = {
#ifndef ENABLE_UPEKE2
{ .vendor = 0x147e, .product = 0x2016 },
#endif
{ .vendor = 0x147e, .product = 0x2020 },
{ 0, 0, 0, },
};
struct fp_img_driver upektc_img_driver = {
.driver = {
.id = UPEKTC_IMG_ID,
.name = FP_COMPONENT,
.full_name = "Upek TouchChip Fingerprint Coprocessor",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
.discover = discover,
},
.flags = 0,
.img_height = IMAGE_HEIGHT,
.img_width = IMAGE_WIDTH,
.bz3_threshold = 70,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -0,0 +1,144 @@
/*
* Upek TouchChip Fingerprint Coprocessor definitions
* Copyright (c) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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 __UPEKTC_IMG_H
#define __UPEKTC_IMG_H
static const unsigned char upek2020_init_1[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0xda, 0xc1
};
static const unsigned char upek2020_init_2[] = {
0x43, 0x69, 0x61, 0x6f,
0x07,
0x00, 0x01,
0x01,
0x3d, 0x72
};
static const unsigned char upek2020_init_3[] = {
'C', 'i', 'a', 'o',
0x04,
0x00, 0x0d,
0x01, 0x00, 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x00,
0x55, 0x2f
};
static const unsigned char upek2020_init_4[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x07,
0x28, 0x04, 0x00, 0x00, 0x00, 0x06, 0x04,
0xc0, 0xd6
};
static const unsigned char upek2020_deinit[] = {
'C', 'i', 'a', 'o',
0x07,
0x00, 0x01,
0x01,
0x3d,
0x72
};
static const unsigned char upek2020_init_capture[] = {
'C', 'i', 'a', 'o',
0x00,
0x00, 0x0e, /* Seq = 7, len = 0x00e */
0x28, /* CMD = 0x28 */
0x0b, 0x00, /* Inner len = 0x000b */
0x00, 0x00,
0x0e, /* SUBCMD = 0x0e */
0x02,
0xfe, 0xff, 0xff, 0xff, /* timeout = -2 = 0xfffffffe = infinite time */
0x02,
0x00, /* Wait for acceptable finger */
0x02,
0x14, 0x9a /* CRC */
};
#if 0
static const unsigned char finger_status[] = {
'C', 'i', 'a', 'o',
0x00,
0x70, 0x14, /* Seq = 7, len = 0x014 */
0x28, /* CMD = 0x28 */
0x11, 0x00, /* Inner len = 0x0011 */
0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x26, 0x03, /* CRC */
0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
static const unsigned char upek2020_ack_00_28[] = {
'C', 'i', 'a', 'o',
0x00,
0x80, 0x08, /* Seq = 8, len = 0x008 */
0x28, /* CMD = 0x28 */
0x05, 0x00, /* Inner len = 0x0005 */
0x00, 0x00, 0x00, 0x30, 0x01,
0x6a, 0xc4 /* CRC */
};
#if 0
/* No seq should be tracked here */
static const unsigned char got_finger[] = {
'C', 'i', 'a', 'o',
0x08,
0x00, 0x00, /* Seq = 0, len = 0x000 */
0xa1, 0xa9, /* CRC */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Rest is garbage */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#endif
/* No seq should be put in there */
static const unsigned char upek2020_ack_08[] = {
'C', 'i', 'a', 'o',
0x09,
0x00, 0x00, /* Seq = 0, len = 0x0 */
0x91, 0x9e /* CRC */
};
static const unsigned char upek2020_ack_frame[] = {
'C', 'i', 'a', 'o',
0x00,
0x50, 0x01, /* Seq = 5, len = 0x001 */
0x30,
0xac, 0x5b /* CRC */
};
#endif

View File

@@ -35,6 +35,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
#define TIMEOUT 5000
@@ -1075,6 +1077,7 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct fp_print_data *fdata = NULL;
struct fp_print_data_item *item = NULL;
int result = -EPROTO;
if (data_len < sizeof(scan_comp)) {
@@ -1083,9 +1086,11 @@ static void e_handle_resp02(struct fp_dev *dev, unsigned char *data,
fp_err("unrecognised data prefix %x %x %x %x %x",
data[0], data[1], data[2], data[3], data[4]);
} else {
fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp));
memcpy(fdata->data, data + sizeof(scan_comp),
fdata = fpi_print_data_new(dev);
item = fpi_print_data_item_new(data_len - sizeof(scan_comp));
memcpy(item->data, data + sizeof(scan_comp),
data_len - sizeof(scan_comp));
fdata->prints = g_slist_prepend(fdata->prints, item);
result = FP_ENROLL_COMPLETE;
}
@@ -1247,12 +1252,13 @@ static void verify_start_sm_run_state(struct fpi_ssm *ssm)
break;
case VERIFY_INIT: ;
struct fp_print_data *print = dev->verify_data;
size_t data_len = sizeof(verify_hdr) + print->length;
struct fp_print_data_item *item = print->prints->data;
size_t data_len = sizeof(verify_hdr) + item->length;
unsigned char *data = g_malloc(data_len);
struct libusb_transfer *transfer;
memcpy(data, verify_hdr, sizeof(verify_hdr));
memcpy(data + sizeof(verify_hdr), print->data, print->length);
memcpy(data + sizeof(verify_hdr), item->data, item->length);
transfer = alloc_send_cmd28_transfer(dev, 0x03, data, data_len,
verify_init_2803_cb, ssm);
g_free(data);
@@ -1464,7 +1470,7 @@ static const struct usb_id id_table[] = {
};
struct fp_driver upekts_driver = {
.id = 1,
.id = UPEKTS_ID,
.name = FP_COMPONENT,
.full_name = "UPEK TouchStrip",
.id_table = id_table,

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,8 @@
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
@@ -368,7 +370,7 @@ static const struct usb_id id_table[] = {
struct fp_img_driver vcom5s_driver = {
.driver = {
.id = 8,
.id = VCOM5S_ID,
.name = FP_COMPONENT,
.full_name = "Veridicom 5thSense",
.id_table = id_table,

1566
libfprint/drivers/vfs101.c Normal file

File diff suppressed because it is too large Load Diff

305
libfprint/drivers/vfs301.c Normal file
View File

@@ -0,0 +1,305 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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 "vfs301"
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include <unistd.h>
#include <fp_internal.h>
#include "driver_ids.h"
/************************** GENERIC STUFF *************************************/
/* Callback of asynchronous sleep */
static void async_sleep_cb(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_timeout *timeout;
/* Add timeout */
timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_mark_aborted(ssm, -ETIME);
}
}
static int submit_image(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
int height;
struct fp_img *img;
#if 0
/* XXX: This is probably handled by libfprint automagically? */
if (vdev->scanline_count < 20) {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
return 0;
}
#endif
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
if (img == NULL)
return 0;
vfs301_extract_image(vdev, img->data, &height);
/* TODO: how to detect flip? should the resulting image be
* oriented so that it is equal e.g. to a fingerprint on a paper,
* or to the finger when I look at it?) */
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
img->width = VFS301_FP_OUTPUT_WIDTH;
img->height = height;
img = fpi_img_resize(img, img->height * img->width);
fpi_imgdev_image_captured(dev, img);
return 1;
}
/* Loop ssm states */
enum
{
/* Step 0 - Scan finger */
M_REQUEST_PRINT,
M_WAIT_PRINT,
M_CHECK_PRINT,
M_READ_PRINT_START,
M_READ_PRINT_WAIT,
M_READ_PRINT_POLL,
M_SUBMIT_PRINT,
/* Number of states */
M_LOOP_NUM_STATES,
};
/* Exec loop sequential state machine */
static void m_loop_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
switch (ssm->cur_state) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_WAIT_PRINT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(dev->udev, vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(dev->udev, vdev);
fpi_ssm_next_state(ssm);
break;
case M_READ_PRINT_WAIT:
/* Wait fingerprint scanning */
async_sleep(200, ssm);
break;
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(dev->udev, vdev);
assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
fpi_ssm_next_state(ssm);
}
break;
case M_SUBMIT_PRINT:
if (submit_image(ssm)) {
fpi_ssm_mark_completed(ssm);
/* NOTE: finger off is expected only after submitting image... */
fpi_imgdev_report_finger_status(dev, FALSE);
} else {
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
}
break;
}
}
/* Complete loop sequential state machine */
static void m_loop_complete(struct fpi_ssm *ssm)
{
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Exec init sequential state machine */
static void m_init_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
assert(ssm->cur_state == 0);
vfs301_proto_init(dev->udev, vdev);
fpi_ssm_mark_completed(ssm);
}
/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fpi_ssm *ssm_loop;
if (!ssm->error) {
/* Notify activate complete */
fpi_imgdev_activate_complete(dev, 0);
/* Start loop ssm */
ssm_loop = fpi_ssm_new(dev->dev, m_loop_state, M_LOOP_NUM_STATES);
ssm_loop->priv = dev;
fpi_ssm_start(ssm_loop, m_loop_complete);
}
/* Free sequential state machine */
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct fpi_ssm *ssm;
/* Start init ssm */
ssm = fpi_ssm_new(dev->dev, m_init_state, 1);
ssm->priv = dev;
fpi_ssm_start(ssm, m_init_complete);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
fpi_imgdev_deactivate_complete(dev);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
vfs301_dev_t *vdev = NULL;
int r;
/* Claim usb interface */
r = libusb_claim_interface(dev->udev, 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
return r;
}
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
dev->priv = vdev;
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
/* Release private structure */
free(((vfs301_dev_t*)dev->priv)->scanline_buf);
g_free(dev->priv);
/* Release usb interface */
libusb_release_interface(dev->udev, 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);
}
/* Usb id table of device */
static const struct usb_id id_table[] =
{
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
{ 0, 0, 0, },
};
/* Device driver definition */
struct fp_img_driver vfs301_driver =
{
/* Driver specification */
.driver =
{
.id = VFS301_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS301",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS301_FP_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

View File

@@ -0,0 +1,639 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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
*/
/*
* TODO:
* - async communication everywhere :)
* - protocol decyphering
* - what is needed and what is redundant
* - is some part of the initial data the firmware?
* - describe some interesting structures better
*/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include "vfs301_proto.h"
#include "vfs301_proto_fragments.h"
#include <unistd.h>
#define min(a, b) (((a) < (b)) ? (a) : (b))
/************************** USB STUFF *****************************************/
#ifdef DEBUG
static void usb_print_packet(int dir, int rv, const unsigned char *data, int length)
{
fprintf(stderr, "%s, rv %d, len %d\n", dir ? "send" : "recv", rv, length);
#ifdef PRINT_VERBOSE
int i;
for (i = 0; i < min(length, 128); i++) {
fprintf(stderr, "%.2X ", data[i]);
if (i % 8 == 7)
fprintf(stderr, " ");
if (i % 32 == 31)
fprintf(stderr, "\n");
}
#endif
fprintf(stderr, "\n");
}
#endif
static int usb_recv(
vfs301_dev_t *dev,
struct libusb_device_handle *devh, unsigned char endpoint, int max_bytes)
{
assert(max_bytes <= sizeof(dev->recv_buf));
int r = libusb_bulk_transfer(
devh, endpoint,
dev->recv_buf, max_bytes,
&dev->recv_len, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(0, r, dev->recv_buf, dev->recv_len);
#endif
if (r < 0)
return r;
return 0;
}
static int usb_send(
struct libusb_device_handle *devh, const unsigned char *data, int length)
{
int transferred = 0;
int r = libusb_bulk_transfer(
devh, VFS301_SEND_ENDPOINT,
(unsigned char *)data, length, &transferred, VFS301_DEFAULT_WAIT_TIMEOUT
);
#ifdef DEBUG
usb_print_packet(1, r, data, length);
#endif
assert(r == 0);
if (r < 0)
return r;
if (transferred < length)
return r;
return 0;
}
/************************** OUT MESSAGES GENERATION ***************************/
static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
{
*data = 0x0B;
*len = 1;
data++;
memset(data, 0, 39);
*len += 38;
data[20] = subtype;
switch (subtype) {
case 0x04:
data[34] = 0x9F;
break;
case 0x05:
data[34] = 0xAB;
len++;
break;
default:
assert(!"unsupported");
break;
}
}
#define HEX_TO_INT(c) \
(((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'A' + 10))
static void translate_str(const char **srcL, unsigned char *data, int *len)
{
const char *src;
unsigned char *dataOrig = data;
while (*srcL != NULL) {
src = *srcL;
while (*src != '\0') {
assert(*src != '\0');
assert(*(src +1) != '\0');
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
data++;
src += 2;
}
srcL++;
}
*len = data - dataOrig;
}
static void vfs301_proto_generate(int type, int subtype, unsigned char *data, int *len)
{
switch (type) {
case 0x01:
case 0x04:
/* After cmd 0x04 is sent, a data is received on VALIDITY_RECEIVE_ENDPOINT_CTRL.
* If it is 0x0000:
* additional 64B and 224B are read from _DATA, then vfs301_next_scan_FA00 is
* sent, 0000 received from _CTRL, and then continue with wait loop
* If it is 0x1204:
* => reinit?
*/
case 0x17:
case 0x19:
case 0x1A:
*data = type;
*len = 1;
break;
case 0x0B:
vfs301_proto_generate_0B(subtype, data, len);
break;
case 0x02D0:
{
const char **dataLs[] = {
vfs301_02D0_01,
vfs301_02D0_02,
vfs301_02D0_03,
vfs301_02D0_04,
vfs301_02D0_05,
vfs301_02D0_06,
vfs301_02D0_07,
};
assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
translate_str(dataLs[subtype - 1], data, len);
}
break;
case 0x0220:
switch (subtype) {
case 1:
translate_str(vfs301_0220_01, data, len);
break;
case 2:
translate_str(vfs301_0220_02, data, len);
break;
case 3:
translate_str(vfs301_0220_03, data, len);
break;
case 0xFA00:
case 0x2C01:
case 0x5E01:
translate_str(vfs301_next_scan_template, data, len);
unsigned char *field = data + *len - (sizeof(S4_TAIL) - 1) / 2 - 4;
assert(*field == 0xDE);
assert(*(field + 1) == 0xAD);
assert(*(field + 2) == 0xDE);
assert(*(field + 3) == 0xAD);
*field = (unsigned char)((subtype >> 8) & 0xFF);
*(field + 1) = (unsigned char)(subtype & 0xFF);
*(field + 2) = *field;
*(field + 3) = *(field + 1);
break;
default:
assert(0);
break;
}
break;
case 0x06:
assert(!"Not generated");
break;
default:
assert(!"Unknown message type");
break;
}
}
/************************** SCAN IMAGE PROCESSING *****************************/
#ifdef SCAN_FINISH_DETECTION
static int img_is_finished_scan(fp_line_t *lines, int no_lines)
{
int i;
int j;
int rv = 1;
for (i = no_lines - VFS301_FP_SUM_LINES; i < no_lines; i++) {
/* check the line for fingerprint data */
for (j = 0; j < sizeof(lines[i].sum2); j++) {
if (lines[i].sum2[j] > (VFS301_FP_SUM_MEDIAN + VFS301_FP_SUM_EMPTY_RANGE))
rv = 0;
}
}
return rv;
}
#endif
static int scanline_diff(const unsigned char *scanlines, int prev, int cur)
{
const unsigned char *line1 = scanlines + prev * VFS301_FP_OUTPUT_WIDTH;
const unsigned char *line2 = scanlines + cur * VFS301_FP_OUTPUT_WIDTH;
int i;
int diff;
#ifdef OUTPUT_RAW
/* We only need the image, not the surrounding stuff. */
line1 = ((vfs301_line_t*)line1)->scan;
line2 = ((vfs301_line_t*)line2)->scan;
#endif
/* TODO: This doesn't work too well when there are parallel lines in the
* fingerprint. */
for (diff = 0, i = 0; i < VFS301_FP_WIDTH; i++) {
if (*line1 > *line2)
diff += *line1 - *line2;
else
diff += *line2 - *line1;
line1++;
line2++;
}
return ((diff / VFS301_FP_WIDTH) > VFS301_FP_LINE_DIFF_THRESHOLD);
}
/** Transform the input data to a normalized fingerprint scan */
void vfs301_extract_image(
vfs301_dev_t *vfs, unsigned char *output, int *output_height
)
{
const unsigned char *scanlines = vfs->scanline_buf;
int last_line;
int i;
assert(vfs->scanline_count >= 1);
*output_height = 1;
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
last_line = 0;
/* The following algorithm is quite trivial - it just picks lines that
* differ more than VFS301_FP_LINE_DIFF_THRESHOLD.
* TODO: A nicer approach would be to pick those lines and then do some kind
* of bi/tri-linear resampling to get the output (so that we don't get so
* many false edges etc.).
*/
for (i = 1; i < vfs->scanline_count; i++) {
if (scanline_diff(scanlines, last_line, i)) {
memcpy(
output + VFS301_FP_OUTPUT_WIDTH * (*output_height),
scanlines + VFS301_FP_OUTPUT_WIDTH * i,
VFS301_FP_OUTPUT_WIDTH
);
last_line = i;
(*output_height)++;
}
}
}
static int img_process_data(
int first_block, vfs301_dev_t *dev, const unsigned char *buf, int len
)
{
vfs301_line_t *lines = (vfs301_line_t*)buf;
int no_lines = len / sizeof(vfs301_line_t);
int i;
/*int no_nonempty;*/
unsigned char *cur_line;
int last_img_height;
#ifdef SCAN_FINISH_DETECTION
int finished_scan;
#endif
if (first_block) {
last_img_height = 0;
dev->scanline_count = no_lines;
} else {
last_img_height = dev->scanline_count;
dev->scanline_count += no_lines;
}
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
assert(dev->scanline_buf != NULL);
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
i < no_lines;
i++, cur_line += VFS301_FP_OUTPUT_WIDTH
) {
#ifndef OUTPUT_RAW
memcpy(cur_line, lines[i].scan, VFS301_FP_OUTPUT_WIDTH);
#else
memcpy(cur_line, &lines[i], VFS301_FP_OUTPUT_WIDTH);
#endif
}
#ifdef SCAN_FINISH_DETECTION
finished_scan = img_is_finished_scan(lines, no_lines);
return !finished_scan;
#else /* SCAN_FINISH_DETECTION */
return 1; /* Just continue until data is coming */
#endif
}
/************************** PROTOCOL STUFF ************************************/
static unsigned char usb_send_buf[0x2000];
#define USB_RECV(from, len) \
usb_recv(dev, devh, from, len)
#define USB_SEND(type, subtype) \
{ \
int len; \
vfs301_proto_generate(type, subtype, usb_send_buf, &len); \
usb_send(devh, usb_send_buf, len); \
}
#define RAW_DATA(x) x, sizeof(x)
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
{
int i;
const unsigned char *buf = dev->recv_buf;
int len = dev->recv_len;
if (first_block) {
assert(len >= VFS301_FP_FRAME_SIZE);
/* Skip bytes until start_sequence is found */
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--) {
if (IS_VFS301_FP_SEQ_START(buf))
break;
}
}
return img_process_data(first_block, dev, buf, len);
}
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x0220, 0xFA00);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 000000000000 */
}
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
USB_SEND(0x17, -1);
assert(USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7) == 0);
if (memcmp(dev->recv_buf, no_event, sizeof(no_event)) == 0) {
return 0;
} else if (memcmp(dev->recv_buf, got_event, sizeof(no_event)) == 0) {
return 1;
} else {
assert(!"unexpected reply to wait");
}
}
#define VARIABLE_ORDER(a, b) \
{ \
int _rv = a;\
b; \
if (_rv == -7) \
a; \
}
static void vfs301_proto_process_event_cb(struct libusb_transfer *transfer)
{
vfs301_dev_t *dev = transfer->user_data;
struct libusb_device_handle *devh = transfer->dev_handle;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
dev->recv_progress = VFS301_FAILURE;
goto end;
} else if (transfer->actual_length < dev->recv_exp_amt) {
/* TODO: process the data anyway? */
dev->recv_progress = VFS301_ENDED;
goto end;
} else {
dev->recv_len = transfer->actual_length;
if (!vfs301_proto_process_data(dev->recv_exp_amt == VFS301_FP_RECV_LEN_1, dev)) {
dev->recv_progress = VFS301_ENDED;
goto end;
}
dev->recv_exp_amt = VFS301_FP_RECV_LEN_2;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
printf("cb::continue fail\n");
dev->recv_progress = VFS301_FAILURE;
goto end;
}
return;
}
end:
libusb_free_transfer(transfer);
}
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
struct libusb_transfer *transfer;
/*
* Notes:
*
* seen next_scan order:
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
* o 5E01 !?
* o FA00
* o FA00
* o 2C01
* o FA00
* o FA00
* o 2C01
*/
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 64);
/* now read the fingerprint data, while there are some */
transfer = libusb_alloc_transfer(0);
if (!transfer) {
dev->recv_progress = VFS301_FAILURE;
return;
}
dev->recv_progress = VFS301_ONGOING;
dev->recv_exp_amt = VFS301_FP_RECV_LEN_1;
libusb_fill_bulk_transfer(
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
dev->recv_buf, dev->recv_exp_amt,
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
if (libusb_submit_transfer(transfer) < 0) {
libusb_free_transfer(transfer);
dev->recv_progress = VFS301_FAILURE;
return;
}
}
int /* vfs301_dev_t::recv_progress */ vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
if (dev->recv_progress != VFS301_ENDED)
return dev->recv_progress;
/* Finish the scan process... */
USB_SEND(0x04, -1);
/* the following may come in random order, data may not come at all, don't
* try for too long... */
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 1204 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 16384)
);
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760), /* seems to always come */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2) /* 0000 */
);
return dev->recv_progress;
}
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0B, 0x04);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 6); /* 000000000000 */
USB_SEND(0x0B, 0x05);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7); /* 00000000000000 */
USB_SEND(0x19, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 64);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 4); /* 6BB4D0BC */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 256);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 32);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_3));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x02D0, 1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 11648); /* 56 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 2);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 53248); /* 2 * 128 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 19968); /* 96 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 4);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5824); /* 28 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 5);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 6);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
USB_SEND(0x02D0, 7);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 832);
usb_send(devh, RAW_DATA(vfs301_12));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_2));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x0220, 2);
VARIABLE_ORDER(
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 0000 */
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760)
);
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_1));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x1A, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_06_4));
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
usb_send(devh, RAW_DATA(vfs301_24)); /* turns on white */
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
USB_SEND(0x01, -1);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
USB_SEND(0x0220, 3);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2368);
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 36);
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760);
}
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev)
{
}

View File

@@ -0,0 +1,137 @@
/*
* vfs301/vfs300 fingerprint reader driver
* https://github.com/andree182/vfs301
*
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
*
* 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
*/
#include <libusb-1.0/libusb.h>
enum {
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
VFS301_SEND_ENDPOINT = 0x01,
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
};
#define VFS301_FP_RECV_LEN_1 (84032)
#define VFS301_FP_RECV_LEN_2 (84096)
typedef struct {
/* buffer for received data */
unsigned char recv_buf[0x20000];
int recv_len;
/* buffer to hold raw scanlines */
unsigned char *scanline_buf;
int scanline_count;
enum {
VFS301_ONGOING = 0,
VFS301_ENDED = 1,
VFS301_FAILURE = -1
} recv_progress;
int recv_exp_amt;
} vfs301_dev_t;
enum {
/* Width of the scanned data in px */
VFS301_FP_WIDTH = 200,
/* sizeof(fp_line_t) */
VFS301_FP_FRAME_SIZE = 288,
/* Width of output line */
#ifndef OUTPUT_RAW
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
#else
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
#endif
VFS301_FP_SUM_LINES = 3,
#ifdef SCAN_FINISH_DETECTION
/* TODO: The following changes (seen ~60 and ~80) In that
* case we'll need to calibrate this from empty data somehow... */
VFS301_FP_SUM_MEDIAN = 60,
VFS301_FP_SUM_EMPTY_RANGE = 5,
#endif
/* Minimum average difference between returned lines */
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
/* Maximum waiting time for a single fingerprint frame */
VFS301_FP_RECV_TIMEOUT = 2000
};
/* Arrays of this structure is returned during the initialization as a response
* to the 0x02D0 messages.
* It seems to be always the same - what is it for? Some kind of confirmation?
*/
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi; /* FIXME ? */
unsigned char flags[3];
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
} vfs301_init_line_t;
typedef struct {
unsigned char sync_0x01;
unsigned char sync_0xfe;
unsigned char counter_lo;
unsigned char counter_hi;
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
unsigned char flag_1;
unsigned char sync_0x00;
unsigned char scan[VFS301_FP_WIDTH];
/* A offseted, stretched, inverted copy of scan... probably could
* serve finger motion speed detection?
* Seems to be subdivided to some 10B + 53B + 1B blocks */
unsigned char mirror[64];
/* Some kind of sum of the scan, very low contrast */
unsigned char sum1[2];
unsigned char sum2[11];
unsigned char sum3[3];
} vfs301_line_t;
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_request_fingerprint(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
/** returns 0 if no event is ready, or 1 if there is one... */
int vfs301_proto_peek_event(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_proto_process_event_start(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
int vfs301_proto_process_event_poll(
struct libusb_device_handle *devh, vfs301_dev_t *dev);
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);

File diff suppressed because it is too large Load Diff

1020
libfprint/drivers/vfs5011.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,15 +28,17 @@
#include <fprint.h>
#define array_n_elements(array) (sizeof(array) / sizeof(array[0]))
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
enum fpi_log_level {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR,
FPRINT_LOG_LEVEL_DEBUG,
FPRINT_LOG_LEVEL_INFO,
FPRINT_LOG_LEVEL_WARNING,
FPRINT_LOG_LEVEL_ERROR,
};
void fpi_log(enum fpi_log_level, const char *component, const char *function,
@@ -53,14 +55,14 @@ void fpi_log(enum fpi_log_level, const char *component, const char *function,
#endif
#ifdef ENABLE_DEBUG_LOGGING
#define fp_dbg(fmt...) _fpi_log(LOG_LEVEL_DEBUG, fmt)
#define fp_dbg(fmt...) _fpi_log(FPRINT_LOG_LEVEL_DEBUG, fmt)
#else
#define fp_dbg(fmt...)
#endif
#define fp_info(fmt...) _fpi_log(LOG_LEVEL_INFO, fmt)
#define fp_warn(fmt...) _fpi_log(LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(LOG_LEVEL_ERROR, fmt)
#define fp_info(fmt...) _fpi_log(FPRINT_LOG_LEVEL_INFO, fmt)
#define fp_warn(fmt...) _fpi_log(FPRINT_LOG_LEVEL_WARNING, fmt)
#define fp_err(fmt...) _fpi_log(FPRINT_LOG_LEVEL_ERROR, fmt)
#ifndef NDEBUG
#define BUG_ON(condition) \
@@ -89,6 +91,10 @@ enum fp_dev_state {
DEV_STATE_IDENTIFYING,
DEV_STATE_IDENTIFY_DONE,
DEV_STATE_IDENTIFY_STOPPING,
DEV_STATE_CAPTURE_STARTING,
DEV_STATE_CAPTURING,
DEV_STATE_CAPTURE_DONE,
DEV_STATE_CAPTURE_STOPPING,
};
struct fp_driver **fprint_get_drivers (void);
@@ -106,8 +112,8 @@ struct fp_dev {
/* drivers should not mess with any of the below */
enum fp_dev_state state;
int __enroll_stage;
int unconditional_capture;
/* async I/O callbacks and data */
/* FIXME: convert this to generic state operational data mechanism? */
@@ -127,6 +133,10 @@ struct fp_dev {
void *identify_cb_data;
fp_identify_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
fp_capture_cb capture_cb;
void *capture_cb_data;
fp_capture_stop_cb capture_stop_cb;
void *capture_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
@@ -144,6 +154,7 @@ enum fp_imgdev_action {
IMG_ACTION_ENROLL,
IMG_ACTION_VERIFY,
IMG_ACTION_IDENTIFY,
IMG_ACTION_CAPTURE,
};
enum fp_imgdev_enroll_state {
@@ -158,7 +169,7 @@ enum fp_imgdev_enroll_state {
enum fp_imgdev_verify_state {
IMG_VERIFY_STATE_NONE = 0,
IMG_VERIFY_STATE_ACTIVATING
IMG_VERIFY_STATE_ACTIVATING
};
struct fp_img_dev {
@@ -168,7 +179,9 @@ struct fp_img_dev {
int action_state;
struct fp_print_data *acquire_data;
struct fp_print_data *enroll_data;
struct fp_img *acquire_img;
int enroll_stage;
int action_result;
/* FIXME: better place to put this? */
@@ -177,8 +190,6 @@ struct fp_img_dev {
void *priv;
};
int fpi_imgdev_capture(struct fp_img_dev *imgdev, int unconditional,
struct fp_img **image);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
@@ -213,6 +224,8 @@ struct fp_driver {
int (*verify_stop)(struct fp_dev *dev, gboolean iterating);
int (*identify_start)(struct fp_dev *dev);
int (*identify_stop)(struct fp_dev *dev, gboolean iterating);
int (*capture_start)(struct fp_dev *dev);
int (*capture_stop)(struct fp_dev *dev);
};
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
@@ -253,9 +266,21 @@ extern struct fp_img_driver uru4000_driver;
#ifdef ENABLE_AES1610
extern struct fp_img_driver aes1610_driver;
#endif
#ifdef ENABLE_AES1660
extern struct fp_img_driver aes1660_driver;
#endif
#ifdef ENABLE_AES2501
extern struct fp_img_driver aes2501_driver;
#endif
#ifdef ENABLE_AES2550
extern struct fp_img_driver aes2550_driver;
#endif
#ifdef ENABLE_AES2660
extern struct fp_img_driver aes2660_driver;
#endif
#ifdef ENABLE_AES3500
extern struct fp_img_driver aes3500_driver;
#endif
#ifdef ENABLE_AES4000
extern struct fp_img_driver aes4000_driver;
#endif
@@ -265,6 +290,21 @@ extern struct fp_img_driver fdu2000_driver;
#ifdef ENABLE_VCOM5S
extern struct fp_img_driver vcom5s_driver;
#endif
#ifdef ENABLE_VFS101
extern struct fp_img_driver vfs101_driver;
#endif
#ifdef ENABLE_VFS301
extern struct fp_img_driver vfs301_driver;
#endif
#ifdef ENABLE_VFS5011
extern struct fp_img_driver vfs5011_driver;
#endif
#ifdef ENABLE_UPEKTC_IMG
extern struct fp_img_driver upektc_img_driver;
#endif
#ifdef ENABLE_ETES603
extern struct fp_img_driver etes603_driver;
#endif
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
@@ -293,15 +333,19 @@ enum fp_print_data_type {
PRINT_DATA_NBIS_MINUTIAE,
};
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
struct fp_print_data_item {
size_t length;
unsigned char data[0];
};
struct fpi_print_data_fp1 {
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
GSList *prints;
};
struct fpi_print_data_fp2 {
char prefix[3];
uint16_t driver_id;
uint32_t devtype;
@@ -309,8 +353,14 @@ struct fpi_print_data_fp1 {
unsigned char data[0];
} __attribute__((__packed__));
struct fpi_print_data_item_fp2 {
uint32_t length;
unsigned char data[0];
} __attribute__((__packed__));
void fpi_data_exit(void);
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length);
struct fp_print_data *fpi_print_data_new(struct fp_dev *dev);
struct fp_print_data_item *fpi_print_data_item_new(size_t length);
gboolean fpi_print_data_compatible(uint16_t driver_id1, uint32_t devtype1,
enum fp_print_data_type type1, uint16_t driver_id2, uint32_t devtype2,
enum fp_print_data_type type2);
@@ -322,8 +372,8 @@ struct fp_minutiae {
};
/* bit values for fp_img.flags */
#define FP_IMG_V_FLIPPED (1<<0)
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_V_FLIPPED (1<<0)
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
@@ -351,7 +401,7 @@ int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print);
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor);
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor);
/* polling and timeouts */
@@ -419,6 +469,11 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img);
void fpi_drvcb_identify_stopped(struct fp_dev *dev);
void fpi_drvcb_capture_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_capture_result(struct fp_dev *dev, int result,
struct fp_img *img);
void fpi_drvcb_capture_stopped(struct fp_dev *dev);
/* for image drivers */
void fpi_imgdev_open_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_close_complete(struct fp_img_dev *imgdev);

View File

@@ -1,81 +0,0 @@
/*
* Helper binary for creating a HAL FDI file for supported devices
* Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
* Copyright (C) 2008 Timo Hoenig <thoenig@suse.de>, <thoenig@nouse.net>
*
* 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
*/
#include <config.h>
#include <stdio.h>
#include "fp_internal.h"
/* FDI entry example:
*
* <!-- AuthenTec AES2501 -->
* <match key="usb.vendor_id" int="0x08ff">
* <match key="usb.product_id" int="0x2580">
* <merge key="info.category" type="string">biometric.fingerprint_reader</merge>
* <append key="biometric.fingerprint_reader.access_method" type="strlist">libfprint</append>
* <append key="info.capabilities" type="strlist">biometric</append>
* <append key="info.capabilities" type="strlist">biometric.fingerprint_reader</append>
* <merge key="biometric.fingerprint_reader.libfprint.driver" type="string">aes2501</merge>
* <merge key="biometric.fingerprint_reader.libfprint.support" type="bool">true</merge>
* </match>
* </match>
*
*/
static void print_driver (struct fp_driver *driver)
{
int i;
for (i = 0; driver->id_table[i].vendor != 0; i++) {
printf (" <!-- %s -->\n", fp_driver_get_full_name (driver));
printf (" <match key=\"usb.vendor_id\" int=\"0x%04x\">\n", driver->id_table[i].vendor);
printf (" <match key=\"usb.product_id\" int=\"0x%04x\">\n", driver->id_table[i].product);
printf (" <merge key=\"info.category\" type=\"string\">biometric.fingerprint_reader</merge>\n");
printf (" <append key=\"biometric.fingerprint_reader.access_method\" type=\"strlist\">libfprint</append>\n");
printf (" <append key=\"info.capabilities\" type=\"strlist\">biometric</append>\n");
printf (" <append key=\"info.capabilities\" type=\"strlist\">biometric.fingerprint_reader</append>\n");
printf (" <merge key=\"biometric.fingerprint_reader.libfprint.driver\" type=\"string\">%s</merge>\n", driver->name);
printf (" <merge key=\"biometric.fingerprint_reader.libfprint.support\" type=\"bool\">true</merge>\n");
printf (" <append key=\"biometric.fingerprint_reader.scan_type\" type=\"string\">%s</append>\n",
fp_driver_get_scan_type (driver) == FP_SCAN_TYPE_PRESS ? "press" : "swipe");
printf (" </match>\n");
printf (" </match>\n");
}
}
int main (int argc, char **argv)
{
struct fp_driver **list;
guint i;
list = fprint_get_drivers ();
printf ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
printf ("<!-- Created from libfprint %s -->\n", VERSION);
printf ("<deviceinfo version=\"0.2\">\n");
for (i = 0; list[i] != NULL; i++) {
print_driver (list[i]);
}
printf ("</deviceinfo>\n");
return 0;
}

View File

@@ -30,30 +30,56 @@ static const struct usb_id whitelist_id_table[] = {
static const struct usb_id blacklist_id_table[] = {
{ .vendor = 0x0483, .product = 0x2016 },
/* https://bugs.freedesktop.org/show_bug.cgi?id=66659 */
{ .vendor = 0x045e, .product = 0x00bb },
{ 0, 0, 0 },
};
struct fp_driver whitelist = {
.id_table = whitelist_id_table,
.full_name = "Hardcoded whitelist"
};
GHashTable *printed = NULL;
static void print_driver (struct fp_driver *driver)
{
int i, j, blacklist;
int i, j, blacklist, num_printed;
num_printed = 0;
for (i = 0; driver->id_table[i].vendor != 0; i++) {
char *key;
blacklist = 0;
for (j = 0; blacklist_id_table[j].vendor != 0; j++) {
if (driver->id_table[i].vendor == blacklist_id_table[j].vendor &&
driver->id_table[j].product == blacklist_id_table[j].product) {
driver->id_table[i].product == blacklist_id_table[j].product) {
blacklist = 1;
break;
}
}
if (blacklist)
continue;
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", ATTR{power/level}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
key = g_strdup_printf ("%04x:%04x", driver->id_table[i].vendor, driver->id_table[i].product);
if (g_hash_table_lookup (printed, key) != NULL) {
g_free (key);
continue;
}
g_hash_table_insert (printed, key, GINT_TO_POINTER (1));
if (num_printed == 0)
printf ("# %s\n", driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", TEST==\"power/control\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
num_printed++;
}
if (num_printed > 0)
printf ("\n");
}
int main (int argc, char **argv)
@@ -63,11 +89,15 @@ int main (int argc, char **argv)
list = fprint_get_drivers ();
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
for (i = 0; list[i] != NULL; i++) {
print_driver (list[i]);
}
print_driver (&whitelist);
g_hash_table_destroy (printed);
return 0;
}

View File

@@ -20,6 +20,10 @@
#ifndef __FPRINT_H__
#define __FPRINT_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <sys/time.h>
@@ -103,6 +107,17 @@ uint32_t fp_dev_get_devtype(struct fp_dev *dev);
int fp_dev_supports_print_data(struct fp_dev *dev, struct fp_print_data *data);
int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print);
/** \ingroup dev
* Image capture result codes returned from fp_dev_img_capture().
*/
enum fp_capture_result {
/** Capture completed successfully, the capture data has been
* returned to the caller. */
FP_CAPTURE_COMPLETE = 0,
/** Capture failed for some reason */
FP_CAPTURE_FAIL,
};
int fp_dev_supports_imaging(struct fp_dev *dev);
int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **image);
@@ -336,5 +351,16 @@ typedef void (*fp_identify_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_identify_stop(struct fp_dev *dev, fp_identify_stop_cb callback,
void *user_data);
typedef void (*fp_capture_cb)(struct fp_dev *dev, int result,
struct fp_img *img, void *user_data);
int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_capture_cb callback, void *user_data);
typedef void (*fp_capture_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_capture_stop(struct fp_dev *dev, fp_capture_stop_cb callback, void *user_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,88 +0,0 @@
/*
* Imaging utility functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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
*/
#include <errno.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "fp_internal.h"
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor)
{
int new_width = img->width * factor;
int new_height = img->height * factor;
GdkPixbuf *orig, *resized;
struct fp_img *newimg;
guchar *pixels;
guint y;
int rowstride;
g_type_init ();
/* It is possible to implement resizing using a simple algorithm, however
* we use gdk-pixbuf because it applies some kind of smoothing to the
* result, which improves matching performances in my experiments. */
/* Create the original pixbuf, and fill it in from the grayscale data */
orig = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
FALSE,
8,
img->width,
img->height);
rowstride = gdk_pixbuf_get_rowstride (orig);
pixels = gdk_pixbuf_get_pixels (orig);
for (y = 0; y < img->height; y++) {
guint x;
for (x = 0; x < img->width; x++) {
guchar *p, *r;
p = pixels + y * rowstride + x * 3;
r = img->data + y * img->width + x;
p[0] = r[0];
p[1] = r[0];
p[2] = r[0];
}
}
/* Resize the pixbuf, and create the new fp_img */
resized = gdk_pixbuf_scale_simple (orig, new_width, new_height, GDK_INTERP_HYPER);
g_object_unref (orig);
newimg = fpi_img_new(new_width * new_height);
newimg->width = new_width;
newimg->height = new_height;
newimg->flags = img->flags;
rowstride = gdk_pixbuf_get_rowstride (resized);
pixels = gdk_pixbuf_get_pixels (resized);
for (y = 0; y < newimg->height; y++) {
guint x;
for (x = 0; x < newimg->width; x++) {
guchar *p, *r;
r = img->data + y * img->width + x;
p = pixels + y * rowstride + x * 3;
r[0] = p[0];
}
}
g_object_unref (resized);
return newimg;
}

View File

@@ -1,67 +0,0 @@
/*
* Imaging utility functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
*
* 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
*/
#include <errno.h>
#include <magick/ImageMagick.h>
#include "fp_internal.h"
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int factor)
{
Image *mimg;
Image *resized;
ExceptionInfo exception;
MagickBooleanType ret;
int new_width = img->width * factor;
int new_height = img->height * factor;
struct fp_img *newimg;
/* It is possible to implement resizing using a simple algorithm, however
* we use ImageMagick because it applies some kind of smoothing to the
* result, which improves matching performances in my experiments. */
if (!IsMagickInstantiated())
InitializeMagick(NULL);
GetExceptionInfo(&exception);
mimg = ConstituteImage(img->width, img->height, "I", CharPixel, img->data,
&exception);
GetExceptionInfo(&exception);
resized = ResizeImage(mimg, new_width, new_height, 0, 1.0, &exception);
newimg = fpi_img_new(new_width * new_height);
newimg->width = new_width;
newimg->height = new_height;
newimg->flags = img->flags;
GetExceptionInfo(&exception);
ret = ExportImagePixels(resized, 0, 0, new_width, new_height, "I",
CharPixel, newimg->data, &exception);
if (ret != MagickTrue) {
fp_err("export failed");
return NULL;
}
DestroyImage(mimg);
DestroyImage(resized);
return newimg;
}

View File

@@ -47,8 +47,7 @@
struct fp_img *fpi_img_new(size_t length)
{
struct fp_img *img = g_malloc(sizeof(*img) + length);
memset(img, 0, sizeof(*img));
struct fp_img *img = g_malloc0(sizeof(*img) + length);
fp_dbg("length=%zd", length);
img->length = length;
return img;
@@ -290,7 +289,7 @@ int fpi_img_detect_minutiae(struct fp_img *img)
&low_contrast_map, &low_flow_map, &high_curve_map,
&map_w, &map_h, &bdata, &bw, &bh, &bd,
img->data, img->width, img->height, 8,
DEFAULT_PPI / (double)25.4, &lfsparms_V2);
DEFAULT_PPI / (double)25.4, &g_lfsparms_V2);
g_timer_stop(timer);
fp_dbg("minutiae scan completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
@@ -314,6 +313,7 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret)
{
struct fp_print_data *print;
struct fp_print_data_item *item;
int r;
if (!img->minutiae) {
@@ -328,9 +328,11 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
/* FIXME: space is wasted if we dont hit the max minutiae count. would
* be good to make this dynamic. */
print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct));
print = fpi_print_data_new(imgdev->dev);
item = fpi_print_data_item_new(sizeof(struct xyt_struct));
print->type = PRINT_DATA_NBIS_MINUTIAE;
minutiae_to_xyt(img->minutiae, img->width, img->height, print->data);
minutiae_to_xyt(img->minutiae, img->width, img->height, item->data);
print->prints = g_slist_prepend(print->prints, item);
/* FIXME: the print buffer at this point is endian-specific, and will
* only work when loaded onto machines with identical endianness. not good!
@@ -343,42 +345,73 @@ int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
struct fp_print_data *new_print)
{
struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->data;
struct xyt_struct *pstruct = (struct xyt_struct *) new_print->data;
GTimer *timer;
int r;
int score, max_score = 0, probe_len;
struct xyt_struct *pstruct = NULL;
struct xyt_struct *gstruct = NULL;
struct fp_print_data_item *data_item;
GSList *list_item;
if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE ||
new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
new_print->type != PRINT_DATA_NBIS_MINUTIAE) {
fp_err("invalid print format");
return -EINVAL;
}
timer = g_timer_new();
r = bozorth_main(pstruct, gstruct);
g_timer_stop(timer);
fp_dbg("bozorth processing took %f seconds, score=%d",
g_timer_elapsed(timer, NULL), r);
g_timer_destroy(timer);
if (g_slist_length(new_print->prints) != 1) {
fp_err("new_print contains more than one sample, is it enrolled print?");
return -EINVAL;
}
return r;
data_item = new_print->prints->data;
pstruct = (struct xyt_struct *)data_item->data;
probe_len = bozorth_probe_init(pstruct);
list_item = enrolled_print->prints;
do {
data_item = list_item->data;
gstruct = (struct xyt_struct *)data_item->data;
score = bozorth_to_gallery(probe_len, pstruct, gstruct);
fp_dbg("score %d", score);
max_score = max(score, max_score);
list_item = g_slist_next(list_item);
} while (list_item);
return max_score;
}
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
struct fp_print_data **gallery, int match_threshold, size_t *match_offset)
{
struct xyt_struct *pstruct = (struct xyt_struct *) print->data;
struct xyt_struct *pstruct;
struct xyt_struct *gstruct;
struct fp_print_data *gallery_print;
int probe_len = bozorth_probe_init(pstruct);
struct fp_print_data_item *data_item;
int probe_len;
size_t i = 0;
int r;
GSList *list_item;
if (g_slist_length(print->prints) != 1) {
fp_err("new_print contains more than one sample, is it enrolled print?");
return -EINVAL;
}
data_item = print->prints->data;
pstruct = (struct xyt_struct *)data_item->data;
probe_len = bozorth_probe_init(pstruct);
while ((gallery_print = gallery[i++])) {
struct xyt_struct *gstruct = (struct xyt_struct *) gallery_print->data;
int r = bozorth_to_gallery(probe_len, pstruct, gstruct);
if (r >= match_threshold) {
*match_offset = i - 1;
return FP_VERIFY_MATCH;
}
list_item = gallery_print->prints;
do {
data_item = list_item->data;
gstruct = (struct xyt_struct *)data_item->data;
r = bozorth_to_gallery(probe_len, pstruct, gstruct);
if (r >= match_threshold) {
*match_offset = i - 1;
return FP_VERIFY_MATCH;
}
list_item = g_slist_next(list_item);
} while (list_item);
}
return FP_VERIFY_NO_MATCH;
}

View File

@@ -25,6 +25,7 @@
#define MIN_ACCEPTABLE_MINUTIAE 10
#define BOZORTH3_DEFAULT_THRESHOLD 40
#define IMG_ENROLL_STAGES 5
static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
{
@@ -33,8 +34,9 @@ static int img_dev_open(struct fp_dev *dev, unsigned long driver_data)
int r = 0;
imgdev->dev = dev;
imgdev->enroll_stage = 0;
dev->priv = imgdev;
dev->nr_enroll_stages = 1;
dev->nr_enroll_stages = IMG_ENROLL_STAGES;
/* for consistency in driver code, allow udev access through imgdev */
imgdev->udev = dev->udev;
@@ -144,8 +146,17 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
switch (imgdev->action) {
case IMG_ACTION_ENROLL:
fp_dbg("reporting enroll result");
fpi_drvcb_enroll_stage_completed(imgdev->dev, r, data, img);
if (r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
data = imgdev->enroll_data;
if (r == FP_ENROLL_COMPLETE) {
imgdev->enroll_data = NULL;
}
fpi_drvcb_enroll_stage_completed(imgdev->dev, r,
r == FP_ENROLL_COMPLETE ? data : NULL,
img);
/* the callback can cancel enrollment, so recheck current
* action and the status to see if retry is needed */
if (imgdev->action == IMG_ACTION_ENROLL &&
r > 0 && r != FP_ENROLL_COMPLETE && r != FP_ENROLL_FAIL) {
imgdev->action_result = 0;
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_ON;
dev_change_state(imgdev, IMG_ACQUIRE_STATE_AWAIT_FINGER_ON);
@@ -160,6 +171,9 @@ void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
imgdev->identify_match_offset, img);
fp_print_data_free(data);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_report_capture_result(imgdev->dev, r, img);
break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -228,20 +242,41 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
fp_img_standardize(img);
imgdev->acquire_img = img;
fpi_img_to_print_data(imgdev, img, &print);
if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
MIN_ACCEPTABLE_MINUTIAE);
fp_print_data_free(print);
/* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
imgdev->action_result = FP_ENROLL_RETRY;
goto next_state;
if (imgdev->action != IMG_ACTION_CAPTURE) {
r = fpi_img_to_print_data(imgdev, img, &print);
if (r < 0) {
fp_dbg("image to print data conversion error: %d", r);
imgdev->action_result = FP_ENROLL_RETRY;
goto next_state;
} else if (img->minutiae->num < MIN_ACCEPTABLE_MINUTIAE) {
fp_dbg("not enough minutiae, %d/%d", img->minutiae->num,
MIN_ACCEPTABLE_MINUTIAE);
fp_print_data_free(print);
/* depends on FP_ENROLL_RETRY == FP_VERIFY_RETRY */
imgdev->action_result = FP_ENROLL_RETRY;
goto next_state;
}
}
imgdev->acquire_data = print;
switch (imgdev->action) {
case IMG_ACTION_ENROLL:
imgdev->action_result = FP_ENROLL_COMPLETE;
if (!imgdev->enroll_data) {
imgdev->enroll_data = fpi_print_data_new(imgdev->dev);
}
BUG_ON(g_slist_length(print->prints) != 1);
/* Move print data from acquire data into enroll_data */
imgdev->enroll_data->prints =
g_slist_prepend(imgdev->enroll_data->prints, print->prints->data);
print->prints = g_slist_remove(print->prints, print->prints->data);
fp_print_data_free(imgdev->acquire_data);
imgdev->acquire_data = NULL;
imgdev->enroll_stage++;
if (imgdev->enroll_stage == imgdev->dev->nr_enroll_stages)
imgdev->action_result = FP_ENROLL_COMPLETE;
else
imgdev->action_result = FP_ENROLL_PASS;
break;
case IMG_ACTION_VERIFY:
verify_process_img(imgdev);
@@ -249,6 +284,9 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
case IMG_ACTION_IDENTIFY:
identify_process_img(imgdev);
break;
case IMG_ACTION_CAPTURE:
imgdev->action_result = FP_CAPTURE_COMPLETE;
break;
default:
BUG();
break;
@@ -273,6 +311,9 @@ void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_report_identify_result(imgdev->dev, error, 0, NULL);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_report_capture_result(imgdev->dev, error, NULL);
break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -293,6 +334,9 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_identify_started(imgdev->dev, status);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_capture_started(imgdev->dev, status);
break;
default:
fp_err("unhandled action %d", imgdev->action);
return;
@@ -318,6 +362,9 @@ void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
case IMG_ACTION_IDENTIFY:
fpi_drvcb_identify_stopped(imgdev->dev);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_capture_stopped(imgdev->dev);
break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -366,7 +413,7 @@ static void dev_deactivate(struct fp_img_dev *imgdev)
struct fp_driver *drv = imgdev->dev->drv;
struct fp_img_driver *imgdrv = fpi_driver_to_img_driver(drv);
if (!imgdrv->activate)
if (!imgdrv->deactivate)
return;
return imgdrv->deactivate(imgdev);
}
@@ -378,6 +425,7 @@ static int generic_acquire_start(struct fp_dev *dev, int action)
fp_dbg("action %d", action);
imgdev->action = action;
imgdev->action_state = IMG_ACQUIRE_STATE_ACTIVATING;
imgdev->enroll_stage = 0;
r = dev_activate(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
if (r < 0)
@@ -393,8 +441,10 @@ static void generic_acquire_stop(struct fp_img_dev *imgdev)
dev_deactivate(imgdev);
fp_print_data_free(imgdev->acquire_data);
fp_print_data_free(imgdev->enroll_data);
fp_img_free(imgdev->acquire_img);
imgdev->acquire_data = NULL;
imgdev->enroll_data = NULL;
imgdev->acquire_img = NULL;
imgdev->action_result = 0;
}
@@ -414,6 +464,14 @@ static int img_dev_identify_start(struct fp_dev *dev)
return generic_acquire_start(dev, IMG_ACTION_IDENTIFY);
}
static int img_dev_capture_start(struct fp_dev *dev)
{
/* Unconditional capture is not supported yet */
if (dev->unconditional_capture)
return -ENOTSUP;
return generic_acquire_start(dev, IMG_ACTION_CAPTURE);
}
static int img_dev_enroll_stop(struct fp_dev *dev)
{
struct fp_img_dev *imgdev = dev->priv;
@@ -439,6 +497,14 @@ static int img_dev_identify_stop(struct fp_dev *dev, gboolean iterating)
return 0;
}
static int img_dev_capture_stop(struct fp_dev *dev)
{
struct fp_img_dev *imgdev = dev->priv;
BUG_ON(imgdev->action != IMG_ACTION_CAPTURE);
generic_acquire_stop(imgdev);
return 0;
}
void fpi_img_driver_setup(struct fp_img_driver *idriver)
{
idriver->driver.type = DRIVER_IMAGING;
@@ -450,5 +516,7 @@ void fpi_img_driver_setup(struct fp_img_driver *idriver)
idriver->driver.verify_stop = img_dev_verify_stop;
idriver->driver.identify_start = img_dev_identify_start;
idriver->driver.identify_stop = img_dev_identify_stop;
idriver->driver.capture_start = img_dev_capture_start;
idriver->driver.capture_stop = img_dev_capture_stop;
}

View File

@@ -1021,12 +1021,12 @@ extern int closest_dir_dist(const int, const int, const int);
/*************************************************************************/
/* EXTERNAL GLOBAL VARIABLE DEFINITIONS */
/*************************************************************************/
extern double dft_coefs[];
extern LFSPARMS lfsparms;
extern LFSPARMS lfsparms_V2;
extern int nbr8_dx[];
extern int nbr8_dy[];
extern int chaincodes_nbr8[];
extern FEATURE_PATTERN feature_patterns[];
extern double g_dft_coefs[];
extern LFSPARMS g_lfsparms;
extern LFSPARMS g_lfsparms_V2;
extern int g_nbr8_dx[];
extern int g_nbr8_dy[];
extern int g_chaincodes_nbr8[];
extern FEATURE_PATTERN g_feature_patterns[];
#endif

View File

@@ -36,10 +36,10 @@ identified are necessarily the best available for the purpose.
#define LOG_FILE "log.txt"
#endif
extern FILE *logfp;
extern int avrdir;
extern float dir_strength;
extern int nvalid;
extern FILE *g_logfp;
extern int g_avrdir;
extern float g_dir_strength;
extern int g_nvalid;
extern int open_logfile(void);
extern int close_logfile(void);

View File

@@ -86,7 +86,7 @@ int block_offsets(int **optr, int *ow, int *oh,
int *blkoffs, bx, by, bw, bh, bi, bsize;
int blkrow_start, blkrow_size, offset;
int lastbw, lastbh;
int pad2, pw, ph;
int pad2, pw;
/* Test if unpadded image is smaller than a single block */
if((iw < blocksize) || (ih < blocksize)){
@@ -99,7 +99,6 @@ int block_offsets(int **optr, int *ow, int *oh,
/* Compute padded width and height of image */
pad2 = pad<<1;
pw = iw + pad2;
ph = ih + pad2;
/* Compute the number of columns and rows of blocks in the image. */
/* Take the ceiling to account for "leftovers" at the right and */

View File

@@ -742,8 +742,8 @@ static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
/* Set current scan pixel to the new neighbor. */
/* REMEMBER: the neighbors are being scanned around the original */
/* feature point. */
cur_nbr_x = cur_x_loc + nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + nbr8_dy[nbr_i];
cur_nbr_x = cur_x_loc + g_nbr8_dx[nbr_i];
cur_nbr_y = cur_y_loc + g_nbr8_dy[nbr_i];
/* If new neighbor is not within image boundaries... */
if((cur_nbr_x < 0) || (cur_nbr_x >= iw) ||
@@ -766,8 +766,8 @@ static int next_contour_pixel(int *next_x_loc, int *next_y_loc,
if(nbr_i % 2){
/* To do this, look ahead one more neighbor pixel. */
ni = next_scan_nbr(nbr_i, scan_clock);
nx = cur_x_loc + nbr8_dx[ni];
ny = cur_y_loc + nbr8_dy[ni];
nx = cur_x_loc + g_nbr8_dx[ni];
ny = cur_y_loc + g_nbr8_dy[ni];
/* If new neighbor is not within image boundaries... */
if((nx < 0) || (nx >= iw) ||
(ny < 0) || (ny >= ih))

View File

@@ -122,7 +122,7 @@ static int lfs_detect_minutiae_V2(MINUTIAE **ominutiae,
/* Initialize wave form lookup tables for DFT analyses. */
/* used for direction binarization. */
if((ret = init_dftwaves(&dftwaves, dft_coefs, lfsparms->num_dft_waves,
if((ret = init_dftwaves(&dftwaves, g_dft_coefs, lfsparms->num_dft_waves,
lfsparms->windowsize))){
/* Free memory allocated to this point. */
free_dir2rad(dir2rad);
@@ -385,12 +385,12 @@ int get_minutiae(MINUTIAE **ominutiae, int **oquality_map,
const int id, const double ppmm, const LFSPARMS *lfsparms)
{
int ret;
MINUTIAE *minutiae;
int *direction_map, *low_contrast_map, *low_flow_map;
int *high_curve_map, *quality_map;
int map_w, map_h;
unsigned char *bdata;
int bw, bh;
MINUTIAE *minutiae = NULL;
int *direction_map = NULL, *low_contrast_map = NULL, *low_flow_map = NULL;
int *high_curve_map = NULL, *quality_map = NULL;
int map_w = 0, map_h = 0;
unsigned char *bdata = NULL;
int bw = 0, bh = 0;
/* If input image is not 8-bit grayscale ... */
if(id != 8){

View File

@@ -40,7 +40,7 @@ identified are necessarily the best available for the purpose.
/*************************************************************************/
#ifdef LOG_REPORT
FILE *logfp;
FILE *g_logfp;
#endif
/* Constants (C) for defining 4 DFT frequencies, where */
@@ -50,10 +50,10 @@ FILE *logfp;
/* 2 = twice the frequency in range X. */
/* 3 = three times the frequency in reange X. */
/* 4 = four times the frequency in ranage X. */
double dft_coefs[NUM_DFT_WAVES] = { 1,2,3,4 };
double g_dft_coefs[NUM_DFT_WAVES] = { 1,2,3,4 };
/* Allocate and initialize a global LFS parameters structure. */
LFSPARMS lfsparms = {
LFSPARMS g_lfsparms = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
@@ -137,7 +137,7 @@ LFSPARMS lfsparms = {
/* Allocate and initialize VERSION 2 global LFS parameters structure. */
LFSPARMS lfsparms_V2 = {
LFSPARMS g_lfsparms_V2 = {
/* Image Controls */
PAD_VALUE,
JOIN_LINE_RADIUS,
@@ -221,17 +221,17 @@ LFSPARMS lfsparms_V2 = {
/* Variables for conducting 8-connected neighbor analyses. */
/* Pixel neighbor offsets: 0 1 2 3 4 5 6 7 */ /* 7 0 1 */
int nbr8_dx[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; /* 6 C 2 */
int nbr8_dy[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; /* 5 4 3 */
int g_nbr8_dx[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; /* 6 C 2 */
int g_nbr8_dy[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; /* 5 4 3 */
/* The chain code lookup matrix for 8-connected neighbors. */
/* Should put this in globals. */
int chaincodes_nbr8[]={ 3, 2, 1,
int g_chaincodes_nbr8[]={ 3, 2, 1,
4,-1, 0,
5, 6, 7};
/* Global array of feature pixel pairs. */
FEATURE_PATTERN feature_patterns[]=
FEATURE_PATTERN g_feature_patterns[]=
{{RIDGE_ENDING, /* a. Ridge Ending (appearing) */
APPEARING,
{0,0},

View File

@@ -110,14 +110,14 @@ static int chain_code_loop(int **ochain, int *onchain,
/* Derive chain code index from neighbor deltas. */
/* The deltas are on the range [-1..1], so to use them as indices */
/* into the code list, they must first be incremented by one. */
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
chain[i] = *(g_chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
}
/* Now derive chain code between last and first points in the */
/* contour list. */
dx = contour_x[0] - contour_x[i];
dy = contour_y[0] - contour_y[i];
chain[i] = *(chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
chain[i] = *(g_chaincodes_nbr8+((dy+1)*NBR8_DIM)+dx+1);
/* Store results to the output pointers. */
*ochain = chain;
@@ -782,7 +782,6 @@ int process_loop(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int halfway;
int idir, type, appearing;
double min_dist, max_dist;
int min_fr, max_fr, min_to, max_to;
@@ -800,9 +799,6 @@ int process_loop(MINUTIAE *minutiae,
/* Get pixel value of feature's interior. */
feature_pix = *(bdata + (contour_y[0] * iw) + contour_x[0]);
/* Compute half the perimeter of the loop. */
halfway = ncontour>>1;
/* Get the aspect dimensions of the loop in units of */
/* squared distance. */
get_loop_aspect(&min_fr, &min_to, &min_dist,
@@ -940,7 +936,6 @@ int process_loop_V2(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
int *plow_flow_map, const LFSPARMS *lfsparms)
{
int halfway;
int idir, type, appearing;
double min_dist, max_dist;
int min_fr, max_fr, min_to, max_to;
@@ -960,9 +955,6 @@ int process_loop_V2(MINUTIAE *minutiae,
/* Get pixel value of feature's interior. */
feature_pix = *(bdata + (contour_y[0] * iw) + contour_x[0]);
/* Compute half the perimeter of the loop. */
halfway = ncontour>>1;
/* Get the aspect dimensions of the loop in units of */
/* squared distance. */
get_loop_aspect(&min_fr, &min_to, &min_dist,

View File

@@ -307,6 +307,10 @@ int gen_initial_maps(int **odmap, int **olcmap, int **olfmap,
xmaxlimit = pw - dftgrids->pad - lfsparms->windowsize - 1;
ymaxlimit = ph - dftgrids->pad - lfsparms->windowsize - 1;
/* max limits should not be negative */
xmaxlimit = MAX(xmaxlimit, 0);
ymaxlimit = MAX(ymaxlimit, 0);
/* Foreach block in image ... */
for(bi = 0; bi < bsize; bi++){
/* Adjust block offset from pointing to block origin to pointing */

View File

@@ -69,8 +69,8 @@ int match_1st_pair(unsigned char p1, unsigned char p2,
/* Foreach set of feature pairs ... */
for(i = 0; i < NFEATURES; i++){
/* If current scan pair matches first pair for feature ... */
if((p1==feature_patterns[i].first[0]) &&
(p2==feature_patterns[i].first[1])){
if((p1==g_feature_patterns[i].first[0]) &&
(p2==g_feature_patterns[i].first[1])){
/* Store feature as a possible match. */
possible[*nposs] = i;
/* Bump number of stored possibilities. */
@@ -117,8 +117,8 @@ int match_2nd_pair(unsigned char p1, unsigned char p2,
/* Foreach possible match based on first pair ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches second pair for feature ... */
if((p1==feature_patterns[possible[i]].second[0]) &&
(p2==feature_patterns[possible[i]].second[1])){
if((p1==g_feature_patterns[possible[i]].second[0]) &&
(p2==g_feature_patterns[possible[i]].second[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */
@@ -160,8 +160,8 @@ int match_3rd_pair(unsigned char p1, unsigned char p2,
/* Foreach possible match based on first and second pairs ... */
for(i = 0; i < tnposs; i++){
/* If current scan pair matches third pair for feature ... */
if((p1==feature_patterns[possible[i]].third[0]) &&
(p2==feature_patterns[possible[i]].third[1])){
if((p1==g_feature_patterns[possible[i]].third[0]) &&
(p2==g_feature_patterns[possible[i]].third[1])){
/* Store feature as a possible match. */
possible[*nposs] = possible[i];
/* Bump number of stored possibilities. */

View File

@@ -2521,7 +2521,7 @@ int process_horizontal_scan_minutia(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set y location to second scan row. */
y_loc = cy+1;
/* Set y location of neighboring edge pixel to the first scan row. */
@@ -2550,15 +2550,15 @@ int process_horizontal_scan_minutia(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current IMAP value. */
idir = get_low_curvature_direction(SCAN_HORIZONTAL,
feature_patterns[feature_id].appearing,
g_feature_patterns[feature_id].appearing,
imapval, lfsparms->num_directions);
}
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
DEFAULT_RELIABILITY,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2627,7 +2627,7 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set y location to second scan row. */
y_loc = cy+1;
/* Set y location of neighboring edge pixel to the first scan row. */
@@ -2665,7 +2665,7 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current block's direction. */
idir = get_low_curvature_direction(SCAN_HORIZONTAL,
feature_patterns[feature_id].appearing, dmapval,
g_feature_patterns[feature_id].appearing, dmapval,
lfsparms->num_directions);
}
@@ -2680,8 +2680,8 @@ int process_horizontal_scan_minutia_V2(MINUTIAE *minutiae,
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
reliability,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2740,7 +2740,7 @@ int process_vertical_scan_minutia(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set x location to second scan column. */
x_loc = cx+1;
/* Set x location of neighboring edge pixel to the first scan column. */
@@ -2776,15 +2776,15 @@ int process_vertical_scan_minutia(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current IMAP value. */
idir = get_low_curvature_direction(SCAN_VERTICAL,
feature_patterns[feature_id].appearing,
g_feature_patterns[feature_id].appearing,
imapval, lfsparms->num_directions);
}
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
DEFAULT_RELIABILITY,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);
@@ -2845,7 +2845,7 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
/* Feature location should always point to either ending */
/* of ridge or (for bifurcations) ending of valley. */
/* So, if detected feature is APPEARING... */
if(feature_patterns[feature_id].appearing){
if(g_feature_patterns[feature_id].appearing){
/* Set x location to second scan column. */
x_loc = cx+1;
/* Set x location of neighboring edge pixel to the first scan column. */
@@ -2890,7 +2890,7 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
else{
/* Get minutia direction based on current block's direction. */
idir = get_low_curvature_direction(SCAN_VERTICAL,
feature_patterns[feature_id].appearing, dmapval,
g_feature_patterns[feature_id].appearing, dmapval,
lfsparms->num_directions);
}
@@ -2905,8 +2905,8 @@ int process_vertical_scan_minutia_V2(MINUTIAE *minutiae,
/* Create a minutia object based on derived attributes. */
if((ret = create_minutia(&minutia, x_loc, y_loc, x_edge, y_edge, idir,
reliability,
feature_patterns[feature_id].type,
feature_patterns[feature_id].appearing, feature_id)))
g_feature_patterns[feature_id].type,
g_feature_patterns[feature_id].appearing, feature_id)))
/* Return system error. */
return(ret);

View File

@@ -565,7 +565,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
MINUTIA *minutia1, *minutia2;
int i, ret, found;
int *xlist, *ylist, num;
int ridge_count, ridge_start, ridge_end;
int ridge_cnt, ridge_start, ridge_end;
int prevpix, curpix;
minutia1 = minutiae->list[first];
@@ -614,7 +614,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
}
/* Ready to count ridges, so initialize counter to 0. */
ridge_count = 0;
ridge_cnt = 0;
print2log("RIDGE COUNT: %d,%d to %d,%d ", minutia1->x, minutia1->y,
minutia2->x, minutia2->y);
@@ -630,7 +630,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
return(ridge_cnt);
}
/* Otherwise, we found a new ridge start transition, so store */
/* its location (the location of the 1 in 0-to-1 transition). */
@@ -647,7 +647,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return number of ridges counted to this point. */
return(ridge_count);
return(ridge_cnt);
}
/* Otherwise, we found a new ridge end transition, so store */
/* its location (the location of the 0 in 1-to-0 transition). */
@@ -680,7 +680,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
if(ret){
/* Then assume we have found a valid ridge crossing and bump */
/* the ridge counter. */
ridge_count++;
ridge_cnt++;
}
/* Otherwise, ignore the current ridge start and end transitions */
@@ -694,7 +694,7 @@ static int ridge_count(const int first, const int second, MINUTIAE *minutiae,
print2log("\n");
/* Return the number of ridges counted. */
return(ridge_count);
return(ridge_cnt);
}
/*************************************************************************
@@ -719,7 +719,7 @@ static int count_minutia_ridges(const int first, MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, ret, *nbr_list, *nbr_nridges, nnbrs;
int i, ret, *nbr_list = NULL, *nbr_nridges, nnbrs;
/* Find up to the maximum number of qualifying neighbors. */
if((ret = find_neighbors(&nbr_list, &nnbrs, lfsparms->max_nbrs,

63
libfprint/pixman.c Normal file
View File

@@ -0,0 +1,63 @@
/*
* Imaging utility functions for libfprint
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2013 Vasily Khoruzhick <anarsoul@gmail.com>
*
* 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
*/
#include <pixman.h>
#include <string.h>
#include "fp_internal.h"
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor)
{
int new_width = img->width * w_factor;
int new_height = img->height * h_factor;
pixman_image_t *orig, *resized;
pixman_transform_t transform;
struct fp_img *newimg;
orig = pixman_image_create_bits(PIXMAN_a8, img->width, img->height, (uint32_t *)img->data, img->width);
resized = pixman_image_create_bits(PIXMAN_a8, new_width, new_height, NULL, new_width);
pixman_transform_init_identity(&transform);
pixman_transform_scale(NULL, &transform, pixman_int_to_fixed(w_factor), pixman_int_to_fixed(h_factor));
pixman_image_set_transform(orig, &transform);
pixman_image_set_filter(orig, PIXMAN_FILTER_BILINEAR, NULL, 0);
pixman_image_composite32(PIXMAN_OP_SRC,
orig, /* src */
NULL, /* mask */
resized, /* dst */
0, 0, /* src x y */
0, 0, /* mask x y */
0, 0, /* dst x y */
new_width, new_height /* width height */
);
newimg = fpi_img_new(new_width * new_height);
newimg->width = new_width;
newimg->height = new_height;
newimg->flags = img->flags;
memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
pixman_image_unref(orig);
pixman_image_unref(resized);
return newimg;
}

View File

@@ -270,6 +270,14 @@ API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
if (r_fprint == 0 && r_libusb == 0)
return 0;
/* if fprint have no pending timeouts return libusb timeout */
else if (r_fprint == 0)
*tv = libusb_timeout;
/* if libusb have no pending timeouts return fprint timeout */
else if (r_libusb == 0)
*tv = fprint_timeout;
/* otherwise return the smaller of the 2 timeouts */
else if (timercmp(&fprint_timeout, &libusb_timeout, <))
*tv = fprint_timeout;

View File

@@ -320,7 +320,6 @@ static void verify_stop_cb(struct fp_dev *dev, void *user_data)
API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
struct fp_print_data *enrolled_print, struct fp_img **img)
{
struct fp_driver *drv = dev->drv;
struct sync_verify_data *vdata;
gboolean stopped = FALSE;
int r;
@@ -335,7 +334,7 @@ API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
return -EINVAL;
}
fp_dbg("to be handled by %s", drv->name);
fp_dbg("to be handled by %s", dev->drv->name);
vdata = g_malloc0(sizeof(struct sync_verify_data));
r = fp_async_verify_start(dev, enrolled_print, sync_verify_cb, vdata);
if (r < 0) {
@@ -452,13 +451,12 @@ API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset,
struct fp_img **img)
{
struct fp_driver *drv = dev->drv;
gboolean stopped = FALSE;
struct sync_identify_data *idata
= g_malloc0(sizeof(struct sync_identify_data));
int r;
fp_dbg("to be handled by %s", drv->name);
fp_dbg("to be handled by %s", dev->drv->name);
r = fp_async_identify_start(dev, print_gallery, sync_identify_cb, idata);
if (r < 0) {
@@ -514,3 +512,100 @@ err:
return r;
}
struct sync_capture_data {
gboolean populated;
int result;
struct fp_img *img;
};
static void sync_capture_cb(struct fp_dev *dev, int result, struct fp_img *img,
void *user_data)
{
struct sync_capture_data *vdata = user_data;
vdata->result = result;
vdata->img = img;
vdata->populated = TRUE;
}
static void capture_stop_cb(struct fp_dev *dev, void *user_data)
{
gboolean *stopped = user_data;
fp_dbg("");
*stopped = TRUE;
}
/** \ingroup dev
* Captures an \ref img "image" from a device. The returned image is the raw
* image provided by the device, you may wish to \ref img_std "standardize" it.
*
* If set, the <tt>unconditional</tt> flag indicates that the device should
* capture an image unconditionally, regardless of whether a finger is there
* or not. If unset, this function will block until a finger is detected on
* the sensor.
*
* \param dev the device
* \param unconditional whether to unconditionally capture an image, or to only capture when a finger is detected
* \param img a location to return the captured image. Must be freed with
* fp_img_free() after use.
* \return 0 on success, non-zero on error. -ENOTSUP indicates that either the
* unconditional flag was set but the device does not support this, or that the
* device does not support imaging.
* \sa fp_dev_supports_imaging()
*/
API_EXPORTED int fp_dev_img_capture(struct fp_dev *dev, int unconditional,
struct fp_img **img)
{
struct sync_capture_data *vdata;
gboolean stopped = FALSE;
int r;
if (!dev->drv->capture_start) {
fp_dbg("image capture is not supported on %s device", dev->drv->name);
return -ENOTSUP;
}
fp_dbg("to be handled by %s", dev->drv->name);
vdata = g_malloc0(sizeof(struct sync_capture_data));
r = fp_async_capture_start(dev, unconditional, sync_capture_cb, vdata);
if (r < 0) {
fp_dbg("capture_start error %d", r);
g_free(vdata);
return r;
}
while (!vdata->populated) {
r = fp_handle_events();
if (r < 0) {
g_free(vdata);
goto err;
}
}
if (img)
*img = vdata->img;
else
fp_img_free(vdata->img);
r = vdata->result;
g_free(vdata);
switch (r) {
case FP_CAPTURE_COMPLETE:
fp_dbg("result: complete");
break;
case FP_CAPTURE_FAIL:
fp_dbg("result: fail");
break;
default:
fp_err("unrecognised return code %d", r);
r = -EINVAL;
}
err:
fp_dbg("ending capture");
if (fp_async_capture_stop(dev, capture_stop_cb, &stopped) == 0)
while (!stopped)
if (fp_handle_events() < 0)
break;
return r;
}