Compare commits

...

162 Commits

Author SHA1 Message Date
Bastien Nocera
74b5c92787 0.8.0 2018-06-12 15:42:22 +02:00
Bastien Nocera
ac1f97e2eb lib: Supported devices list is for master, not stable 2018-06-12 12:46:19 +02:00
Bastien Nocera
40f486b108 HACKING: Update hacking file 2018-06-12 11:06:26 +02:00
Bastien Nocera
b62e67401c lib: Fix supported devices page title again
We need to call setlocale() so we run in UTF-8 mode, and can print that
sweet sweet em dash.
2018-06-08 16:23:35 +02:00
Bastien Nocera
dd0a0134a6 lib: Fix supported devices page title 2018-06-07 17:46:08 +02:00
Bastien Nocera
5e24000799 build: Remove unused meson rule
We're generating the supported-devices.md file in the website CI now.
2018-06-07 17:39:36 +02:00
Bastien Nocera
c5cdfcb120 lib: Sort supported devices list 2018-06-07 17:39:04 +02:00
Bastien Nocera
549a6694d2 build: Disable supported-devices.md generation for now
As added in e5393bf46a

It fails currently.
2018-06-04 16:13:08 +02:00
Bastien Nocera
14e34e1d15 build: Remove ported Makefile.am snippet 2018-06-03 14:41:02 +02:00
Bastien Nocera
e5393bf46a lib: Add script to print MarkDown page of supported devices 2018-06-03 14:40:34 +02:00
Bastien Nocera
878a201bb1 README: Update links to new website 2018-06-03 00:31:05 +02:00
Bastien Nocera
b0e4619e0a docs: Update links to new website 2018-06-03 00:31:05 +02:00
Bastien Nocera
aec65777a7 nbis: Update links to new website 2018-06-03 00:30:10 +02:00
Bastien Nocera
31bad8ddd2 build: Fix fprint.h install destination
Since the port to meson, fprint.h was installing to $includedir instead
of $includedir/libfprint/
2018-05-31 14:35:16 +02:00
Bastien Nocera
79d65c907f build: Add missing X11 deps for the examples 2018-05-31 12:30:48 +02:00
Bastien Nocera
92231d984f build: Add some missing build essentials 2018-05-31 12:23:30 +02:00
Bastien Nocera
2fbc77955e build: Add CI
Barebones, just compiles libfprint.
2018-05-31 12:18:17 +02:00
Bastien Nocera
c91819f551 lib: Remove drv->close absence support in fp_async_dev_close()
The driver will at least need to close its hardware resources, and
free memory, so it must have had one.

This case was never actually used as can be seen from the fact that
we would assert in fpi_drvcb_close_complete() if the state was wrong
but never set it to the expected value.
2018-05-31 11:06:31 +02:00
Bastien Nocera
b3f6ff5a36 lib: Add guard to async functions
To avoid having NULL devices being passed dereferenced.
2018-05-31 11:06:31 +02:00
Bastien Nocera
52f84bee3c vfs5011: Error out when no lines were captured
Which avoids passing zero lines to fpi_assemble_lines()

"gmem.c:130: failed to allocate 18446744073709551612 bytes"

 #3  0x00007fe4f6ef428f in g_log (log_domain=log_domain@entry=0x7fe4f6f3506e "GLib", log_level=log_level@entry=G_LOG_LEVEL_ERROR, format=format@entry=0x7fe4f6f3e610 "%s: failed to allocate %lu bytes") at gmessages.c:1398
 #4  0x00007fe4f6ef2ac4 in g_malloc0 (n_bytes=n_bytes@entry=18446744073709551612) at gmem.c:129
 #5  0x00007fe4f8052020 in median_filter (filtersize=25, size=-1, data=0x0) at assembling.c:309
 #6  fpi_assemble_lines (ctx=ctx@entry=0x7fe4f82ac3c0 <assembling_ctx>, lines=0x0, lines_len=0) at assembling.c:389
 #7  0x00007fe4f805f3db in submit_image (ssm=ssm@entry=0x16c3cba360, data=data@entry=0x16c3cb9cc0) at drivers/vfs5011.c:412

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

Closes: #42
2018-05-31 11:08:41 +02:00
Bastien Nocera
dda6857fee assembling: Add guards to fpi_assemble_lines()
With the goal of not crashing when we try to malloc MAXINT bytes of RAM.

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

Closes: #42
2018-05-31 11:06:31 +02:00
Timur Celik
0215483fb3 assembling: Fix assembling of last frame in reverse mode
The last image is always misplaced because the sign of the delta
vector isn't corrected.

This could result in false positives and verification failing.

https://bugs.freedesktop.org/show_bug.cgi?id=105027
2018-05-30 11:17:08 +02:00
Timur Celik
37bb59df13 assembling: Fix assembling of frames for non-reverse stripes
Every frame stores the delta from the previous frame, in reverse mode
it stores the delta to the next frame. This causes images to use the
wrong delta while assembling in forward mode.

The broken assembling in forward mode will create a small error for
linear motion, because the delta of all frames is approximately the
same in this case. But if you move your finger, stop and then continue
moving in a single scan, the misplaced frames should be visible in
the assembled output.

This could result in false positives and verification failing.

https://bugs.freedesktop.org/show_bug.cgi?id=105027
2018-05-30 11:15:17 +02:00
Bastien Nocera
db34837d2d lib: Simplify device discovery
Use GPtrArray instead of open-coding a NULL terminated array.

https://bugs.freedesktop.org/show_bug.cgi?id=106279
2018-05-29 13:45:27 +02:00
Bastien Nocera
ba49677794 vfs0050: Rename "udev" to "usb_dev"
To reduce confusion with possible future udev usage.
2018-05-29 13:37:19 +02:00
Bastien Nocera
1a376c1bfa lib: Remove 2 more functions from the drivers API 2018-05-29 13:34:18 +02:00
Bastien Nocera
ef807d9d0e lib: s/array_n_elements/G_N_ELEMENTS/ 2018-05-29 13:34:18 +02:00
Bastien Nocera
2a4893d946 lib: fp_imgdev_*() functions are only used in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
cee061b363 lib: fpi_ssm_start_subsm() is only used in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
1bbdf304ab lib: Remove never defined fpi_ssm_has_completed() 2018-05-29 13:34:18 +02:00
Bastien Nocera
6155068f9e lib: Split off some fp_img related functions
Some are internal, some are for drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
0c4e3bb1c4 lib: Remove drivers definitions from drivers API 2018-05-29 13:34:18 +02:00
Bastien Nocera
4547ff0c19 lib: Remove a number of data types from drivers API
They're unused in drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
b7cce4d91d lib: Make fp_minutiae opaque
And fp_minutia private.
2018-05-29 13:34:18 +02:00
Bastien Nocera
475250ce71 lib: Split off some polling related functions 2018-05-29 13:34:18 +02:00
Bastien Nocera
f40f231a63 lib: Make fp_img_dev structure opaque 2018-05-29 13:34:18 +02:00
Bastien Nocera
d83d92adf2 lib: Make fp_dev structure opaque 2018-05-29 13:34:18 +02:00
Bastien Nocera
d15282bff1 lib: Remove minutiae utils from internal header
It's only used by drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
00637c4f0b lib: Remove fprint_get_drivers() from drivers API
Drivers don't need to get a list of drivers.
2018-05-29 13:34:18 +02:00
Bastien Nocera
2e035a7f45 lib: Make fpi_ssm opaque for drivers
We shouldn't access fpi_ssm struct fields directly in drivers, so add
accessor and setter functions for the common uses.
2018-05-29 13:34:18 +02:00
Bastien Nocera
17577175f8 lib: Remove fpi_driver_to_img_driver() from drivers API
It's only used internally to the library.
2018-05-29 13:34:18 +02:00
Bastien Nocera
933fab6ed4 lib: Remove array_n_elements() from internal header
It's unused.
2018-05-29 13:34:18 +02:00
Bastien Nocera
7e5661caff lib: Don't include assembling.h in internal header
It's not needed.
2018-05-29 13:34:18 +02:00
Bastien Nocera
19dfb138a6 drivers: Use new drivers_api.h in drivers 2018-05-29 13:34:18 +02:00
Bastien Nocera
0930f2614c lib: Use public header in helper files
aeslib.h and assembling.h don't need access to internal data structures,
so make them include the public fprint.h rather than the internal header
fp_internal.h.
2018-05-29 13:34:18 +02:00
Bastien Nocera
1b20521e5c lib: Add drivers_api.h file
This will hide library internals, for the purpose of making the drivers
API documentable.
2018-05-29 13:34:18 +02:00
Bastien Nocera
0e351db91a lib: Fix new BUG_ON() implementation again
This time, the macro didn't wrap the condition, resulting in calls
like BUG_ON(size > 100) being expanded to g_assert(!size > 100), when
what we wanted was BUG_ON(!(size > 100)).

See 9cca501650,
ff09456cf5, and egg on my face.
2018-05-25 17:38:40 +02:00
Bastien Nocera
9cca501650 lib: Fix new BUG_ON() implementation
A thinko reversed the meaning of the BUG_ON() condition. This fixes
the inverted implementation in commit
ff09456cf5.
2018-05-25 16:12:29 +02:00
Bastien Nocera
2481cbe4ab poll: Add some details about how mainloop integration works 2018-05-25 15:58:29 +02:00
Bastien Nocera
bc3959d1e0 lib: Fix internal deprecation warning
Remove deprecation warning that comes from an internal function, it
serves no purpose, and the function itself is deprecated.

Follow-up from commit 1f0079a274.
2018-05-25 13:45:13 +02:00
Bastien Nocera
1ac815e457 examples: Fix integer overflow warnings 2018-05-25 13:40:33 +02:00
Bastien Nocera
55b83062d0 img: Fix fd leaks
Spotted by maddin200@aol.com

https://bugs.freedesktop.org/show_bug.cgi?id=90197
2018-05-25 13:30:35 +02:00
Mark Harfouche
391f77ce5e aes1610: Fix compilation warning in aes1610
Fixes:
drivers/aes1610.c:736:34: warning: ‘stop_reader’ defined but not used [-Wunused-const-variable=]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-05-25 13:21:17 +02:00
Bastien Nocera
bc30a3d2e5 aes2501: Fix state machine never using "init_3" state
This fixes this warning by the same token:
drivers/aes2501.c:671:34: warning: ‘init_3’ defined but not used [-Wunused-const-variable=]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-05-25 13:21:17 +02:00
Bastien Nocera
ff5de4ff03 lib: Replace open-coded array_n_elements()
Replace it with glib's version, which already exists.

https://bugs.freedesktop.org/show_bug.cgi?id=106280
2018-05-25 13:20:34 +02:00
Bastien Nocera
f433a4d67c upekts: Fix compilation warning
libfprint/drivers/upekts.c: In function ‘alloc_send_cmd_transfer’:
libfprint/drivers/upekts.c:161:2: warning: ‘strncpy’ output truncated before terminating nul copying 4 bytes from a string of the same length [-Wstringop-truncation]
  strncpy(buf, "Ciao", 4);
  ^~~~~~~~~~~~~~~~~~~~~~~

Replace with memcpy() to only copy the 4 bytes we need.

https://bugs.freedesktop.org/show_bug.cgi?id=106281
2018-05-25 13:19:31 +02:00
Bastien Nocera
994061af44 drivers: Simplify headers
Remove all the headers already included through "fp_internal.h" such as
<libusb.h> and <errno.h>, include "assembling.h" and "driver_ids.h" there
as well to avoid doing it in (almost) every driver.
2018-05-24 17:02:15 +02:00
Bastien Nocera
501020921e vfs301_proto: Use GLib helpers
No need to roll out own min() implementation, or use the bare assert().
2018-05-24 17:00:17 +02:00
Bastien Nocera
53c09405b2 etes603: Use GLib's g_assert()
Instead of requiring assert.h to be included.
2018-05-24 16:57:14 +02:00
Bastien Nocera
1f0079a274 data: Mark all the fp_dscv_print functions as deprecated
As this is pretty much copy/pasted in fprintd, and should instead be
implemented by whatever system actually stores the data, rather than
in a generic but not quite "fits-all-purpose" way.

https://bugs.freedesktop.org/show_bug.cgi?id=106550
2018-05-24 13:21:40 +02:00
Bastien Nocera
15afe43cf0 examples: Replace deprecated fp_set_debug() 2018-05-24 12:43:53 +02:00
Bastien Nocera
eb8f7ba3b2 lib: Mark fp_set_debug() as deprecated
As per the documentation.
2018-05-24 12:42:18 +02:00
Bastien Nocera
901a6f7fed lib: Add macro for deprecated functions 2018-05-24 12:41:35 +02:00
Bastien Nocera
fc92f62136 build: Remove the need to modify sources for new drivers
Instead of having to modify both fp_internal.h to list each driver
definition structure, and core.c to add those drivers to arrays we
can loop over, generate both of those using meson.
2018-05-24 12:23:39 +02:00
Bastien Nocera
ff09456cf5 lib: Use g_assert* to implement BUG() and BUG_ON() assertions 2018-05-23 19:14:33 +02:00
Bastien Nocera
32fcfde86b lib: Use GLib and libusb directly for debug output
Use GLib internally to output debug information, and tell about
libusb's LIBUSB_DEBUG envvar for libusb debug.

fp_set_debug() is now a no-op.

https://bugs.freedesktop.org/show_bug.cgi?id=106552
2018-05-23 19:13:08 +02:00
Bastien Nocera
363a1b3371 lib: Replace empty fp_dbg() calls
GLib won't like them, so use G_DEBUG_HERE() instead.
2018-05-23 19:11:07 +02:00
Bastien Nocera
c376c6fb02 lib: Fix type mismatch warnings in debug output 2018-05-23 19:11:05 +02:00
Bastien Nocera
8e6e23b8d0 build: Always enable debugging logging
We shouldn't need to specifically enable debug logging to get useful
data out of a compiled libfprint either, so always enable debugging
output. It will still be switched off at runtime, by default.
2018-05-23 19:11:02 +02:00
Bastien Nocera
63e5d56441 build: Always allow switching log level at runtime
There are no parts of libfprint that are so resource intensive that we'd
want to disable logging. This avoids (hopefully rare) cases where
compiled versions of libfprint are distributed with logging completely
disabled, and thus can't be debugged.
2018-05-23 19:10:58 +02:00
Bastien Nocera
e9bfd943fc docs: Mark a few FP_VERIFY_MATCH as constant
Add "%" prefix to mark it as a constant in the docs.
2018-05-23 15:18:34 +02:00
Bastien Nocera
614e2286c2 docs: Fix typo in fp_enroll_finger_img() API docs
Missing ":" after argument name.
2018-05-23 15:18:34 +02:00
Bastien Nocera
768a74c4bf docs: Remove transfer information from fp_dev_open()
There's no GObject usage in the public API.
2018-05-23 15:18:34 +02:00
Bastien Nocera
612e9e11de docs: Document async function callbacks 2018-05-23 15:18:34 +02:00
Bastien Nocera
06c72d54be poll: Add missing API docs for polling functions 2018-05-23 15:18:34 +02:00
Bastien Nocera
be68bacc94 lib: Merge two other async callback types
Merge fp_capture_cb and fp_verify_cb.

https://bugs.freedesktop.org/show_bug.cgi?id=106551
2018-05-23 15:18:34 +02:00
Bastien Nocera
317d7bc988 lib: Simplify fp_*_stop_cb callback definitions
They're all the same, so merge them into a single fp_operation_stop_cb.

https://bugs.freedesktop.org/show_bug.cgi?id=106551
2018-05-23 13:39:10 +02:00
Bastien Nocera
b44e417bca docs: Fix '\sa' doxygen references 2018-05-23 13:39:10 +02:00
Bastien Nocera
9d67ce484d lib: Make inline functions real functions
Otherwise they will not be parsed by gtk-doc and documented.
2018-05-23 13:39:10 +02:00
Bastien Nocera
78b8602cf6 lib: Make fp_minutia an opaque structure
Nothing uses the elements of the structure, so make it opaque.
2018-05-23 13:39:10 +02:00
Bastien Nocera
83a0a7681b img: Make fpi_img_detect_minutiae() static
It's unused outside img.c, so mark it as static.
2018-05-23 13:39:10 +02:00
Bastien Nocera
c5e0e41ce7 docs: Add documentation for opaque structures 2018-05-23 13:39:00 +02:00
Bastien Nocera
b3fe4a1e91 docs: Update API documentation
Fixes to layout, dead links, typography, and more.

Thanks to Benjamin Berg <bberg@redhat.com> for the thorough review
2018-05-18 05:51:58 +02:00
Bastien Nocera
f59bf389d9 INSTALL: Update for Meson 2018-05-18 01:16:30 +02:00
Bastien Nocera
231b8f9f92 doc: Port from Doxygen to gtk-doc
Split the introduction into separate chapters, add filler documentation
for async functions, fix mismatched function arguments.
2018-05-17 17:39:51 +02:00
Bastien Nocera
dac153d24a build: Port to meson
And remove the autotools. Faster, cleaner.

https://bugs.freedesktop.org/show_bug.cgi?id=106514
2018-05-15 10:46:54 +02:00
Bastien Nocera
3661d146a7 drivers: Remove UPEKE2 driver
The device is already handled by upektc_img driver, and its sources
still exist in git if needed.

https://bugs.freedesktop.org/show_bug.cgi?id=106514
2018-05-15 10:46:54 +02:00
Bastien Nocera
3bf55a3e07 examples: Fix compile-time warning
Fix "function declaration isn’t a prototype" warning
2018-05-14 17:35:51 +02:00
Bastien Nocera
5226467fc2 build: Make NSS (and URU4000) driver optional
In case NSS isn't available.

https://bugs.freedesktop.org/show_bug.cgi?id=106278
2018-05-11 11:13:25 +02:00
Mark Harfouche
54deaa1b24 mindtct: Fix compilation warnings
nbis/mindtct/morph.c:152:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:176:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:200:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]
nbis/mindtct/morph.c:222:4: warning: this ‘if’ clause does not guard... [-Wmisleading-indentation]

https://bugs.freedesktop.org/show_bug.cgi?id=105429
2018-03-11 22:43:04 +01:00
Mark Harfouche
7ff667f58d examples: Fix memory leaked by device discovery
https://bugs.freedesktop.org/show_bug.cgi?id=105427
2018-03-11 13:46:00 +01:00
Mark Harfouche
58ba9b02ed lib: Fix memory leak patch in device discovery
libusb_free_device_list() needs to be called on the list of USB devices
obtained through libusb_get_device_list() or the list and its elements
will be leaked.

https://bugs.freedesktop.org/show_bug.cgi?id=105427
2018-03-11 13:43:58 +01:00
Hans de Goede
d35da0ce99 fprint-list-udev-rules: Add some unsupported Validity Sensors readers to the whitelist 2017-12-12 10:51:43 +01:00
Hans de Goede
041e6a1078 fprint-list-udev-rules: Add some unsupported Elantech readers to the whitelist 2017-12-12 10:51:43 +01:00
Hans de Goede
1c73c36ed7 fprint-list-udev-rules: Remove white-list entries which have a driver now
Remove 2 entries from the whitelist for finger-print models for which
we have a driver now, so they no longer need to be on the whitelist.
2017-12-12 10:51:43 +01:00
Anton Eliasson
69de32f700 vfs5011: Don't submit an image if there was an error capturing it 2017-12-03 13:12:53 -08:00
Alan Davidson
4bfee76ead Fix security hole: zero out memory when allocated 2017-12-03 13:11:48 -08:00
Vasily Khoruzhick
4d7afd9b4f uru4k: increase threshold to detect encryption
Otherwise we get false positives on devices without encryption
2017-12-03 13:10:13 -08:00
Igor Filatov
cfe60c0640 Add Elan driver 2017-12-03 21:17:26 +02:00
Vasily Khoruzhick
d82847a6b4 0.7.0 2017-05-14 09:26:30 -07:00
Vasily Khoruzhick
5a7e6e07ff etes603: fix compilation error caused by a0bbbd7d3 2016-11-07 08:10:44 -08:00
Vasily Khoruzhick
2162aa9f48 uru4k: decrypt image if necessary
Add naive detection of image encryption: if stddev between two
adjacent rows is higher than threshold assume that image is encrypted
and decrypt it.

Fixes fd.o bug 88945
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
a1f36c71c9 upeksonly: decrease bz3_threshold for 147e:1001
Its width is only 216 pixels, and it appears not to be enough for matching at
default threshold.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
e4eedef27e upeksonly: use fpi_std_sq_dev() and fpi_mean_sq_diff_norm()
Use fpi_mean_sq_diff_norm() for blank line detection and
fpi_mean_sq_diff_norm() for duplicate line detection.
Fixes finger presence and removal detection.
2016-11-05 20:58:18 -07:00
Vasily Khoruzhick
5e29695969 lib: move some functions from vfs5011.c into img.c 2016-11-05 20:58:18 -07:00
Konstantin Semenov
49a46668ad lib: Add VFS0050 driver
New driver for 138a:0050 device

https://bugs.freedesktop.org/show_bug.cgi?id=91616
2016-11-05 20:58:18 -07:00
Bastien Nocera
76269decdd aes4000: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.

https://bugzilla.gnome.org/show_bug.cgi?id=98594
2016-11-05 00:13:51 +01:00
Bastien Nocera
8454a25ecf aes3500: Fix possible crash if USB init fails
The code was indented, but only the first call was actually in the
conditional, which meant that initialisation carried on as normal on top
of a failed USB device. Exit early and remove the conditional to fix
this.
2016-11-05 00:11:30 +01:00
Bastien Nocera
a0bbbd7d32 drivers: Print USB error when libusb_claim_interface() fails
https://bugs.freedesktop.org/show_bug.cgi?id=98594
2016-11-05 00:10:17 +01:00
Bastien Nocera
12f6dae8cd build: Fix 9570c36 on Debian
Debian's ash doesn't implement pushd/popd.
2016-11-03 14:05:46 +01:00
Bastien Nocera
9570c36fd4 build: Fix running autogen.sh out-of-tree 2016-07-19 11:18:58 +02:00
Bastien Nocera
487dae0d2f Work-around kernel's lack of USB PM
The device is already supported by the vfs5011 driver and non-
blacklisted, so will show up in the udev rules, but for ease of
backporting to older versions, add it to the whitelist anyway.

https://bugzilla.redhat.com/show_bug.cgi?id=1173367
2016-04-12 16:39:39 +02:00
Vasily Khoruzhick
d71018bd8f vfs5011: add USB IDs of device found in some Toshiba laptops 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
61fa57b05d aes1610: decrease bz3_threshold to 20 2015-12-16 13:24:39 -08:00
Volkau Siarhei
fc1781e317 vfs101: fix broken enrolling by prev commit to vfs101.c 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
ffef6c2bcc vfs5011: add proper dev_deactivate() support
Driver is not able to cancel imaging process without this change
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
67d29f7936 aes1610: improve gain settings 2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
9437c98d54 lib: frame assembling: flip image for non-reverse direction
It was here for aes2501 and aes1610 before moving assembling routines
into common code.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
f7d00a828d lib: use normalized error to make decision about movement direction
Height is not a good determinant in movement direction, normalized
error is much better. Should fix aes1610 and aes2501 driver issues.
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
0f0a4b2da6 aes2660: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:39 -08:00
Vasily Khoruzhick
c9cdbaf880 aes2550: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
83f29dad9f aes2501: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
c2a11c5918 aes1610: set partial flag on an image
Set partial flag to remove false minutiae at the perimeter of a scan
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
3746b2ad5c upeksonly: use line assembling routines to account variable swiping speed 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
b51fa446e3 lib: move line assembling routines out of vfs5011 into common code 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6fc5293e83 upeksonly: add support for 147e:1001 device 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
aab3daa28b upeksonly: move regwrite sequences into a header 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
e40f7bd1f7 lib: move frame assembling routines into a separate file and make them usable by non-aes drivers
Frame assembling routines are not aes-specific, so move them into a separate file
and add an accessor for peeking a pixel.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6664f87d8f aes1610: fix memory corruption introduced by a457658f1b 2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
6e8d5cd6a1 vfs5011: do duplicate line search for 30 lines
It seems that on faster devices, the driver can get more duplicate lines.
Without this change scan looks too stretched.

Suggested by: Thomas Rinsma <thomasrinsma@gmail.com>
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9f7e1ecf40 vfs5011: make '5 scans per enroll' work with this driver
Restart scan if core did not ask to terminate it explicitly.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
d9567002e4 imgdev: reset action_result after reporting it
Otherwise we end up in reporting the same result on next iteration.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
a656a4a9f3 upektc_img: Handle scan status reported by sensor properly
Sensor can report "short scan" or "finger not centered" instead of image,
so it's necessary to handle them gracefully.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
2944a35e74 imgdev: Add fpi_imgdev_abort_scan() to abort scan gracefully from within the driver.
Smart sensors like Upek TouchChip Coprocessor can provide scan status instead
of image, as result we need to report such status as "short scan" or "finger not centered"
from within the driver, since it's not a session error.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
391373fb0c upektc_img: Fix copy/paste error
Replace upekts_img with upektc_img
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
985e8c4577 aes1660: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 25,
since for wrong finger score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
bd0d4258e4 upektc_img: set partial flag on an image
Sensor width seems to be only 128 pixels and that's not enough
for scanning whole finger surface. Lower bz3_threshold to 20,
since for wrong fingerprint score never goes above 10, but sometimes
for right finger score is below 40.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
9bbd9b208a lib: add partial image flag
And activate perimeter points removal if this flag is set.
2015-12-16 13:24:38 -08:00
Vasily Khoruzhick
59fe0fb699 nbis: add one more step of false minutia removal
Remove minutiae that are located on scan perimeter
2015-12-16 13:24:38 -08:00
Bastien Nocera
f1fdd71613 rules: Add driver name to the USB properties
Makes it easier to see which driver is being used.
2015-03-15 23:13:08 +01:00
Bastien Nocera
bb66780cb5 build: Document why some drivers are disabled
This avoids confusion when looking at the build logs
2015-03-15 23:10:03 +01:00
Carlos Olmedo Escobar
45fb6d7908 vfs101: Logical conjunction always evaluates to false. 2015-02-09 12:47:22 +01:00
Timo Teräs
9f408bf51b imgdev: fix enum mismatch for dev_change_state() call
This bug has existed long time, but it was uncovered by commit
e215b05094 which enabled multiple enrollment rounds.

In practice this broke (at least) URU4000 driver state machine
causing it to enter indefinite loop - due to the invalid state
change callback.

Patch originally posted at:
http://lists.freedesktop.org/archives/fprint/2014-June/000603.html

Test and verification results:
http://lists.freedesktop.org/archives/fprint/2014-June/000607.html

Signed-off-by: Timo Teräs <timo.teras@iki.fi>
2015-02-03 18:26:33 +01:00
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
93 changed files with 17256 additions and 6165 deletions

20
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,20 @@
image: fedora:rawhide
stages:
- build
variables:
DEPENDENCIES: libusb1-devel glib2-devel nss-devel pixman-devel systemd meson gtk-doc
gcc gcc-c++ glibc-devel libX11-devel libXv-devel
before_script:
- dnf update -y --nogpgcheck && dnf install -y --nogpgcheck $DEPENDENCIES
build:
stage: build
script:
- meson . _build
- ninja -C _build
- ninja -C _build install

96
HACKING
View File

@@ -1,96 +0,0 @@
Copyright notices
=================
If you make a contribution substantial enough to add or update a copyright
notice on a file, such notice must be mirrored in the AUTHORS file. This is
to make it easy for people to comply to section 6 of the LGPL, which states
that a "work that uses the Library" must include copyright notices from
this library. By providing them all in one place, hopefully we save such
users some time.
USB
===
At the time of development, there are no known consumer fingerprint readers
which do not operate over the USB bus. Therefore the library is designed around
the fact that each driver drivers USB devices, and each device is a USB device.
If we were to ever support a non-USB device, some rearchitecting would be
needed, but this would not be a substantial task.
GLib
====
Although the library uses GLib internally, libfprint is designed to provide
a completely neutral interface to it's application users. So, the public
APIs should never return GLib data types or anything like that.
Two-faced-ness
==============
Like any decent library, this one is designed to provide a stable and
documented API to it's users: applications. Clear distinction is made between
data available internally in the library, and data/functions available to
the applications.
This library is confused a little by the fact that there is another 'interface'
at hand: the internal interface provided to drivers. So, we effectively end
up with 2 APIs:
1. The external-facing API for applications
2. The internal API for fingerprint drivers
Non-static functions which are intended for internal use only are prepended
with the "fpi_" prefix.
API stability
=============
No API stability has been promised to anyone: go wild, there's no issue with
breaking APIs at this point in time.
Portability
===========
libfprint is primarily written for Linux. However, I'm interested in
supporting efforts to port this to other operating systems too.
You should ensure code is portable wherever possible. Try and use GLib rather
than OS-specific features.
Endianness must be considered in all code. libfprint must support both big-
and little-endian systems.
Coding Style
============
This project follows Linux kernel coding style but with a tab width of 4.
Documentation
=============
All additions of public API functions must be accompanied with doxygen
comments.
All changes which potentially change the behaviour of the public API must
be reflected by updating the appropriate doxygen comments.
Contributing
============
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.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.

52
HACKING.md Normal file
View File

@@ -0,0 +1,52 @@
# Contributing to libfprint
## GLib
Although the library uses GLib internally, libfprint is designed to provide
a completely neutral interface to it's application users. So, the public
APIs should never return GLib data types or anything like that.
## Two-faced-ness
Like any decent library, this one is designed to provide a stable and
documented API to its users: applications. Clear distinction is made between
data available internally in the library, and data/functions available to
the applications.
This library is confused a little by the fact that there is another 'interface'
at hand: the internal interface provided to drivers. So, we effectively end
up with 2 APIs:
1. The [external-facing API for applications](libfprint/fprint.h)
2. The [internal API for fingerprint drivers](libfprint/drivers_api.h)
Non-static functions which are intended for internal use only are prepended
with the "fpi_" prefix.
## Documentation
All additions of public API functions must be accompanied with gtk-doc
comments.
All changes which potentially change the behaviour of the public API must
be reflected by updating the appropriate gtk-doc comments.
## Contributing
Patches should be sent as merge requests to the gitlab page:
https://gitlab.freedesktop.org/libfprint/libfprint/merge_requests
Drivers are not usually written by libfprint developers, but when they
are, we require:
- 3 stand-alone devices. Not in a laptop or another embedded device, as
space is scarce, unless the device has special integration with that
hardware.
- specifications of the protocol.
If you are an end-user, you can file a feature request with the "Driver Request"
tag on [libfprint's issue page](https://gitlab.freedesktop.org/libfprint/libfprint/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Driver%20Request),
or subscribe to an existing feature request there.
If you are an enterprising hacker, please file a new merge request with
the driver patches integrated.

238
INSTALL
View File

@@ -1,234 +1,6 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.
libfprint uses the Meson build system. See
http://mesonbuild.com/Quick-guide.html for details on how to
compile libfprint.
The "meson configure" command will give you a list of available
command-line options.

View File

@@ -1,25 +0,0 @@
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = THANKS TODO HACKING libfprint.pc.in
DISTCLEANFILES = ChangeLog libfprint.pc
SUBDIRS = libfprint doc
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
.PHONY: ChangeLog dist-up
ChangeLog:
git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
dist-hook: ChangeLog
dist-up: dist
rsync $(distdir).tar.bz2 frs.sourceforge.net:uploads/

63
NEWS
View File

@@ -1,6 +1,69 @@
This file lists notable changes in each release. For the full history of all
changes, see ChangeLog.
2018-06-12: v0.8.0 release
- Port to meson as the build system
- Port documentation to gtk-doc
* Drivers:
- Add Elan driver
- Increase threshold to detect encryption on URU4000 devices
- Remove already replaced UPEKE2 driver
- Fix possible crash caused by vfs5011 when no lines were captured
* Library:
- Fix a number of memory and file descriptor leaks and warnings
- Make NSS (and URU4000) driver optional
- Fix assembling of frames for non-reverse and non reverse stripes
- Split internal private header to clarify drivers API
- Simplify logging system, now all the builds can be used to output
debug information
- Mark fp_dscv_print functions as deprecated
* Udev rules:
- Add some unsupported devices to the whitelist
2017-05-14: v0.7.0 release
* Drivers:
- Add VFS0050 driver
- Fix possible crash in AES3500 and AES4000
- Fix broken enrollment in VFS101
- Better verification with small sensor scanners
- Plenty of fixes in VFS5011
- Fix memory corruption in AES1610
- Improve calibration settings for AES1610
- Improve image assembling in upeksonly driver
- Autodetect whether image is encrypted in uru4k
* Library:
- NBIS: Remove false minutia at the edge of partial image
- Introduce routines to assemble image from lines (used in VFS5011 and upeksonly)
- Fix a bug that can cause driver state machine to enter into endless loop.
* Udev rules:
- Add driver name to the USB properties
* Plenty of build fixes
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

6
README
View File

@@ -2,7 +2,7 @@ libfprint
=========
libfprint is part of the fprint project:
http://www.reactivated.net/fprint
https://fprint.freedesktop.org/
libfprint was originally developed as part of an academic project at the
University of Manchester with the aim of hiding differences between different
@@ -22,7 +22,7 @@ university computers and the project is not hosted at the university either.
For more information on libfprint, supported devices, API documentation, etc.,
see the homepage:
http://www.reactivated.net/fprint/Libfprint
https://fprint.freedesktop.org/
libfprint is licensed under the GNU LGPL version 2.1. See the COPYING file
for the license text.
@@ -37,5 +37,5 @@ libfprint includes code from NIST's NBIS software distribution:
http://fingerprint.nist.gov/NBIS/index.html
We include bozorth3 from the US export controlled distribution. We have
determined that it is fine to ship bozorth3 in an open source project,
see http://reactivated.net/fprint/wiki/US_export_control
see https://fprint.freedesktop.org/us-export-control.html

View File

@@ -1,10 +0,0 @@
#!/bin/sh
libtoolize --copy --force || exit 1
aclocal || exit 1
autoheader || exit 1
autoconf || exit 1
automake -a -c || exit 1
if test -z "$NOCONFIGURE"; then
exec ./configure --enable-maintainer-mode --enable-examples-build \
--enable-x11-examples-build --enable-debug-log $*
fi

View File

@@ -1,372 +0,0 @@
AC_INIT([libfprint], [0.5.1])
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz check-news])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([libfprint/core.c])
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
lt_major="0"
lt_revision="0"
lt_age="0"
AC_SUBST(lt_major)
AC_SUBST(lt_revision)
AC_SUBST(lt_age)
all_drivers="upeke2 upekts upektc upeksonly vcom5s uru4000 fdu2000 aes1610 aes1660 aes2501 aes2550 aes2660 aes4000 vfs101 vfs301"
require_imaging='no'
require_aeslib='no'
require_aesX660='no'
enable_upeke2='no'
enable_upekts='no'
enable_upektc='no'
enable_upeksonly='no'
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_aes4000='no'
enable_vfs101='no'
enable_vfs301='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)
AC_DEFINE([ENABLE_UPEKTS], [], [Build UPEK TouchStrip driver])
enable_upekts="yes"
;;
upeke2)
AC_DEFINE([ENABLE_UPEKE2], [], [Build UPEK Eikon 2])
enable_upeke2="yes"
;;
upektc)
AC_DEFINE([ENABLE_UPEKTC], [], [Build UPEK TouchChip driver])
enable_upektc="yes"
;;
upeksonly)
AC_DEFINE([ENABLE_UPEKSONLY], [], [Build UPEK TouchStrip sensor-only driver])
enable_upeksonly="yes"
;;
uru4000)
AC_DEFINE([ENABLE_URU4000], [], [Build Digital Persona U.are.U 4000 driver])
enable_uru4000="yes"
;;
fdu2000)
AC_DEFINE([ENABLE_FDU2000], [], [Build Secugen FDU 2000 driver])
enable_fdu2000="no"
# Driver not ported
;;
vcom5s)
AC_DEFINE([ENABLE_VCOM5S], [], [Build Veridicom 5thSense driver])
enable_vcom5s="yes"
;;
aes2501)
AC_DEFINE([ENABLE_AES2501], [], [Build AuthenTec AES2501 driver])
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"
;;
aes4000)
AC_DEFINE([ENABLE_AES4000], [], [Build AuthenTec AES4000 driver])
require_aeslib="yes"
require_imaging="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"
;;
esac
done
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_AES4000], [test "$enable_aes4000" = "yes"])
AM_CONDITIONAL([REQUIRE_AESLIB], [test "$require_aeslib" = "yes"])
AM_CONDITIONAL([REQUIRE_AESX660], [test "$require_aesX660" = "yes"])
AM_CONDITIONAL([ENABLE_VFS101], [test "$enable_vfs101" = "yes"])
AM_CONDITIONAL([ENABLE_VFS301], [test "$enable_vfs301" = "yes"])
PKG_CHECK_MODULES(LIBUSB, [libusb-1.0 >= 0.9.1])
AC_SUBST(LIBUSB_CFLAGS)
AC_SUBST(LIBUSB_LIBS)
# check for OpenSSL's libcrypto
PKG_CHECK_MODULES(CRYPTO, nss)
AC_SUBST(CRYPTO_CFLAGS)
AC_SUBST(CRYPTO_LIBS)
PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.28])
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
imagemagick_found=no
gdkpixbuf_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, 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])
fi
fi
if test "$require_imaging" = "yes"; 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" = "yes"])
AM_CONDITIONAL([REQUIRE_IMAGEMAGICK], [test "$imagemagick_found" = "yes"])
AC_SUBST(IMAGING_CFLAGS)
AC_SUBST(IMAGING_LIBS)
# Examples build
AC_ARG_ENABLE([examples-build], [AS_HELP_STRING([--enable-examples-build],
[build example applications (default n)])],
[build_examples=$enableval],
[build_examples='no'])
AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != "xno"])
# Examples build
AC_ARG_ENABLE([x11-examples-build], [AS_HELP_STRING([--enable-x11-examples-build],
[build X11 example applications (default n)])],
[build_x11_examples=$enableval],
[build_x11_examples='no'])
AM_CONDITIONAL([BUILD_X11_EXAMPLES], [test "x$build_x11_examples" != "xno"])
if test "x$build_x11_examples" != "xno"; then
# check for Xv extensions
# imported from Coriander
AC_DEFUN([AC_CHECK_XV],[
AC_SUBST(XV_CFLAGS)
AC_SUBST(XV_LIBS)
AC_MSG_CHECKING(for Xv extensions)
AC_TRY_COMPILE([
#include <X11/Xlib.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=""
AC_DEFINE(HAVE_XV,1,[defined if XV video overlay is available])
else
AC_MSG_ERROR([XV is required for X11 examples])
fi
])
AC_CHECK_XV
fi
# Message logging
AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])],
[log_enabled=$enableval],
[log_enabled='yes'])
if test "x$log_enabled" != "xno"; then
AC_DEFINE([ENABLE_LOGGING], 1, [Message logging])
fi
AC_ARG_ENABLE([debug-log], [AS_HELP_STRING([--enable-debug-log],
[enable debug logging (default n)])],
[debug_log_enabled=$enableval],
[debug_log_enabled='no'])
if test "x$debug_log_enabled" != "xno"; then
AC_DEFINE([ENABLE_DEBUG_LOGGING], 1, [Debug message logging])
fi
# Restore gnu89 inline semantics on gcc 4.3 and newer
saved_cflags="$CFLAGS"
CFLAGS="$CFLAGS -fgnu89-inline"
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$gdkpixbuf_found != no; then
AC_MSG_NOTICE([** Using gdk-pixbuf for imaging])
else
AC_MSG_NOTICE([** Using ImageMagick 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_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$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
AC_CONFIG_FILES([libfprint.pc] [Makefile] [libfprint/Makefile] [examples/Makefile] [doc/Makefile])
AC_OUTPUT

View File

@@ -1,10 +0,0 @@
EXTRA_DIST = doxygen.cfg
docs: doxygen.cfg
doxygen $^
docs-upload: docs
ln -s html api
ncftpput -f ~/.ncftp/reactivated -m -R httpdocs/fprint api/
rm -f api

115
doc/advanced-topics.xml Normal file
View File

@@ -0,0 +1,115 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="advanced-topics" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Advanced Topics</title>
<refsect2 id="compatibility">
<title>Device and print compatibility</title>
<para>
Moving off generic conceptual ideas and onto libfprint-specific
implementation details, here are some introductory notes regarding how
libfprint copes with compatibility of fingerprints.
</para>
<para>
libfprint deals with a whole variety of different fingerprint readers and
the design includes considerations of compatibility and interoperability
between multiple devices. Your application should also be prepared to
work with more than one type of fingerprint reader and should consider that
enrolled fingerprint X may not be compatible with the device the user has
plugged in today.
</para>
<para>
libfprint implements the principle that fingerprints from different devices
are not necessarily compatible. For example, different devices may see
significantly different areas of fingerprint surface, and comparing images
between the devices would be unreliable. Also, devices can stretch and
distort images in different ways.
</para>
<para>
libfprint also implements the principle that in some cases, fingerprints
<emphasis>are</emphasis> compatible between different devices. If you go and buy two
identical fingerprint readers, it seems logical that you should be able
to enroll on one and verify on another without problems.
</para>
<para>
libfprint takes a fairly simplistic approach to these issues. Internally,
fingerprint hardware is driven by individual drivers. libfprint enforces
that a fingerprint that came from a device backed by driver X is never
compared to a fingerprint that came from a device backed by driver Y.
</para>
<para>
Additionally, libfprint is designed for the situation where a single driver
may support a range of devices which differ in imaging or scanning
properties. For example, a driver may support two ranges of devices which
even though are programmed over the same interface, one device sees
substantially less of the finger flesh, therefore images from the two
device types should be incompatible despite being from the same driver. To
implement this, each driver assigns a <emphasis>device type</emphasis> to each device
that it detects based on its imaging characteristics. libfprint ensures that
two prints being compared have the same device type.
</para>
<para>
In summary, libfprint represents fingerprints in several internal structures
and each representation will offer you a way of determining the
<ulink url="#driver_id">driver ID</ulink> and <ulink url="#device-types">devtype</ulink> of the print in
question. Prints are only compatible if the driver ID <emphasis role="strong">and</emphasis> devtypes
match. libfprint does offer you some "is this print compatible?" helper
functions, so you don't have to worry about these details too much.
</para>
</refsect2>
<refsect2 id="driver_id">
<title>Driver IDs</title>
<para>
Each driver is assigned a unique ID by the project maintainer. These
assignments are
<ulink url="https://gitlab.freedesktop.org/libfprint/libfprint/blob/master/libfprint/drivers/driver_ids.h">
documented in the sources</ulink> and will never change.
</para>
<para>
The only reason you may be interested in retrieving the driver ID for a
driver is for the purpose of checking if some print data is compatible
with a device. libfprint uses the driver ID as one way of checking that
the print you are trying to verify is compatible with the device in
question - it ensures that enrollment data from one driver is never fed to
another. Note that libfprint does provide you with helper functions to
determine whether a print is compatible with a device, so under most
circumstances, you don't have to worry about driver IDs at all.
</para>
</refsect2>
<refsect2 id="device-types">
<title>Device types</title>
<para>
Internally, the <ulink url="libfprint-Driver-operations.html#libfprint-Driver-operations.description">driver</ulink> behind a device assigns a 32-bit
<emphasis>devtype</emphasis> identifier to the device. This cannot be used as a unique
ID for a specific device as many devices under the same range may share
the same devtype. The devtype may even be 0 in all cases.
</para>
<para>
The only reason you may be interested in retrieving the devtype for a
device is for the purpose of checking if some print data is compatible
with a device. libfprint uses the devtype as one way of checking that the
print you are verifying is compatible with the device in question - the
devtypes must be equal. This effectively allows drivers to support more
than one type of device where the data from each one is not compatible with
the other. Note that libfprint does provide you with helper functions to
determine whether a print is compatible with a device, so under most
circumstances, you don't have to worry about devtypes at all.
</para>
</refsect2>
</chapter>

File diff suppressed because it is too large Load Diff

30
doc/getting-started.xml Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="getting-started" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Getting Started</title>
<para>
libfprint includes several simple functional examples under the <computeroutput>examples/</computeroutput>
directory in the libfprint source distribution. Those are good starting
points.
</para>
<para>
Usually the first thing you want to do is determine which fingerprint
devices are present. This is done through <ulink url="libfprint-Device-discovery.html">device discovery</ulink>.
</para>
<para>
Once you have found a device you would like to operate, you should open it.
Refer to <ulink url="libfprint-Devices-operations.html">device operations</ulink>. This section also details enrollment,
image capture, and verification.
</para>
<para>
That should be enough to get you started, but do remember there are
documentation pages on other aspects of libfprint's API (see the modules
page).
</para>
</chapter>

106
doc/intro.xml Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<chapter id="intro" xmlns:xi="http://www.w3.org/2003/XInclude">
<title>Introduction</title>
<para>
libfprint is an open source library to provide access to fingerprint
scanning devices. For more info, see the
<ulink url="https://fprint.freedesktop.org/">libfprint project homepage</ulink>.
</para>
<para>
This documentation is aimed at application developers who wish to integrate
fingerprint-related functionality into their software. libfprint has been
designed so that you only have to do this once by integrating your
software with libfprint, you'll be supporting all the fingerprint readers
that we have got our hands on. As such, the API is rather general (and
therefore hopefully easy to comprehend!), and does its best to hide the
technical details that required to operate the hardware.
</para>
<para>
This documentation is not aimed at developers wishing to develop and
contribute fingerprint device drivers to libfprint.
</para>
<para>
Feedback on this API and its associated documentation is appreciated. Was
anything unclear? Does anything seem unreasonably complicated? Is anything
missing? Let us know on the
<ulink url="https://lists.freedesktop.org/mailman/listinfo/fprint">mailing list</ulink>.
</para>
<refsect2 id="enrollment">
<title>Enrollment</title>
<para>
Before you dive into the API, it's worth introducing a couple of concepts.
</para>
<para>
The process of enrolling a finger is where you effectively scan your
finger for the purposes of teaching the system what your finger looks like.
This means that you scan your fingerprint, then the system processes it and
stores some data about your fingerprint to refer to later.
</para>
</refsect2>
<refsect2 id="verification">
<title>Verification</title>
<para>
Verification is what most people think of when they think about fingerprint
scanning. The process of verification is effectively performing a fresh
fingerprint scan, and then comparing that scan to a finger that was
previously enrolled.
</para>
<para>
As an example scenario, verification can be used to implement what people
would picture as fingerprint login (i.e. fingerprint replaces password).
For example:
</para>
<itemizedlist mark='dot'>
<listitem>
I enroll my fingerprint through some software that trusts I am who I say
I am. This is a prerequisite before I can perform fingerprint-based
login for my account.
</listitem>
<listitem>
Some time later, I want to login to my computer. I enter my username,
but instead of prompting me for a password, it asks me to scan my finger.
I scan my finger.
</listitem>
<listitem>
The system compares the finger I just scanned to the one that was
enrolled earlier. If the system decides that the fingerprints match,
I am successfully logged in. Otherwise, the system informs me that I am
not authorised to login as that user.
</listitem>
</itemizedlist>
</refsect2>
<refsect2 id="identification">
<title>Identification</title>
<para>
Identification is the process of comparing a freshly scanned fingerprint
to a <emphasis>collection</emphasis> of previously enrolled fingerprints. For example,
imagine there are 100 people in an organisation, and they all have enrolled
their fingerprints. One user walks up to a fingerprint scanner and scans
their finger. With <emphasis>no other knowledge</emphasis> of who that user might be,
the system examines their fingerprint, looks in the database, and determines
that the user is user number #61.
</para>
<para>
In other words, verification might be seen as a one-to-one fingerprint
comparison where you know the identity of the user that you wish to
authenticate, whereas identification is a one-to-many comparison where you
do not know the identity of the user that you wish to authenticate.
</para>
</refsect2>
</chapter>

53
doc/libfprint-docs.xml Normal file
View File

@@ -0,0 +1,53 @@
<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
[
<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
]>
<book id="index">
<bookinfo>
<title>libfprint Reference Manual</title>
<releaseinfo>
<para>This document is the API reference for the libfprint library.</para>
<para>
The latest version of libfprint, as well as the latest version of
this API reference, is <ulink role="online-location" url="https://www.freedesktop.org/wiki/Software/fprint/libfprint/">available online</ulink>.
</para>
</releaseinfo>
</bookinfo>
<part>
<title>Library Overview</title>
<xi:include href="intro.xml"/>
<xi:include href="advanced-topics.xml"/>
<xi:include href="getting-started.xml"/>
</part>
<part>
<title>API Documentation</title>
<xi:include href="xml/events.xml"/>
<xi:include href="xml/discovery.xml"/>
<xi:include href="xml/drv.xml"/>
<xi:include href="xml/dev.xml"/>
<xi:include href="xml/print_data.xml"/>
<!-- FIXME https://bugs.freedesktop.org/show_bug.cgi?id=106550 -->
<xi:include href="xml/dscv_print.xml"/>
<xi:include href="xml/img.xml"/>
</part>
<!--
<part>
<title>Writing Drivers</title>
</part>
-->
<index id="api-index">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
<index id="deprecated-api-index" role="deprecated">
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
</book>

136
doc/libfprint-sections.txt Normal file
View File

@@ -0,0 +1,136 @@
<INCLUDE>fprint.h</INCLUDE>
<SECTION>
<FILE>events</FILE>
<TITLE>Initialisation and events handling</TITLE>
fp_set_debug
fp_init
fp_exit
fp_pollfd
fp_handle_events_timeout
fp_handle_events
fp_get_next_timeout
fp_get_pollfds
fp_pollfd_added_cb
fp_pollfd_removed_cb
fp_set_pollfd_notifiers
</SECTION>
<SECTION>
<FILE>discovery</FILE>
<TITLE>Device discovery</TITLE>
fp_dscv_dev
fp_discover_devs
fp_dscv_devs_free
fp_dscv_dev_get_driver
fp_dscv_dev_get_devtype
fp_dscv_dev_get_driver_id
fp_dscv_dev_supports_print_data
fp_dscv_dev_supports_dscv_print
fp_dscv_dev_for_print_data
fp_dscv_dev_for_dscv_print
</SECTION>
<SECTION>
<FILE>drv</FILE>
fp_driver
fp_driver_get_name
fp_driver_get_full_name
fp_driver_get_driver_id
fp_driver_get_scan_type
</SECTION>
<SECTION>
<FILE>dev</FILE>
fp_dev
fp_scan_type
fp_capture_result
fp_enroll_result
fp_verify_result
fp_dev_get_driver
fp_dev_get_nr_enroll_stages
fp_dev_get_devtype
fp_dev_supports_print_data
fp_dev_supports_imaging
fp_dev_supports_identification
fp_dev_supports_dscv_print
fp_dev_get_img_width
fp_dev_get_img_height
fp_operation_stop_cb
fp_img_operation_cb
fp_dev_open_cb
fp_enroll_stage_cb
fp_identify_cb
fp_dev_open
fp_async_dev_open
fp_dev_close
fp_async_dev_close
fp_enroll_finger
fp_enroll_finger_img
fp_async_enroll_start
fp_async_enroll_stop
fp_verify_finger
fp_verify_finger_img
fp_async_verify_start
fp_async_verify_stop
fp_identify_finger
fp_identify_finger_img
fp_async_identify_start
fp_async_identify_stop
fp_dev_img_capture
fp_async_capture_start
fp_async_capture_stop
</SECTION>
<SECTION>
<FILE>print_data</FILE>
fp_finger
fp_print_data
fp_print_data
fp_print_data_get_data
fp_print_data_from_data
fp_print_data_save
fp_print_data_load
fp_print_data_delete
fp_print_data_from_dscv_print
fp_print_data_free
fp_print_data_get_driver_id
fp_print_data_get_devtype
</SECTION>
<SECTION>
<FILE>dscv_print</FILE>
fp_dscv_print
fp_discover_prints
fp_dscv_prints_free
fp_dscv_print_get_driver_id
fp_dscv_print_get_devtype
fp_dscv_print_get_finger
fp_dscv_print_delete
</SECTION>
<SECTION>
<FILE>img</FILE>
fp_img
fp_minutia
fp_img_free
fp_img_get_height
fp_img_get_width
fp_img_get_data
fp_img_save_to_file
fp_img_standardize
fp_img_binarize
fp_img_get_minutiae
</SECTION>
<SECTION>
<FILE>poll</FILE>
</SECTION>

67
doc/meson.build Normal file
View File

@@ -0,0 +1,67 @@
subdir('xml')
private_headers = [
'config.h',
'aeslib.h',
'assembling.h',
'fp_internal.h',
# Drivers
'aes1660.h',
'aes2501.h',
'aes2550.h',
'aes2660.h',
'aes3k.h',
'aesx660.h',
'driver_ids.h',
'elan.h',
'upeksonly.h',
'upektc.h',
'upektc_img.h',
'vfs0050.h',
'vfs301_proto_fragments.h',
'vfs301_proto.h',
'vfs5011_proto.h',
# NBIS
'morph.h',
'sunrast.h',
'bozorth.h',
'defs.h',
'log.h',
'bz_array.h',
'lfs.h',
]
html_images = [
]
content_files = [
'intro.xml'
]
expand_content_files = content_files
glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix')
glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html')
docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html')
gnome.gtkdoc('libfprint',
main_xml: 'libfprint-docs.xml',
src_dir: join_paths(meson.source_root(), 'libfprint'),
dependencies: libfprint_dep,
content_files: content_files,
expand_content_files: expand_content_files,
scan_args: [
'--rebuild-types',
'--ignore-decorators=API_EXPORTED',
'--ignore-headers=' + ' '.join(private_headers),
],
fixxref_args: [
'--html-dir=@0@'.format(docpath),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')),
],
html_assets: html_images,
install: true)

View File

@@ -0,0 +1,8 @@
<!ENTITY package "@PACKAGE@">
<!ENTITY package_bugreport "@PACKAGE_BUGREPORT@">
<!ENTITY package_name "@PACKAGE_NAME@">
<!ENTITY package_string "@PACKAGE_STRING@">
<!ENTITY package_tarname "@PACKAGE_TARNAME@">
<!ENTITY package_url "@PACKAGE_URL@">
<!ENTITY package_version "@PACKAGE_VERSION@">
<!ENTITY package_api_version "@PACKAGE_API_VERSION@">

10
doc/xml/meson.build Normal file
View File

@@ -0,0 +1,10 @@
ent_conf = configuration_data()
ent_conf.set('PACKAGE', 'libfprint')
ent_conf.set('PACKAGE_BUGREPORT', 'https://bugs.freedesktop.org/enter_bug.cgi?product=libfprint')
ent_conf.set('PACKAGE_NAME', 'libfprint')
ent_conf.set('PACKAGE_STRING', 'libfprint')
ent_conf.set('PACKAGE_TARNAME', 'libfprint-' + meson.project_version())
ent_conf.set('PACKAGE_URL', 'https://www.freedesktop.org/wiki/Software/fprint/libfprint/')
ent_conf.set('PACKAGE_VERSION', meson.project_version())
ent_conf.set('PACKAGE_API_VERSION', '1.0')
configure_file(input: 'gtkdocentities.ent.in', output: 'gtkdocentities.ent', configuration: ent_conf)

View File

@@ -1,26 +0,0 @@
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
enroll_SOURCES = enroll.c
enroll_LDADD = ../libfprint/libfprint.la
verify_SOURCES = verify.c
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
img_capture_continuous_CFLAGS = $(X_CFLAGS) $(XV_CFLAGS)
img_capture_continuous_SOURCES = img_capture_continuous.c
img_capture_continuous_LDADD = ../libfprint/libfprint.la $(X_LIBS) $(X_PRE_LIBS) $(XV_LIBS) -lX11 $(X_EXTRA_LIBS);
endif

View File

@@ -110,12 +110,14 @@ int main(void)
"Ctrl+C\n");
getchar();
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

View File

@@ -42,12 +42,14 @@ int main(void)
struct fp_dev *dev;
struct fp_img *img = NULL;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {
@@ -57,6 +59,7 @@ int main(void)
ddev = discover_device(discovered_devs);
if (!ddev) {
fp_dscv_devs_free(discovered_devs);
fprintf(stderr, "No devices detected.\n");
goto out;
}

View File

@@ -53,7 +53,7 @@ static int connection = -1;
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
int i, j;
int y0, y1, u0, u1, v0, v1;
int gval;
uint64_t gval;
for (i = 0, j = 0; i < num; i += 2, j += 4)
{
@@ -84,7 +84,7 @@ static void display_frame(struct fp_img *img)
0, 0, width, height, 0, 0, width, height);
}
static void QueryXv()
static void QueryXv(void)
{
unsigned int num_adaptors;
int num_formats;
@@ -141,12 +141,14 @@ int main(void)
int img_height;
int standardize = 0;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

29
examples/meson.build Normal file
View File

@@ -0,0 +1,29 @@
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
foreach example: examples
executable(example,
example + '.c',
dependencies: libfprint_dep,
include_directories: [
root_inc,
],
c_args: common_cflags)
endforeach
executable('cpp-test',
'cpp-test.cpp',
dependencies: libfprint_dep,
include_directories: [
root_inc,
],
c_args: common_cflags)
if get_option('x11-examples')
executable('img_capture_continuous',
'img_capture_continuous.c',
dependencies: [ libfprint_dep, xv_dep, x11_dep ],
include_directories: [
root_inc,
],
c_args: common_cflags)
endif

View File

@@ -86,12 +86,14 @@ int main(void)
struct fp_dev *dev;
struct fp_print_data *data;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

View File

@@ -129,12 +129,14 @@ int main(void)
struct fp_dev *dev;
struct fp_print_data *data;
setenv ("G_MESSAGES_DEBUG", "all", 0);
setenv ("LIBUSB_DEBUG", "3", 0);
r = fp_init();
if (r < 0) {
fprintf(stderr, "Failed to initialize libfprint\n");
exit(1);
}
fp_set_debug(3);
discovered_devs = fp_discover_devs();
if (!discovered_devs) {

View File

@@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libfprint
Description: Generic C API for fingerprint reader access
Version: @VERSION@
Libs: -L${libdir} -lfprint
Cflags: -I${includedir}/libfprint

View File

@@ -1,194 +0,0 @@
lib_LTLIBRARIES = libfprint.la
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 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
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
EXTRA_DIST = \
$(UPEKE2_SRC) \
$(UPEKTS_SRC) \
$(UPEKTC_SRC) \
$(UPEKSONLY_SRC) \
$(URU4000_SRC) \
$(AES1610_SRC) \
$(AES1660_SRC) \
$(AES2501_SRC) \
$(AES2550_SRC) \
$(AES2660_SRC) \
$(AES4000_SRC) \
$(FDU2000_SRC) \
$(VCOM5S_SRC) \
$(VFS101_SRC) \
$(VFS301_SRC) \
drivers/aesx660.c \
drivers/aesx660.h \
drivers/driver_ids.h \
aeslib.c aeslib.h \
imagemagick.c \
gdkpixbuf.c \
60-fprint-autosuspend.rules
DRIVER_SRC =
OTHER_SRC =
NBIS_SRC = \
nbis/include/bozorth.h \
nbis/include/bz_array.h \
nbis/include/defs.h \
nbis/include/lfs.h \
nbis/include/log.h \
nbis/include/morph.h \
nbis/include/sunrast.h \
nbis/bozorth3/bozorth3.c \
nbis/bozorth3/bz_alloc.c \
nbis/bozorth3/bz_drvrs.c \
nbis/bozorth3/bz_gbls.c \
nbis/bozorth3/bz_io.c \
nbis/bozorth3/bz_sort.c \
nbis/mindtct/binar.c \
nbis/mindtct/block.c \
nbis/mindtct/contour.c \
nbis/mindtct/detect.c \
nbis/mindtct/dft.c \
nbis/mindtct/free.c \
nbis/mindtct/globals.c \
nbis/mindtct/imgutil.c \
nbis/mindtct/init.c \
nbis/mindtct/line.c \
nbis/mindtct/log.c \
nbis/mindtct/loop.c \
nbis/mindtct/maps.c \
nbis/mindtct/matchpat.c \
nbis/mindtct/minutia.c \
nbis/mindtct/morph.c \
nbis/mindtct/quality.c \
nbis/mindtct/remove.c \
nbis/mindtct/ridges.c \
nbis/mindtct/shape.c \
nbis/mindtct/sort.c \
nbis/mindtct/util.c
libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(CRYPTO_LIBS)
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 $(GLIB_LIBS)
udev_rules_DATA = 60-fprint-autosuspend.rules
if ENABLE_UDEV_RULES
$(udev_rules_DATA): fprint-list-udev-rules
$(builddir)/fprint-list-udev-rules > $@
endif
if ENABLE_UPEKE2
DRIVER_SRC += $(UPEKE2_SRC)
endif
if ENABLE_UPEKTS
DRIVER_SRC += $(UPEKTS_SRC)
endif
if ENABLE_UPEKSONLY
DRIVER_SRC += $(UPEKSONLY_SRC)
endif
if ENABLE_UPEKTC
DRIVER_SRC += $(UPEKTC_SRC)
endif
if ENABLE_URU4000
DRIVER_SRC += $(URU4000_SRC)
endif
if ENABLE_VCOM5S
DRIVER_SRC += $(VCOM5S_SRC)
endif
#if ENABLE_FDU2000
#DRIVER_SRC += $(FDU2000_SRC)
#endif
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_AES4000
DRIVER_SRC += $(AES4000_SRC)
endif
if ENABLE_VFS101
DRIVER_SRC += $(VFS101_SRC)
endif
if ENABLE_VFS301
DRIVER_SRC += $(VFS301_SRC)
endif
if REQUIRE_IMAGEMAGICK
OTHER_SRC += imagemagick.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
if REQUIRE_GDKPIXBUF
OTHER_SRC += gdkpixbuf.c
libfprint_la_CFLAGS += $(IMAGING_CFLAGS)
libfprint_la_LIBADD += $(IMAGING_LIBS)
endif
if REQUIRE_AESLIB
OTHER_SRC += aeslib.c aeslib.h
endif
if REQUIRE_AESX660
OTHER_SRC += drivers/aesx660.c drivers/aesx660.h
endif
libfprint_la_SOURCES = \
fp_internal.h \
async.c \
core.c \
data.c \
drv.c \
img.c \
imgdev.c \
poll.c \
sync.c \
$(DRIVER_SRC) \
$(OTHER_SRC) \
$(NBIS_SRC)
pkginclude_HEADERS = fprint.h

View File

@@ -19,13 +19,15 @@
#define FP_COMPONENT "aeslib"
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "fp_internal.h"
#include "assembling.h"
#include "aeslib.h"
#define MAX_REGWRITES_PER_REQUEST 16
@@ -158,127 +160,16 @@ 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)
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y)
{
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 & 0x0f) * 17;
output[width * (row + 1) + column] = ((*input & 0xf0) >> 4) * 17;
input++;
}
}
}
/* find overlapping parts of frames */
static unsigned int find_overlap(unsigned char *first_frame,
unsigned char *second_frame, unsigned int *min_error,
unsigned int frame_width, unsigned int frame_height)
{
unsigned int dy;
unsigned int not_overlapped_height = 0;
/* 255 is highest brightness value for an 8bpp image */
*min_error = 255 * frame_width * frame_height;
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(GSList *list_entry, size_t num_stripes,
unsigned int frame_width, unsigned int frame_height,
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, frame_size = frame_width * frame_height;
*errors_sum = 0;
if (reverse)
output += (num_stripes - 1) * frame_size;
for (frame = 0; frame < num_stripes; 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_stripes; frame++) {
int not_overlapped;
output += frame_size;
not_overlapped = find_overlap(assembled, output, &min_error,
frame_width, frame_height);
*errors_sum += min_error;
image_height += not_overlapped;
assembled += frame_width * not_overlapped;
memcpy(assembled, output, frame_size);
}
return image_height;
}
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height)
{
size_t final_size;
struct fp_img *img;
unsigned int frame_size = frame_width * frame_height;
unsigned int errors_sum, r_errors_sum;
BUG_ON(stripes_len == 0);
/* create buffer big enough for max image */
img = fpi_img_new(stripes_len * frame_size);
img->flags = FP_IMG_COLORS_INVERTED;
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
img->data, FALSE, &errors_sum);
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
img->data, TRUE, &r_errors_sum);
if (r_errors_sum > errors_sum) {
img->height = assemble(stripes, stripes_len,
frame_width, frame_height,
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);
img->width = frame_width;
return img;
ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
ret = y % 2 ? ret >> 4 : ret & 0xf;
ret *= 17;
return ret;
}

View File

@@ -20,24 +20,26 @@
#ifndef __AESLIB_H__
#define __AESLIB_H__
#include <fp_internal.h>
#include <fprint.h>
struct aes_regwrite {
unsigned char reg;
unsigned char value;
};
struct fpi_frame;
struct fpi_frame_asmbl_ctx;
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);
struct fp_img *aes_assemble(GSList *stripes, size_t stripes_len,
unsigned int frame_width, unsigned int frame_height);
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned int x,
unsigned int y);
#endif

434
libfprint/assembling.c Normal file
View File

@@ -0,0 +1,434 @@
/*
* Image assembling routines
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
* Copyright (C) 2015 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 "assembling"
#include "fp_internal.h"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <glib.h>
#include "assembling.h"
static unsigned int calc_error(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
int dx,
int dy)
{
unsigned int width, height;
unsigned int x1, y1, x2, y2, err, i, j;
width = ctx->frame_width - (dx > 0 ? dx : -dx);
height = ctx->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 = ctx->get_pixel(ctx, first_frame, x1, y1);
v2 = ctx->get_pixel(ctx, second_frame, x2, y2);
err += v1 > v2 ? v1 - v2 : v2 - v1;
j++;
x1++;
x2++;
} while (j < width);
i++;
y1++;
y2++;
} while (i < height);
/* Normalize error */
err *= (ctx->frame_height * ctx->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 fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *first_frame,
struct fpi_frame *second_frame,
unsigned int *min_error)
{
int dx, dy;
unsigned int err;
*min_error = 255 * ctx->frame_height * ctx->frame_width;
/* 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 < ctx->frame_height; dy++) {
for (dx = -8; dx < 8; dx++) {
err = calc_error(ctx, first_frame, second_frame,
dx, dy);
if (err < *min_error) {
*min_error = err;
second_frame->delta_x = -dx;
second_frame->delta_y = dy;
}
}
}
}
static unsigned int do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes,
gboolean reverse)
{
GSList *list_entry = stripes;
GTimer *timer;
int frame = 1;
struct fpi_frame *prev_stripe = list_entry->data;
unsigned int min_error;
/* Max error is width * height * 255, for AES2501 which has the largest
* sensor its 192*16*255 = 783360. So for 32bit value it's ~5482 frame before
* we might get int overflow. Use 64bit value here to prevent integer overflow
*/
unsigned long long total_error = 0;
list_entry = g_slist_next(list_entry);
timer = g_timer_new();
do {
struct fpi_frame *cur_stripe = list_entry->data;
if (reverse) {
find_overlap(ctx, prev_stripe, cur_stripe, &min_error);
cur_stripe->delta_y = -cur_stripe->delta_y;
cur_stripe->delta_x = -cur_stripe->delta_x;
}
else
find_overlap(ctx, cur_stripe, prev_stripe, &min_error);
total_error += min_error;
frame++;
prev_stripe = cur_stripe;
list_entry = g_slist_next(list_entry);
} while (frame < num_stripes);
g_timer_stop(timer);
fp_dbg("calc delta completed in %f secs", g_timer_elapsed(timer, NULL));
g_timer_destroy(timer);
return total_error / num_stripes;
}
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t num_stripes)
{
int err, rev_err;
err = do_movement_estimation(ctx, stripes, num_stripes, FALSE);
rev_err = do_movement_estimation(ctx, stripes, num_stripes, TRUE);
fp_dbg("errors: %d rev: %d", err, rev_err);
if (err < rev_err) {
do_movement_estimation(ctx, stripes, num_stripes, FALSE);
}
}
static inline void aes_blit_stripe(struct fpi_frame_asmbl_ctx *ctx,
struct fp_img *img,
struct fpi_frame *stripe,
int x, int y)
{
unsigned int ix, iy;
unsigned int fx, fy;
unsigned int width, height;
/* Find intersection */
if (x < 0) {
width = ctx->frame_width + x;
ix = 0;
fx = -x;
} else {
ix = x;
fx = 0;
width = ctx->frame_width;
}
if ((ix + width) > img->width)
width = img->width - ix;
if (y < 0) {
iy = 0;
fy = -y;
height = ctx->frame_height + y;
} else {
iy = y;
fy = 0;
height = ctx->frame_height;
}
if (fx > ctx->frame_width)
return;
if (fy > ctx->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)] = ctx->get_pixel(ctx, stripe, fx, fy);
}
}
}
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len)
{
GSList *stripe;
struct fp_img *img;
int height = 0;
int i, y, x;
gboolean reverse = FALSE;
struct fpi_frame *fpi_frame;
BUG_ON(stripes_len == 0);
BUG_ON(ctx->image_width < ctx->frame_width);
/* Calculate height */
i = 0;
stripe = stripes;
/* No offset for 1st image */
fpi_frame = stripe->data;
fpi_frame->delta_x = 0;
fpi_frame->delta_y = 0;
do {
fpi_frame = stripe->data;
height += fpi_frame->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 += ctx->frame_height;
/* Create buffer big enough for max image */
img = fpi_img_new(ctx->image_width * height);
img->flags = FP_IMG_COLORS_INVERTED;
img->flags |= reverse ? 0 : FP_IMG_H_FLIPPED | FP_IMG_V_FLIPPED;
img->width = ctx->image_width;
img->height = height;
/* Assemble stripes */
i = 0;
stripe = stripes;
y = reverse ? (height - ctx->frame_height) : 0;
x = (ctx->image_width - ctx->frame_width) / 2;
do {
fpi_frame = stripe->data;
if(reverse) {
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
}
aes_blit_stripe(ctx, img, fpi_frame, x, y);
if(!reverse) {
y += fpi_frame->delta_y;
x += fpi_frame->delta_x;
}
stripe = g_slist_next(stripe);
i++;
} while (i < stripes_len);
return img;
}
static int cmpint(const void *p1, const void *p2, gpointer data)
{
int a = *((int *)p1);
int b = *((int *)p2);
if (a < b)
return -1;
else if (a == b)
return 0;
else
return 1;
}
static void median_filter(int *data, int size, int filtersize)
{
int i;
int *result = (int *)g_malloc0(size*sizeof(int));
int *sortbuf = (int *)g_malloc0(filtersize*sizeof(int));
for (i = 0; i < size; i++) {
int i1 = i - (filtersize-1)/2;
int i2 = i + (filtersize-1)/2;
if (i1 < 0)
i1 = 0;
if (i2 >= size)
i2 = size-1;
g_memmove(sortbuf, data+i1, (i2-i1+1)*sizeof(int));
g_qsort_with_data(sortbuf, i2-i1+1, sizeof(int), cmpint, NULL);
result[i] = sortbuf[(i2-i1+1)/2];
}
memmove(data, result, size*sizeof(int));
g_free(result);
g_free(sortbuf);
}
static void interpolate_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, float y1, GSList *line2,
float y2, unsigned char *output, float yi, int size)
{
int i;
unsigned char p1, p2;
if (!line1 || !line2)
return;
for (i = 0; i < size; i++) {
p1 = ctx->get_pixel(ctx, line1, i);
p2 = ctx->get_pixel(ctx, line2, i);
output[i] = (float)p1
+ (yi - y1)/(y2 - y1)*(p2 - p1);
}
}
static int min(int a, int b) {return (a < b) ? a : b; }
/* Rescale image to account for variable swiping speed */
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t lines_len)
{
/* Number of output lines per distance between two scanners */
int i;
GSList *row1, *row2;
float y = 0.0;
int line_ind = 0;
int *offsets = (int *)g_malloc0((lines_len / 2) * sizeof(int));
unsigned char *output = g_malloc0(ctx->line_width * ctx->max_height);
struct fp_img *img;
g_return_val_if_fail (lines != NULL, NULL);
g_return_val_if_fail (lines_len > 0, NULL);
fp_dbg("%"G_GINT64_FORMAT, g_get_real_time());
row1 = lines;
for (i = 0; (i < lines_len - 1) && row1; i += 2) {
int bestmatch = i;
int bestdiff = 0;
int j, firstrow, lastrow;
firstrow = i + 1;
lastrow = min(i + ctx->max_search_offset, lines_len - 1);
row2 = g_slist_next(row1);
for (j = firstrow; j <= lastrow; j++) {
int diff = ctx->get_deviation(ctx,
row1,
row2);
if ((j == firstrow) || (diff < bestdiff)) {
bestdiff = diff;
bestmatch = j;
}
row2 = g_slist_next(row2);
}
offsets[i / 2] = bestmatch - i;
fp_dbg("%d", offsets[i / 2]);
row1 = g_slist_next(row1);
if (row1)
row1 = g_slist_next(row1);
}
median_filter(offsets, (lines_len / 2) - 1, ctx->median_filter_size);
fp_dbg("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time());
for (i = 0; i <= (lines_len / 2) - 1; i++)
fp_dbg("%d", offsets[i]);
row1 = lines;
for (i = 0; i < lines_len - 1; i++, row1 = g_slist_next(row1)) {
int offset = offsets[i/2];
if (offset > 0) {
float ynext = y + (float)ctx->resolution / offset;
while (line_ind < ynext) {
if (line_ind > ctx->max_height - 1)
goto out;
interpolate_lines(ctx,
row1, y,
g_slist_next(row1),
ynext,
output + line_ind * ctx->line_width,
line_ind,
ctx->line_width);
line_ind++;
}
y = ynext;
}
}
out:
img = fpi_img_new(ctx->line_width * line_ind);
img->height = line_ind;
img->width = ctx->line_width;
img->flags = FP_IMG_V_FLIPPED;
g_memmove(img->data, output, ctx->line_width * line_ind);
g_free(offsets);
g_free(output);
return img;
}

65
libfprint/assembling.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* Image assembling routines
* Shared functions between libfprint Authentec drivers
* Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2015 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 __ASSEMBLING_H__
#define __ASSEMBLING_H__
#include <fprint.h>
struct fpi_frame {
int delta_x;
int delta_y;
unsigned char data[0];
};
struct fpi_frame_asmbl_ctx {
unsigned frame_width;
unsigned frame_height;
unsigned image_width;
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame,
unsigned x,
unsigned y);
};
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len);
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
GSList *stripes, size_t stripes_len);
struct fpi_line_asmbl_ctx {
unsigned line_width;
unsigned max_height;
unsigned resolution;
unsigned median_filter_size;
unsigned max_search_offset;
int (*get_deviation)(struct fpi_line_asmbl_ctx *ctx,
GSList *line1, GSList *line2);
unsigned char (*get_pixel)(struct fpi_line_asmbl_ctx *ctx,
GSList *line,
unsigned x);
};
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
GSList *lines, size_t lines_len);
#endif

View File

@@ -19,12 +19,12 @@
#define FP_COMPONENT "async"
#include "fp_internal.h"
#include <config.h>
#include <errno.h>
#include <glib.h>
#include "fp_internal.h"
/* Drivers call this when device initialisation has completed */
void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
{
@@ -36,15 +36,27 @@ void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
dev->open_cb(dev, status, dev->open_cb_data);
}
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
/**
* fp_async_dev_open:
* @ddev:
* @callback:
* @user_data
*
* Returns:
*/
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
void *user_data)
{
struct fp_driver *drv = ddev->drv;
struct fp_driver *drv;
struct fp_dev *dev;
libusb_device_handle *udevh;
int r;
fp_dbg("");
g_return_val_if_fail(ddev != NULL, -ENODEV);
drv = ddev->drv;
G_DEBUG_HERE();
r = libusb_open(ddev->udev, &udevh);
if (r < 0) {
fp_err("usb_open failed, error %d", r);
@@ -56,7 +68,7 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
dev->udev = udevh;
dev->__enroll_stage = -1;
dev->state = DEV_STATE_INITIALIZING;
dev->open_cb = cb;
dev->open_cb = callback;
dev->open_cb_data = user_data;
if (!drv->open) {
@@ -78,7 +90,7 @@ API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb cb,
/* Drivers call this when device deinitialisation has completed */
void fpi_drvcb_close_complete(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
dev->state = DEV_STATE_DEINITIALIZED;
libusb_close(dev->udev);
@@ -87,10 +99,22 @@ void fpi_drvcb_close_complete(struct fp_dev *dev)
g_free(dev);
}
/**
* fp_async_dev_close:
* @dev:
* @callback:
* @user_data
*/
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
fp_dev_close_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
g_return_if_fail (dev != NULL);
drv = dev->drv;
g_return_if_fail (drv->close != NULL);
if (g_slist_index(opened_devices, (gconstpointer) dev) == -1)
fp_err("device %p not in opened list!", dev);
@@ -98,12 +122,6 @@ API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
dev->close_cb = callback;
dev->close_cb_data = user_data;
if (!drv->close) {
fpi_drvcb_close_complete(dev);
return;
}
dev->state = DEV_STATE_DEINITIALIZING;
drv->close(dev);
}
@@ -127,12 +145,24 @@ void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
}
}
/**
* fp_async_enroll_start:
* @dev:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
fp_enroll_stage_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
if (!dev->nr_enroll_stages || !drv->enroll_start) {
fp_err("driver %s has 0 enroll stages or no enroll func",
drv->name);
@@ -174,20 +204,32 @@ void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
/* Drivers call this when enrollment has stopped */
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ENROLL_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->enroll_stop_cb)
dev->enroll_stop_cb(dev, dev->enroll_stop_cb_data);
}
/**
* fp_async_enroll_stop:
* @dev:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
fp_enroll_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->enroll_start)
return -ENOTSUP;
@@ -210,13 +252,26 @@ API_EXPORTED int fp_async_enroll_stop(struct fp_dev *dev,
return r;
}
/**
* fp_async_verify_start:
* @dev:
* @data:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
struct fp_print_data *data, fp_verify_cb callback, void *user_data)
struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->verify_start)
return -ENOTSUP;
@@ -237,7 +292,7 @@ API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
/* Drivers call this when verification has started */
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
if (status) {
if (status > 0) {
@@ -271,21 +326,33 @@ void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
/* Drivers call this when verification has stopped */
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_VERIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->verify_stop_cb)
dev->verify_stop_cb(dev, dev->verify_stop_cb_data);
}
/**
* fp_async_verify_stop:
* @dev:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
fp_verify_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_ERROR
&& dev->state != DEV_STATE_VERIFYING
&& dev->state != DEV_STATE_VERIFY_DONE);
@@ -311,13 +378,26 @@ API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
return r;
}
/**
* fp_async_identify_start:
* @dev:
* @gallery:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_identify_start(struct fp_dev *dev,
struct fp_print_data **gallery, fp_identify_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
if (!drv->identify_start)
return -ENOTSUP;
dev->state = DEV_STATE_IDENTIFY_STARTING;
@@ -369,14 +449,26 @@ void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
fp_dbg("ignoring verify result as no callback is subscribed");
}
/**
* fp_async_identify_stop:
* @dev:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
fp_identify_stop_cb callback, void *user_data)
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv = dev->drv;
struct fp_driver *drv;
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
int r;
fp_dbg("");
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
&& dev->state != DEV_STATE_IDENTIFY_DONE);
@@ -405,10 +497,133 @@ API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
/* Drivers call this when identification has stopped */
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
{
fp_dbg("");
G_DEBUG_HERE();
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STOPPING);
dev->state = DEV_STATE_INITIALIZED;
if (dev->identify_stop_cb)
dev->identify_stop_cb(dev, dev->identify_stop_cb_data);
}
/**
* fp_async_capture_start:
* @dev:
* @unconditional:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_capture_start(struct fp_dev *dev, int unconditional,
fp_img_operation_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
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)
{
G_DEBUG_HERE();
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)
{
G_DEBUG_HERE();
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);
}
/**
* fp_async_capture_stop:
* @dev:
* @callback:
* @user_data:
*
* Returns:
*/
API_EXPORTED int fp_async_capture_stop(struct fp_dev *dev,
fp_operation_stop_cb callback, void *user_data)
{
struct fp_driver *drv;
int r;
g_return_val_if_fail(dev != NULL, -ENODEV);
drv = dev->drv;
G_DEBUG_HERE();
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,8 +32,11 @@
#define DIR_PERMS 0700
/** @defgroup print_data Stored prints
* Stored prints are represented by a structure named <tt>fp_print_data</tt>.
/**
* SECTION: print_data
* @title: Stored prints
*
* Stored prints are represented by a structure named #fp_print_data.
* Stored prints are originally obtained from an enrollment function such as
* fp_enroll_finger().
*
@@ -73,7 +76,6 @@ void fpi_data_exit(void)
((finger) >= LEFT_THUMB && (finger) <= RIGHT_LITTLE)
/* for debug messages only */
#ifdef ENABLE_DEBUG_LOGGING
static const char *finger_num_to_str(enum fp_finger finger)
{
const char *names[] = {
@@ -92,89 +94,191 @@ static const char *finger_num_to_str(enum fp_finger finger)
return "UNKNOWN";
return names[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_malloc0(sizeof(*data) + length);
fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype);
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)
{
return print_data_new(dev->drv->id, dev->devtype,
fpi_driver_get_data_type(dev->drv), length);
g_free(item);
}
/** \ingroup print_data
struct fp_print_data_item *fpi_print_data_item_new(size_t length)
{
struct fp_print_data_item *item = g_malloc0(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));
}
/**
* fp_print_data_get_data:
* @data: the stored print
* @ret: output location for the data buffer. Must be freed with free()
* after use.
* Convert a stored print into a unified representation inside a data buffer.
* You can then store this data buffer in any way that suits you, and load
* it back at some later time using fp_print_data_from_data().
* \param data the stored print
* \param ret output location for the data buffer. Must be freed with free()
* after use.
* \returns the size of the freshly allocated buffer, or 0 on error.
*
* Returns: the size of the freshly allocated buffer, or 0 on error.
*/
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("");
G_DEBUG_HERE();
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;
}
/** \ingroup print_data
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", (int) item_len, (int) 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;
}
/**
* fp_print_data_from_data:
* @buf: the data buffer
* @buflen: the length of the buffer
* 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
* fp_print_data_get_data() function.
* \param buf the data buffer
* \param buflen the length of the buffer
* \returns the stored print represented by the data, or NULL on error. Must
*
* Returns: the stored print represented by the data, or %NULL on error. Must
* be freed with fp_print_data_free() after use.
*/
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)
@@ -208,7 +312,11 @@ static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
return __get_path_to_print(dev->drv->id, dev->devtype, finger);
}
/** \ingroup print_data
/**
* fp_print_data_save:
* @data: the stored print to save to disk
* @finger: the finger that this print corresponds to
*
* Saves a stored print to disk, assigned to a specific finger. Even though
* you are limited to storing only the 10 human fingers, this is a
* per-device-type limit. For example, you can store the users right index
@@ -219,9 +327,8 @@ static char *get_path_to_print(struct fp_dev *dev, enum fp_finger finger)
* This function will unconditionally overwrite a fingerprint previously
* saved for the same finger and device type. The print is saved in a hidden
* directory beneath the current user's home directory.
* \param data the stored print to save to disk
* \param finger the finger that this print corresponds to
* \returns 0 on success, non-zero on error.
*
* Returns: 0 on success, non-zero on error.
*/
API_EXPORTED int fp_print_data_save(struct fp_print_data *data,
enum fp_finger finger)
@@ -318,7 +425,13 @@ static int load_from_file(char *path, struct fp_print_data **data)
return 0;
}
/** \ingroup print_data
/**
* fp_print_data_load:
* @dev: the device you are loading the print for
* @finger: the finger of the file you are loading
* @data: output location to put the corresponding stored print. Must be
* freed with fp_print_data_free() after use.
* Loads a previously stored print from disk. The print must have been saved
* earlier using the fp_print_data_save() function.
*
@@ -326,11 +439,7 @@ static int load_from_file(char *path, struct fp_print_data **data)
* be found. Other error codes (both positive and negative) are possible for
* obscure error conditions (e.g. corruption).
*
* \param dev the device you are loading the print for
* \param finger the finger of the file you are loading
* \param data output location to put the corresponding stored print. Must be
* freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error
* Returns: 0 on success, non-zero on error
*/
API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
enum fp_finger finger, struct fp_print_data **data)
@@ -358,11 +467,14 @@ API_EXPORTED int fp_print_data_load(struct fp_dev *dev,
return 0;
}
/** \ingroup print_data
/**
* fp_print_data_delete:
* @dev: the device that the print belongs to
* @finger: the finger of the file you are deleting
* Removes a stored print from disk previously saved with fp_print_data_save().
* \param dev the device that the print belongs to
* \param finger the finger of the file you are deleting
* \returns 0 on success, negative on error
*
* Returns: 0 on success, negative on error
*/
API_EXPORTED int fp_print_data_delete(struct fp_dev *dev,
enum fp_finger finger)
@@ -380,18 +492,22 @@ API_EXPORTED int fp_print_data_delete(struct fp_dev *dev,
return r;
}
/** \ingroup print_data
* Attempts to load a stored print based on a \ref dscv_print
* "discovered print" record.
/**
* fp_print_data_from_dscv_print:
* @print: the discovered print
* @data: output location to point to the corresponding stored print. Must
* be freed with fp_print_data_free() after use.
* Attempts to load a stored print based on a #fp_dscv_print
* discovered print record.
*
* A return code of -ENOENT indicates that the file referred to by the
* discovered print could not be found. Other error codes (both positive and
* negative) are possible for obscure error conditions (e.g. corruption).
*
* \param print the discovered print
* \param data output location to point to the corresponding stored print. Must
* be freed with fp_print_data_free() after use.
* \returns 0 on success, non-zero on error.
* Returns: 0 on success, non-zero on error.
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
struct fp_print_data **data)
@@ -399,40 +515,53 @@ API_EXPORTED int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
return load_from_file(print->path, data);
}
/** \ingroup print_data
/**
* fp_print_data_free:
* @data: the stored print to destroy. If NULL, function simply returns.
*
* Frees a stored print. Must be called when you are finished using the print.
* \param data the stored print to destroy. If NULL, function simply returns.
*/
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);
}
/** \ingroup print_data
* Gets the \ref driver_id "driver ID" for a stored print. The driver ID
/**
* fp_print_data_get_driver_id:
* @data: the stored print
* Gets the [driver ID](advanced-topics.html#driver_id) for a stored print. The driver ID
* indicates which driver the print originally came from. The print is
* only usable with a device controlled by that driver.
* \param data the stored print
* \returns the driver ID of the driver compatible with the print
*
* Returns: the driver ID of the driver compatible with the print
*/
API_EXPORTED uint16_t fp_print_data_get_driver_id(struct fp_print_data *data)
{
return data->driver_id;
}
/** \ingroup print_data
* Gets the \ref devtype "devtype" for a stored print. The devtype represents
/**
* fp_print_data_get_devtype:
* @data: the stored print
* Gets the [devtype](advanced-topics.html#device-types) for a stored print. The devtype represents
* which type of device under the parent driver is compatible with the print.
* \param data the stored print
* \returns the devtype of the device range compatible with the print
*
* Returns: the devtype of the device range compatible with the print
*/
API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
{
return data->devtype;
}
/** @defgroup dscv_print Print discovery
* The \ref print_data "stored print" documentation detailed a simple API
/**
* SECTION:dscv_print
* @title: Print discovery (deprecated)
*
* The [stored print](libfprint-Stored-prints.html) documentation detailed a simple API
* for storing per-device prints for a single user, namely
* fp_print_data_save(). It also detailed a load function,
* fp_print_data_load(), but usage of this function is limited to scenarios
@@ -443,7 +572,7 @@ API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
* previously saved prints, potentially even before device discovery. These
* functions are designed to offer this functionality to you.
*
* Discovered prints are stored in a <tt>dscv_print</tt> structure, and you
* Discovered prints are stored in a #fp_dscv_print structure, and you
* can use functions documented below to access some information about these
* prints. You can determine if a discovered print appears to be compatible
* with a device using functions such as fp_dscv_dev_supports_dscv_print() and
@@ -461,6 +590,10 @@ API_EXPORTED uint32_t fp_print_data_get_devtype(struct fp_print_data *data)
* circumstances it may turn out that the print is corrupt or not for the
* device that it appeared to be. Also, it is possible that the print may have
* been deleted by the time you come to load it.
*
* Note that this portion of the library is deprecated. All that it offers is
* already implementable using publicly available functions, and its usage is
* unnecessarily restrictive in terms of how it stores data.
*/
static GSList *scan_dev_store_dir(char *devpath, uint16_t driver_id,
@@ -544,11 +677,16 @@ static GSList *scan_driver_store_dir(char *drvpath, uint16_t driver_id,
return list;
}
/** \ingroup dscv_print
/**
* fp_discover_prints:
*
* Scans the users home directory and returns a list of prints that were
* previously saved using fp_print_data_save().
* \returns a NULL-terminated list of discovered prints, must be freed with
*
* Returns: a %NULL-terminated list of discovered prints, must be freed with
* fp_dscv_prints_free() after use.
*
* Deprecated: Do not use.
*/
API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
{
@@ -605,12 +743,16 @@ API_EXPORTED struct fp_dscv_print **fp_discover_prints(void)
return list;
}
/** \ingroup dscv_print
/**
* fp_dscv_prints_free:
* @prints: the list of discovered prints. If NULL, function simply
* returns.
*
* Frees a list of discovered prints. This function also frees the discovered
* prints themselves, so make sure you do not use any discovered prints
* after calling this function.
* \param prints the list of discovered prints. If NULL, function simply
* returns.
*
* Deprecated: Do not use.
*/
API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
{
@@ -628,47 +770,67 @@ API_EXPORTED void fp_dscv_prints_free(struct fp_dscv_print **prints)
g_free(prints);
}
/** \ingroup dscv_print
* Gets the \ref driver_id "driver ID" for a discovered print. The driver ID
/**
* fp_dscv_print_get_driver_id:
* @print: the discovered print
*
* Gets the [driver ID](advanced-topics.html#driver_id) for a discovered print. The driver ID
* indicates which driver the print originally came from. The print is only
* usable with a device controlled by that driver.
* \param print the discovered print
* \returns the driver ID of the driver compatible with the print
*
* Returns: the driver ID of the driver compatible with the print
*
* Deprecated: Do not use.
*/
API_EXPORTED uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print)
{
return print->driver_id;
}
/** \ingroup dscv_print
* Gets the \ref devtype "devtype" for a discovered print. The devtype
/**
* fp_dscv_print_get_devtype:
* @print: the discovered print
*
* Gets the [devtype](advanced-topics.html#device-types) for a discovered print. The devtype
* represents which type of device under the parent driver is compatible
* with the print.
* \param print the discovered print
* \returns the devtype of the device range compatible with the print
*
* Returns: the devtype of the device range compatible with the print
*
* Deprecated: Do not use.
*/
API_EXPORTED uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print)
{
return print->devtype;
}
/** \ingroup dscv_print
/**
* fp_dscv_print_get_finger:
* @print: discovered print
*
* Gets the finger code for a discovered print.
* \param print discovered print
* \returns a finger code from #fp_finger
*
* Returns: a finger code from #fp_finger
*
* Deprecated: Do not use.
*/
API_EXPORTED enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print)
{
return print->finger;
}
/** \ingroup dscv_print
/**
* fp_dscv_print_delete:
* @print: the discovered print to remove from disk
*
* Removes a discovered print from disk. After successful return of this
* function, functions such as fp_dscv_print_get_finger() will continue to
* operate as before, however calling fp_print_data_from_dscv_print() will
* fail for obvious reasons.
* \param print the discovered print to remove from disk
* \returns 0 on success, negative on error
*
* Returns: 0 on success, negative on error
*
* Deprecated: Do not use.
*/
API_EXPORTED int fp_dscv_print_delete(struct fp_dscv_print *print)
{

View File

@@ -25,15 +25,8 @@
#define FP_COMPONENT "aes1610"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "driver_ids.h"
#include "drivers_api.h"
#include "aeslib.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
@@ -66,6 +59,7 @@ static int adjust_gain(unsigned char *buffer, int status);
#define FRAME_WIDTH 128
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 350
@@ -80,6 +74,13 @@ struct aes1610_dev {
uint8_t blanks_count;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes1610_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
@@ -129,6 +130,7 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
struct fp_dev *dev;
int r;
if (!transfer) {
@@ -137,7 +139,8 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
}
data = g_malloc(bytes);
libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes,
dev = fpi_ssm_get_dev(ssm);
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_IN, data, bytes,
generic_ignore_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -230,7 +233,7 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_da
}
data = g_malloc(19);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 19,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, 19,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -244,7 +247,7 @@ 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 aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -404,7 +407,7 @@ static unsigned char list_BE_values[10] = {
/*
* The different possible values for 0xBD register */
static unsigned char list_BD_values[10] = {
0x48, 0x4B, 0x4F, 0x52, 0x57, 0x59, 0x5B
0x28, 0x2b, 0x30, 0x3b, 0x45, 0x49, 0x4B
};
/*
@@ -425,25 +428,25 @@ static int adjust_gain(unsigned char *buffer, int status)
strip_scan_reqs[0].value = 0x6B;
strip_scan_reqs[1].value = 0x06;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x5B;
strip_scan_reqs[3].value = 0x4B;
}
else if (buffer[1] > 0x55) {
strip_scan_reqs[0].value = 0x63;
strip_scan_reqs[1].value = 0x15;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4F;
strip_scan_reqs[3].value = 0x3b;
}
else if (buffer[1] > 0x40 || buffer[16] > 0x19) {
strip_scan_reqs[0].value = 0x43;
strip_scan_reqs[1].value = 0x13;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x4B;
strip_scan_reqs[3].value = 0x30;
}
else { // minimum gain needed
strip_scan_reqs[0].value = 0x23;
strip_scan_reqs[1].value = 0x07;
strip_scan_reqs[2].value = 0x35;
strip_scan_reqs[3].value = 0x48;
strip_scan_reqs[3].value = 0x28;
}
// Now copy this values in capture_reqs
@@ -555,8 +558,8 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
{
unsigned char *stripdata;
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
int sum, i;
@@ -579,11 +582,14 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
}
if (sum > 0) {
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(128 * 4);
memcpy(stripdata, data + 1, 128 * 4);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
aesdev->strips_len++;
/* FIXME: would preallocating strip buffers be a decent optimization? */
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * (FRAME_HEIGHT / 2) + sizeof(struct fpi_frame));
stripe->delta_x = 0;
stripe->delta_y = 0;
stripdata = stripe->data;
memcpy(stripdata, data + 1, FRAME_WIDTH * (FRAME_HEIGHT / 2));
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
aesdev->blanks_count = 0;
}
@@ -614,8 +620,9 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
/* send stop capture bits */
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
fpi_do_movement_estimation(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -638,11 +645,11 @@ out:
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS:
fp_dbg("write reqs");
aes_write_regv(dev, capture_reqs, G_N_ELEMENTS(capture_reqs),
@@ -670,7 +677,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(665);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 665,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, 665,
capture_read_strip_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -685,14 +692,14 @@ static void capture_run_state(struct fpi_ssm *ssm)
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes1610_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
fp_dbg("");
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -700,7 +707,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
@@ -708,9 +715,9 @@ static void start_capture(struct fp_img_dev *dev)
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
G_DEBUG_HERE();
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -720,10 +727,11 @@ static const struct aes_regwrite init[] = {
{ 0x82, 0x00 }
};
/*
static const struct aes_regwrite stop_reader[] = {
{ 0xFF, 0x00 }
};
*/
enum activate_states {
WRITE_INIT,
@@ -732,11 +740,11 @@ enum activate_states {
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
/* activation on aes1610 seems much more straightforward compared to aes2501 */
/* verify theres anything missing here */
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
fp_dbg("write init");
aes_write_regv(dev, init, G_N_ELEMENTS(init), generic_write_regv_cb, ssm);
@@ -747,21 +755,21 @@ static void activate_run_state(struct fpi_ssm *ssm)
/* jump to finger detection */
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
fp_dbg("status %d", ssm->error);
fpi_imgdev_activate_complete(dev, ssm->error);
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct aes1610_dev *aesdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
@@ -769,7 +777,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
@@ -777,8 +785,8 @@ static void dev_deactivate(struct fp_img_dev *dev)
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes1610_dev *aesdev = dev->priv;
fp_dbg("");
struct aes1610_dev *aesdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
@@ -795,22 +803,26 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes1610_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes1610_dev));
aesdev = g_malloc0(sizeof(struct aes1610_dev));
fpi_imgdev_set_user_data(dev, aesdev);
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);
struct aes1610_dev *aesdev;
aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
@@ -829,13 +841,9 @@ struct fp_img_driver aes1610_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 128,
.img_width = IMAGE_WIDTH,
/* 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 = 20,
.open = dev_init,
.close = dev_deinit,

View File

@@ -19,21 +19,20 @@
#define FP_COMPONENT "aes1660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes1660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 128
#define SCALE_FACTOR 2
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
@@ -41,22 +40,23 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fpi_imgdev_set_user_data(dev, aesdev);
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
aesdev->h_scale_factor = SCALE_FACTOR;
aesdev->init_seqs[0] = aes1660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes1660_init_1);
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes1660_init_1);
aesdev->init_seqs[1] = aes1660_init_2;
aesdev->init_seqs_len[1] = array_n_elements(aes1660_init_2);
aesdev->init_seqs_len[1] = G_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;
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
@@ -64,10 +64,10 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
@@ -102,13 +102,8 @@ struct fp_img_driver aes1660_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH * SCALE_FACTOR,
/* 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 = 25,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.bz3_threshold = 20,
.open = dev_init,
.close = dev_deinit,

View File

@@ -23,16 +23,9 @@
#define FP_COMPONENT "aes2501"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.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);
@@ -58,6 +51,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 16
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
/* maximum number of frames to read during a scan */
/* FIXME reduce substantially */
#define MAX_FRAMES 150
@@ -72,6 +66,13 @@ struct aes2501_dev {
int no_finger_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
typedef void (*aes2501_read_regs_cb)(struct fp_img_dev *dev, int status,
unsigned char *regs, void *user_data);
@@ -121,7 +122,7 @@ static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
}
data = g_malloc(126);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 126,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, 126,
read_regs_data_cb, rdata, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -146,7 +147,7 @@ static void read_regs(struct fp_img_dev *dev, aes2501_read_regs_cb callback,
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
struct aes2501_read_regs *rdata = g_malloc(sizeof(*rdata));
fp_dbg("");
G_DEBUG_HERE();
regwrite->reg = AES2501_REG_CTRL2;
regwrite->value = AES2501_CTRL2_READ_REGS;
rdata->dev = dev;
@@ -208,6 +209,7 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
int r;
if (!transfer) {
@@ -216,7 +218,7 @@ static void generic_read_ignore_data(struct fpi_ssm *ssm, size_t bytes)
}
data = g_malloc(bytes);
libusb_fill_bulk_transfer(transfer, ssm->dev->udev, EP_IN, data, bytes,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_IN, data, bytes,
generic_ignore_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -331,7 +333,7 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
}
data = g_malloc(20);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 20,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, 20,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -344,8 +346,8 @@ static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
static void start_finger_detection(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
fp_dbg("");
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -435,8 +437,8 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
{
unsigned char *stripdata;
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
int sum;
int threshold;
@@ -483,8 +485,11 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
img = aes_assemble(aesdev->strips, aesdev->strips_len,
FRAME_WIDTH, FRAME_HEIGHT);
fpi_do_movement_estimation(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -498,10 +503,13 @@ static void capture_read_strip_cb(struct libusb_transfer *transfer)
} else {
/* obtain next strip */
/* FIXME: would preallocating strip buffers be a decent optimization? */
stripdata = g_malloc(192 * 8);
struct fpi_frame *stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame));
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, stripdata);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
@@ -514,11 +522,11 @@ out:
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS_1:
aes_write_regv(dev, capture_reqs_1, G_N_ELEMENTS(capture_reqs_1),
generic_write_regv_cb, ssm);
@@ -550,7 +558,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(1705);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, 1705,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, 1705,
capture_read_strip_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -565,14 +573,14 @@ static void capture_run_state(struct fpi_ssm *ssm)
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2501_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
fp_dbg("");
G_DEBUG_HERE();
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -580,7 +588,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
@@ -591,9 +599,9 @@ static void start_capture(struct fp_img_dev *dev)
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;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
G_DEBUG_HERE();
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -706,7 +714,7 @@ 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 aes2501_dev *aesdev = dev->priv;
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
if (status != 0) {
fpi_ssm_mark_aborted(ssm, status);
@@ -731,7 +739,7 @@ static void activate_init3_cb(struct fp_img_dev *dev, int result,
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
/* This state machine isn't as linear as it may appear. After doing init1
* and init2 register configuration writes, we have to poll a register
@@ -752,7 +760,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
aes_write_regv(init_4);
*/
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT_1:
aes_write_regv(dev, init_1, G_N_ELEMENTS(init_1),
generic_write_regv_cb, ssm);
@@ -769,7 +777,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
read_regs(dev, activate_read_regs_cb, ssm);
break;
case WRITE_INIT_3:
aes_write_regv(dev, init_4, G_N_ELEMENTS(init_4),
aes_write_regv(dev, init_3, G_N_ELEMENTS(init_3),
activate_init3_cb, ssm);
break;
case WRITE_INIT_4:
@@ -785,21 +793,21 @@ static void activate_run_state(struct fpi_ssm *ssm)
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);
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct aes2501_dev *aesdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, activate_run_state,
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, dev);
aesdev->read_regs_retry_count = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
@@ -807,7 +815,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
/* FIXME: audit cancellation points, probably need more, specifically
* in error handling paths? */
aesdev->deactivating = TRUE;
@@ -815,8 +823,8 @@ static void dev_deactivate(struct fp_img_dev *dev)
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2501_dev *aesdev = dev->priv;
fp_dbg("");
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
/* FIXME: if we're in the middle of a scan, we should cancel the scan.
* maybe we can do this with a master reset, unconditionally? */
@@ -832,22 +840,25 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
/* FIXME check endpoints */
int r;
struct aes2501_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes2501_dev));
aesdev = g_malloc0(sizeof(struct aes2501_dev));
fpi_imgdev_set_user_data(dev, aesdev);
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);
struct aes2501_dev *aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
@@ -867,7 +878,7 @@ struct fp_img_driver aes2501_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = IMAGE_WIDTH,
.open = dev_init,
.close = dev_deinit,

View File

@@ -23,16 +23,9 @@
#define FP_COMPONENT "aes2550"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aes2550.h"
#include "driver_ids.h"
#include "aeslib.h"
static void start_capture(struct fp_img_dev *dev);
static void complete_deactivation(struct fp_img_dev *dev);
@@ -56,6 +49,7 @@ static void complete_deactivation(struct fp_img_dev *dev);
#define FRAME_WIDTH 192
#define FRAME_HEIGHT 8
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
struct aes2550_dev {
GSList *strips;
@@ -64,6 +58,13 @@ struct aes2550_dev {
int heartbeat_cnt;
};
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
/****** FINGER PRESENCE DETECTION ******/
static unsigned char finger_det_reqs[] = {
@@ -132,7 +133,7 @@ static void finger_det_reqs_cb(struct libusb_transfer *t)
/* 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,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -148,9 +149,9 @@ exit_free_transfer:
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct aes2550_dev *aesdev = dev->priv;
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct libusb_transfer *transfer;
fp_dbg("");
G_DEBUG_HERE();
if (aesdev->deactivating) {
complete_deactivation(dev);
@@ -162,7 +163,7 @@ static void start_finger_detection(struct fp_img_dev *dev)
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, finger_det_reqs,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT, finger_det_reqs,
sizeof(finger_det_reqs), finger_det_reqs_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -202,8 +203,9 @@ enum capture_states {
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 fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_frame *stripe;
int len;
if (data[0] != AES2550_EDATA_MAGIC) {
@@ -214,11 +216,16 @@ static int process_strip_data(struct fpi_ssm *ssm, unsigned char *data)
if (len != (AES2550_STRIP_SIZE - 3)) {
fp_dbg("Bogus frame len: %.4x\n", len);
}
stripdata = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2); /* 4 bits per pixel */
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 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, stripdata);
aesdev->strips = g_slist_prepend(aesdev->strips, stripe);
aesdev->strips_len++;
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
return 0;
}
@@ -238,16 +245,18 @@ static void capture_reqs_cb(struct libusb_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
(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);
img = fpi_assemble_frames(&assembling_ctx,
aesdev->strips, aesdev->strips_len);
img->flags |= FP_IMG_PARTIAL;
g_slist_free_full(aesdev->strips, g_free);
aesdev->strips = NULL;
aesdev->strips_len = 0;
@@ -264,8 +273,8 @@ static void capture_set_idle_reqs_cb(struct libusb_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
int r;
@@ -316,10 +325,10 @@ out:
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_REQS:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
@@ -327,7 +336,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_reqs,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT, capture_reqs,
sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -347,7 +356,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -365,7 +374,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, capture_set_idle_reqs,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), 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) {
@@ -379,14 +388,14 @@ static void capture_run_state(struct fpi_ssm *ssm)
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aes2550_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
fp_dbg("Capture completed");
if (aesdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -394,7 +403,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
@@ -403,9 +412,9 @@ static void start_capture(struct fp_img_dev *dev)
}
aesdev->heartbeat_cnt = 0;
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
G_DEBUG_HERE();
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -476,10 +485,10 @@ static void calibrate_read_data_cb(struct libusb_transfer *transfer)
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
@@ -487,7 +496,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, init_reqs,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT, init_reqs,
sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -507,7 +516,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
init_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -525,7 +534,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, calibrate_reqs,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT, calibrate_reqs,
sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -545,7 +554,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN, data, AES2550_EP_IN_BUF_SIZE,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
calibrate_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -561,35 +570,35 @@ static void activate_run_state(struct fpi_ssm *ssm)
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);
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
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,
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, 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;
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
aesdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aes2550_dev *aesdev = dev->priv;
fp_dbg("");
struct aes2550_dev *aesdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);
@@ -602,22 +611,26 @@ 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 aes2550_dev *aes2550_dev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes2550_dev));
aes2550_dev = g_malloc0(sizeof(struct aes2550_dev));
fpi_imgdev_set_user_data(dev, 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);
struct aes2550_dev *aesdev;
aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
@@ -637,7 +650,7 @@ struct fp_img_driver aes2550_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = 192,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

View File

@@ -19,20 +19,20 @@
#define FP_COMPONENT "aes2660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
#include "aes2660.h"
#include "driver_ids.h"
#define FRAME_WIDTH 192
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = FRAME_WIDTH,
.frame_height = AESX660_FRAME_HEIGHT,
.image_width = IMAGE_WIDTH,
.get_pixel = aes_get_pixel,
};
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
@@ -40,23 +40,24 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
int r;
struct aesX660_dev *aesdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = aesdev = g_malloc0(sizeof(struct aesX660_dev));
aesdev = g_malloc0(sizeof(struct aesX660_dev));
fpi_imgdev_set_user_data(dev, aesdev);
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
/* No scaling for AES2660 */
aesdev->h_scale_factor = 1;
aesdev->init_seqs[0] = aes2660_init_1;
aesdev->init_seqs_len[0] = array_n_elements(aes2660_init_1);
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes2660_init_1);
aesdev->init_seqs[1] = aes2660_init_2;
aesdev->init_seqs_len[1] = array_n_elements(aes2660_init_2);
aesdev->init_seqs_len[1] = G_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;
aesdev->assembling_ctx = &assembling_ctx;
aesdev->extra_img_flags = FP_IMG_PARTIAL;
fpi_imgdev_open_complete(dev, 0);
return 0;
@@ -64,10 +65,11 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_deinit(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
struct aesX660_dev *aesdev;
aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev->buffer);
g_free(aesdev);
libusb_release_interface(dev->udev, 0);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
@@ -103,7 +105,7 @@ struct fp_img_driver aes2660_driver = {
},
.flags = 0,
.img_height = -1,
.img_width = FRAME_WIDTH,
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
.open = dev_init,
.close = dev_deinit,

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

@@ -0,0 +1,183 @@
/*
* 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 "drivers_api.h"
#include "aeslib.h"
#include "aes3k.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(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
aesdev = g_malloc0(sizeof(struct aes3k_dev));
fpi_imgdev_set_user_data(dev, aesdev);
if (!aesdev)
return -ENOMEM;
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 = fpi_imgdev_get_user_data(dev);
g_free(aesdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 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,
};

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

@@ -0,0 +1,163 @@
/*
* 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 "drivers_api.h"
#include "aeslib.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 = fpi_imgdev_get_user_data(dev);
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 = fpi_imgdev_get_user_data(dev);
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, fpi_imgdev_get_usb_dev(dev), 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 = fpi_imgdev_get_user_data(dev);
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 = fpi_imgdev_get_user_data(dev);
/* 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
@@ -19,32 +26,20 @@
#define FP_COMPONENT "aes4000"
#include <errno.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aes3k.h"
#include <glib.h>
#include <libusb.h>
#define DATA_BUFLEN 0x1259
#include <aeslib.h>
#include <fp_internal.h>
/* 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
#include "driver_ids.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
#define IMG_HEIGHT 96
#define IMG_WIDTH 96
#define ENLARGE_FACTOR 3
struct aes4k_dev {
struct libusb_transfer *img_trf;
};
static const struct aes_regwrite init_reqs[] = {
static struct aes_regwrite init_reqs[] = {
/* master reset */
{ 0x80, 0x01 },
{ 0, 0 },
@@ -119,131 +114,44 @@ 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, 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");
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = g_malloc0(sizeof(struct aes4k_dev));
aesdev = g_malloc0(sizeof(struct aes3k_dev));
fpi_imgdev_set_user_data(dev, aesdev);
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
if (!aesdev)
return -ENOMEM;
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)
{
g_free(dev->priv);
libusb_release_interface(dev->udev, 0);
struct aes3k_dev *aesdev = fpi_imgdev_get_user_data(dev);
g_free(aesdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x08ff, .product = 0x5501 },
{ 0, 0, 0, },
@@ -258,15 +166,15 @@ struct fp_img_driver aes4000_driver = {
.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,
};

View File

@@ -23,16 +23,8 @@
#define FP_COMPONENT "aesX660"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <aeslib.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "aeslib.h"
#include "aesx660.h"
static void start_capture(struct fp_img_dev *dev);
@@ -41,14 +33,14 @@ 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 FRAME_HEIGHT AESX660_FRAME_HEIGHT
#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 fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
@@ -57,7 +49,7 @@ static void aesX660_send_cmd_timeout(struct fpi_ssm *ssm, const unsigned char *c
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT,
(unsigned char *)cmd, cmd_len,
callback, ssm, timeout);
r = libusb_submit_transfer(transfer);
@@ -77,7 +69,7 @@ static void aesX660_send_cmd(struct fpi_ssm *ssm, const unsigned char *cmd,
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 fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
@@ -88,7 +80,7 @@ static void aesX660_read_response(struct fpi_ssm *ssm, size_t buf_len,
}
data = g_malloc(buf_len);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN,
data, buf_len,
callback, ssm, BULK_TIMEOUT);
@@ -152,8 +144,8 @@ enum finger_det_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
aesdev->fd_data_transfer = NULL;
@@ -205,9 +197,9 @@ static void finger_det_set_idle_cmd_cb(struct libusb_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Finger detection completed");
fpi_imgdev_report_finger_status(dev, TRUE);
@@ -225,7 +217,7 @@ static void finger_det_sm_complete(struct fpi_ssm *ssm)
static void finger_det_run_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case FINGER_DET_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_blink_cmd, sizeof(led_blink_cmd),
aesX660_send_cmd_cb);
@@ -248,15 +240,15 @@ static void finger_det_run_state(struct fpi_ssm *ssm)
static void start_finger_detection(struct fp_img_dev *dev)
{
struct fpi_ssm *ssm;
struct aesX660_dev *aesdev = dev->priv;
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
if (aesdev->deactivating) {
complete_deactivation(dev);
return;
}
ssm = fpi_ssm_new(dev->dev, finger_det_run_state, FINGER_DET_NUM_STATES);
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), finger_det_run_state, FINGER_DET_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, finger_det_sm_complete);
}
@@ -273,19 +265,25 @@ enum capture_states {
/* Returns number of processed bytes */
static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
{
struct fpi_frame *stripe;
unsigned char *stripdata;
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
stripdata = g_malloc(aesdev->frame_width * FRAME_HEIGHT / 2); /* 4 bits per pixel */
stripe = g_malloc(aesdev->assembling_ctx->frame_width * FRAME_HEIGHT / 2 + sizeof(struct fpi_frame)); /* 4 bpp */
stripdata = stripe->data;
fp_dbg("Processing frame %.2x %.2x", data[AESX660_IMAGE_OK_OFFSET],
data[AESX660_LAST_FRAME_OFFSET]);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->frame_width * FRAME_HEIGHT / 2);
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);
aesdev->strips = g_slist_prepend(aesdev->strips, stripdata);
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->assembling_ctx->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 {
@@ -297,27 +295,20 @@ static int process_stripe_data(struct fpi_ssm *ssm, unsigned char *data)
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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
struct fp_img *img, *tmp;
struct fp_img *img;
aesdev->strips = g_slist_reverse(aesdev->strips);
tmp = aes_assemble(aesdev->strips, aesdev->strips_len,
aesdev->frame_width, FRAME_HEIGHT);
img = fpi_assemble_frames(aesdev->assembling_ctx, aesdev->strips, aesdev->strips_len);
img->flags |= aesdev->extra_img_flags;
g_slist_foreach(aesdev->strips, (GFunc) g_free, NULL);
g_slist_free(aesdev->strips);
aesdev->strips = NULL;
aesdev->strips_len = 0;
if (aesdev->h_scale_factor > 1) {
img = fpi_im_resize(tmp, aesdev->h_scale_factor, 1);
fp_img_free(tmp);
} else {
img = tmp;
tmp = NULL;
}
fpi_imgdev_image_captured(dev, img);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_mark_completed(ssm);
@@ -330,8 +321,8 @@ static void capture_set_idle_cmd_cb(struct libusb_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
int finger_missing = 0;
size_t copied, actual_len = transfer->actual_length;
@@ -341,7 +332,7 @@ static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
goto out;
}
fp_dbg("Got %d bytes of data", actual_len);
fp_dbg("Got %lu bytes of data", actual_len);
do {
copied = min(aesdev->buffer_max - aesdev->buffer_size, actual_len);
memcpy(aesdev->buffer + aesdev->buffer_size,
@@ -350,13 +341,13 @@ static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
actual_len -= copied;
data += copied;
aesdev->buffer_size += copied;
fp_dbg("Copied %.4x bytes into internal buffer",
fp_dbg("Copied %.4lx 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_SIZE_MSB_OFFSET] << 8) + AESX660_HEADER_SIZE;
fp_dbg("Got frame, type %.2x size %.4lx",
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
aesdev->buffer_max);
continue;
@@ -382,10 +373,10 @@ out:
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_SEND_LED_CMD:
aesX660_send_cmd(ssm, led_solid_cmd, sizeof(led_solid_cmd),
aesX660_send_cmd_cb);
@@ -402,7 +393,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
capture_read_stripe_data_cb);
break;
case CAPTURE_SET_IDLE:
fp_dbg("Got %d frames\n", aesdev->strips_len);
fp_dbg("Got %lu frames\n", aesdev->strips_len);
aesX660_send_cmd(ssm, set_idle_cmd, sizeof(set_idle_cmd),
capture_set_idle_cmd_cb);
break;
@@ -411,9 +402,9 @@ static void capture_run_state(struct fpi_ssm *ssm)
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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
int err = fpi_ssm_get_error(ssm);
fp_dbg("Capture completed");
fpi_ssm_free(ssm);
@@ -428,7 +419,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
if (aesdev->deactivating) {
@@ -436,9 +427,9 @@ static void start_capture(struct fp_img_dev *dev)
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
G_DEBUG_HERE();
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, capture_sm_complete);
}
@@ -458,8 +449,8 @@ enum activate_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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
@@ -509,8 +500,8 @@ out:
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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
fp_dbg("read_init_cb\n");
@@ -545,10 +536,10 @@ out:
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct aesX660_dev *aesdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case ACTIVATE_SET_IDLE:
aesdev->init_seq_idx = 0;
fp_dbg("Activate: set idle\n");
@@ -591,8 +582,8 @@ static void activate_run_state(struct fpi_ssm *ssm)
static void activate_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int err = ssm->error;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int err = fpi_ssm_get_error(ssm);
fp_dbg("status %d", err);
fpi_imgdev_activate_complete(dev, err);
fpi_ssm_free(ssm);
@@ -603,16 +594,16 @@ static void activate_sm_complete(struct fpi_ssm *ssm)
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,
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, 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;
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
if (aesdev->fd_data_transfer)
libusb_cancel_transfer(aesdev->fd_data_transfer);
@@ -622,8 +613,8 @@ void aesX660_dev_deactivate(struct fp_img_dev *dev)
static void complete_deactivation(struct fp_img_dev *dev)
{
struct aesX660_dev *aesdev = dev->priv;
fp_dbg("");
struct aesX660_dev *aesdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
aesdev->deactivating = FALSE;
g_slist_free(aesdev->strips);

View File

@@ -23,7 +23,7 @@
#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_RESPONSE_SIZE_MSB_OFFSET 0x02
#define AESX660_CALIBRATE_RESPONSE 0x06
#define AESX660_FINGER_DET_RESPONSE 0x40
@@ -35,9 +35,14 @@
#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
#define AESX660_FRAME_HEIGHT 8
struct aesX660_dev {
GSList *strips;
size_t strips_len;
@@ -52,12 +57,12 @@ struct aesX660_dev {
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 fpi_frame_asmbl_ctx *assembling_ctx;
uint16_t extra_img_flags;
};
struct aesX660_cmd {

View File

@@ -33,9 +33,15 @@ enum {
VFS101_ID = 10,
VFS301_ID = 11,
AES2550_ID = 12,
UPEKE2_ID = 13,
/* UPEKE2_ID = 13 */
AES1660_ID = 14,
AES2660_ID = 15,
AES3500_ID = 16,
UPEKTC_IMG_ID = 17,
ETES603_ID = 18,
VFS5011_ID = 19,
VFS0050_ID = 20,
ELAN_ID = 21,
};
#endif

625
libfprint/drivers/elan.c Normal file
View File

@@ -0,0 +1,625 @@
/*
* Elan driver for libfprint
*
* Copyright (C) 2017 Igor Filatov <ia.filatov@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 "elan"
#include "drivers_api.h"
#include "elan.h"
unsigned char elan_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
struct fpi_frame *frame, unsigned int x,
unsigned int y)
{
return frame->data[x + y * ctx->frame_width];
}
static struct fpi_frame_asmbl_ctx assembling_ctx = {
.frame_width = 0,
.frame_height = 0,
.image_width = 0,
.get_pixel = elan_get_pixel,
};
struct elan_dev {
gboolean deactivating;
const struct elan_cmd *cmds;
size_t cmds_len;
int cmd_idx;
int cmd_timeout;
struct libusb_transfer *cur_transfer;
unsigned char *last_read;
unsigned char frame_width;
unsigned char frame_height;
unsigned char raw_frame_width;
int num_frames;
GSList *frames;
};
static void elan_dev_reset(struct elan_dev *elandev)
{
G_DEBUG_HERE();
BUG_ON(elandev->cur_transfer);
elandev->deactivating = FALSE;
elandev->cmds = NULL;
elandev->cmd_idx = 0;
elandev->cmd_timeout = ELAN_CMD_TIMEOUT;
g_free(elandev->last_read);
elandev->last_read = NULL;
g_slist_free_full(elandev->frames, g_free);
elandev->frames = NULL;
elandev->num_frames = 0;
}
static void elan_save_frame(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
unsigned char raw_height = elandev->frame_width;
unsigned char raw_width = elandev->raw_frame_width;
unsigned short *frame =
g_malloc(elandev->frame_width * elandev->frame_height * 2);
G_DEBUG_HERE();
/* Raw images are vertical and perpendicular to swipe direction of a
* normalized image, which means we need to make them horizontal before
* assembling. We also discard stirpes of ELAN_FRAME_MARGIN along raw
* height. */
for (int y = 0; y < raw_height; y++)
for (int x = ELAN_FRAME_MARGIN;
x < raw_width - ELAN_FRAME_MARGIN; x++) {
int frame_idx =
y + (x - ELAN_FRAME_MARGIN) * raw_height;
int raw_idx = x + y * raw_width;
frame[frame_idx] =
((unsigned short *)elandev->last_read)[raw_idx];
}
elandev->frames = g_slist_prepend(elandev->frames, frame);
elandev->num_frames += 1;
}
/* Transform raw sesnsor data to normalized 8-bit grayscale image. */
static void elan_process_frame(unsigned short *raw_frame, GSList ** frames)
{
unsigned int frame_size =
assembling_ctx.frame_width * assembling_ctx.frame_height;
struct fpi_frame *frame =
g_malloc(frame_size + sizeof(struct fpi_frame));
G_DEBUG_HERE();
unsigned short min = 0xffff, max = 0;
for (int i = 0; i < frame_size; i++) {
if (raw_frame[i] < min)
min = raw_frame[i];
if (raw_frame[i] > max)
max = raw_frame[i];
}
unsigned short px;
for (int i = 0; i < frame_size; i++) {
px = raw_frame[i];
if (px <= min)
px = 0;
else if (px >= max)
px = 0xff;
else
px = (px - min) * 0xff / (max - min);
frame->data[i] = (unsigned char)px;
}
*frames = g_slist_prepend(*frames, frame);
}
static void elan_submit_image(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
GSList *frames = NULL;
struct fp_img *img;
G_DEBUG_HERE();
for (int i = 0; i < ELAN_SKIP_LAST_FRAMES; i++)
elandev->frames = g_slist_next(elandev->frames);
assembling_ctx.frame_width = elandev->frame_width;
assembling_ctx.frame_height = elandev->frame_height;
assembling_ctx.image_width = elandev->frame_width * 3 / 2;
g_slist_foreach(elandev->frames, (GFunc) elan_process_frame, &frames);
fpi_do_movement_estimation(&assembling_ctx, frames,
elandev->num_frames - ELAN_SKIP_LAST_FRAMES);
img = fpi_assemble_frames(&assembling_ctx, frames,
elandev->num_frames - ELAN_SKIP_LAST_FRAMES);
img->flags |= FP_IMG_PARTIAL;
fpi_imgdev_image_captured(dev, img);
}
static void elan_cmd_done(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elandev->cmd_idx += 1;
if (elandev->cmd_idx < elandev->cmds_len)
elan_run_next_cmd(ssm);
else
fpi_ssm_next_state(ssm);
}
static void elan_cmd_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elandev->cur_transfer = NULL;
switch (transfer->status) {
case LIBUSB_TRANSFER_COMPLETED:
if (transfer->length != transfer->actual_length) {
fp_dbg("unexpected transfer length");
elan_dev_reset(elandev);
fpi_ssm_mark_aborted(ssm, -EPROTO);
} else if (transfer->endpoint & LIBUSB_ENDPOINT_IN)
/* just finished receiving */
elan_cmd_done(ssm);
else {
/* just finished sending */
if (elandev->cmds[elandev->cmd_idx].response_len)
elan_cmd_read(ssm);
else
elan_cmd_done(ssm);
}
break;
case LIBUSB_TRANSFER_CANCELLED:
fp_dbg("transfer cancelled");
fpi_ssm_mark_aborted(ssm, -ECANCELED);
break;
case LIBUSB_TRANSFER_TIMED_OUT:
fp_dbg("transfer timed out");
// elan_dev_reset(elandev);
fpi_ssm_mark_aborted(ssm, -ETIMEDOUT);
break;
default:
fp_dbg("transfer failed: %d", transfer->status);
elan_dev_reset(elandev);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
static void elan_cmd_read(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
int response_len = elandev->cmds[elandev->cmd_idx].response_len;
G_DEBUG_HERE();
if (elandev->cmds[elandev->cmd_idx].cmd == read_cmds[0].cmd)
/* raw data has 2-byte "pixels" and the frame is vertical */
response_len =
elandev->raw_frame_width * elandev->frame_width * 2;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
elandev->cur_transfer = transfer;
g_free(elandev->last_read);
elandev->last_read = g_malloc(response_len);
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev),
elandev->cmds[elandev->cmd_idx].response_in,
elandev->last_read, response_len, elan_cmd_cb,
ssm, elandev->cmd_timeout);
transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
int r = libusb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
}
static void elan_run_next_cmd(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
if (!transfer) {
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
elandev->cur_transfer = transfer;
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), ELAN_EP_CMD_OUT,
(unsigned char *)elandev->cmds[elandev->
cmd_idx].cmd,
ELAN_CMD_LEN, elan_cmd_cb, ssm,
elandev->cmd_timeout);
transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
int r = libusb_submit_transfer(transfer);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
}
static void elan_run_cmds(struct fpi_ssm *ssm, const struct elan_cmd *cmds,
size_t cmds_len, int cmd_timeout)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elandev->cmds = cmds;
elandev->cmds_len = cmds_len;
elandev->cmd_idx = 0;
if (cmd_timeout != -1)
elandev->cmd_timeout = cmd_timeout;
elan_run_next_cmd(ssm);
}
enum deactivate_states {
DEACTIVATE,
DEACTIVATE_NUM_STATES,
};
static void elan_deactivate_run_state(struct fpi_ssm *ssm)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case DEACTIVATE:
elan_run_cmds(ssm, deactivate_cmds, deactivate_cmds_len,
ELAN_CMD_TIMEOUT);
break;
}
}
static void deactivate_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fpi_imgdev_deactivate_complete(dev);
}
static void elan_deactivate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elan_dev_reset(elandev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_deactivate_run_state,
DEACTIVATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, deactivate_complete);
}
enum capture_states {
CAPTURE_START,
CAPTURE_WAIT_FINGER,
CAPTURE_READ_DATA,
CAPTURE_SAVE_FRAME,
CAPTURE_NUM_STATES,
};
static void elan_capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_START:
elan_run_cmds(ssm, capture_start_cmds, capture_start_cmds_len,
ELAN_CMD_TIMEOUT);
break;
case CAPTURE_WAIT_FINGER:
elan_run_cmds(ssm, capture_wait_finger_cmds,
capture_wait_finger_cmds_len, -1);
break;
case CAPTURE_READ_DATA:
/* 0x55 - finger present
* 0xff - device not calibrated */
if (elandev->last_read && elandev->last_read[0] == 0x55) {
fpi_imgdev_report_finger_status(dev, TRUE);
elan_run_cmds(ssm, read_cmds, read_cmds_len,
ELAN_CMD_TIMEOUT);
} else
fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY);
break;
case CAPTURE_SAVE_FRAME:
elan_save_frame(dev);
if (elandev->num_frames < ELAN_MAX_FRAMES) {
/* quickly stop if finger is removed */
elandev->cmd_timeout = ELAN_FINGER_TIMEOUT;
fpi_ssm_jump_to_state(ssm, CAPTURE_WAIT_FINGER);
}
break;
}
}
static void elan_capture_async(void *data)
{
elan_capture((struct fp_img_dev *)data);
}
static void capture_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
if (elandev->deactivating)
elan_deactivate(dev);
/* either max frames captured or timed out waiting for the next frame */
else if (!fpi_ssm_get_error(ssm)
|| (fpi_ssm_get_error(ssm) == -ETIMEDOUT
&& fpi_ssm_get_cur_state(ssm) == CAPTURE_WAIT_FINGER))
if (elandev->num_frames >= ELAN_MIN_FRAMES) {
elan_submit_image(dev);
fpi_imgdev_report_finger_status(dev, FALSE);
} else
fpi_imgdev_session_error(dev,
FP_VERIFY_RETRY_TOO_SHORT);
/* other error
* It says "...session_error" but repotring 1 during verification
* makes it successful! */
else
fpi_imgdev_session_error(dev, FP_VERIFY_NO_MATCH);
/* When enrolling the lib won't restart the capture after a stage has
* completed, so we need to keep feeding it images till it's had enough.
* But after that it can't finalize enrollemnt until this callback exits.
* That's why we schedule elan_capture instead of running it directly. */
if (fpi_dev_get_dev_state(fpi_imgdev_get_dev(dev)) == DEV_STATE_ENROLLING
&& !fpi_timeout_add(10, elan_capture_async, dev))
fpi_imgdev_session_error(dev, -ETIME);
fpi_ssm_free(ssm);
}
static void elan_capture(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elan_dev_reset(elandev);
struct fpi_ssm *ssm =
fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_capture_run_state, CAPTURE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, capture_complete);
}
enum calibrate_states {
CALIBRATE_START_1,
CALIBRATE_READ_DATA_1,
CALIBRATE_END_1,
CALIBRATE_START_2,
CALIBRATE_READ_DATA_2,
CALIBRATE_END_2,
CALIBRATE_NUM_STATES,
};
static void elan_calibrate_run_state(struct fpi_ssm *ssm)
{
switch (fpi_ssm_get_cur_state(ssm)) {
case CALIBRATE_START_1:
case CALIBRATE_START_2:
elan_run_cmds(ssm, calibrate_start_cmds,
calibrate_start_cmds_len, ELAN_CMD_TIMEOUT);
break;
case CALIBRATE_READ_DATA_1:
case CALIBRATE_READ_DATA_2:
elan_run_cmds(ssm, read_cmds, read_cmds_len, ELAN_CMD_TIMEOUT);
break;
case CALIBRATE_END_1:
case CALIBRATE_END_2:
elan_run_cmds(ssm, calibrate_end_cmds, calibrate_end_cmds_len,
ELAN_CMD_TIMEOUT);
}
}
static void calibrate_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
if (elandev->deactivating)
elan_deactivate(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else {
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
elan_capture(dev);
}
fpi_ssm_free(ssm);
}
static void elan_calibrate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elan_dev_reset(elandev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_calibrate_run_state,
CALIBRATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, calibrate_complete);
}
enum activate_states {
ACTIVATE_GET_SENSOR_DIM,
ACTIVATE_SET_SENSOR_DIM,
ACTIVATE_START,
ACTIVATE_READ_DATA,
ACTIVATE_END,
ACTIVATE_NUM_STATES,
};
static void elan_activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case ACTIVATE_GET_SENSOR_DIM:
elan_run_cmds(ssm, get_sensor_dim_cmds, get_sensor_dim_cmds_len,
ELAN_CMD_TIMEOUT);
break;
case ACTIVATE_SET_SENSOR_DIM:
elandev->frame_width = elandev->last_read[2];
elandev->raw_frame_width = elandev->last_read[0];
elandev->frame_height =
elandev->raw_frame_width - 2 * ELAN_FRAME_MARGIN;
fpi_ssm_next_state(ssm);
break;
case ACTIVATE_START:
elan_run_cmds(ssm, init_start_cmds, init_start_cmds_len,
ELAN_CMD_TIMEOUT);
break;
case ACTIVATE_READ_DATA:
elan_run_cmds(ssm, read_cmds, read_cmds_len, ELAN_CMD_TIMEOUT);
break;
case ACTIVATE_END:
elan_run_cmds(ssm, init_end_cmds, init_end_cmds_len,
ELAN_CMD_TIMEOUT);
}
}
static void activate_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
if (elandev->deactivating)
elan_deactivate(dev);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
elan_calibrate(dev);
fpi_ssm_free(ssm);
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elan_dev_reset(elandev);
struct fpi_ssm *ssm =
fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_activate_run_state, ACTIVATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, activate_complete);
return 0;
}
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
struct elan_dev *elandev;
int r;
G_DEBUG_HERE();
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
elandev = g_malloc0(sizeof(struct elan_dev));
fpi_imgdev_set_user_data(dev, elandev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elan_dev_reset(elandev);
g_free(elandev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
elandev->deactivating = TRUE;
if (elandev->cur_transfer)
libusb_cancel_transfer(elandev->cur_transfer);
else
elan_deactivate(dev);
}
static const struct usb_id id_table[] = {
{.vendor = 0x04f3,.product = 0x0907},
{0, 0, 0,},
};
struct fp_img_driver elan_driver = {
.driver = {
.id = ELAN_ID,
.name = FP_COMPONENT,
.full_name = "ElanTech Fingerprint Sensor",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.bz3_threshold = 22,
.open = dev_init,
.close = dev_deinit,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

176
libfprint/drivers/elan.h Normal file
View File

@@ -0,0 +1,176 @@
/*
* Elan driver for libfprint
*
* Copyright (C) 2017 Igor Filatov <ia.filatov@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 __ELAN_H
#define __ELAN_H
#include <string.h>
#include <libusb.h>
/* number of pixels to discard on left and right (along raw image height)
* because they have different intensity from the rest of the frame */
#define ELAN_FRAME_MARGIN 12
/* min and max frames in a capture */
#define ELAN_MIN_FRAMES 7
#define ELAN_MAX_FRAMES 30
/* number of frames to drop at the end of capture because frames captured
* while the finger is being lifted can be bad */
#define ELAN_SKIP_LAST_FRAMES 1
#define ELAN_CMD_LEN 0x2
#define ELAN_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT)
#define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN)
#define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN)
/* usual command timeout and timeout for when we need to check if the finger is
* still on the device */
#define ELAN_CMD_TIMEOUT 10000
#define ELAN_FINGER_TIMEOUT 200
struct elan_cmd {
unsigned char cmd[ELAN_CMD_LEN];
int response_len;
int response_in;
};
static const struct elan_cmd get_sensor_dim_cmds[] = {
{
.cmd = {0x00, 0x0c},
.response_len = 0x4,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t get_sensor_dim_cmds_len =
G_N_ELEMENTS(get_sensor_dim_cmds);
static const struct elan_cmd init_start_cmds[] = {
{
.cmd = {0x40, 0x19},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
{
.cmd = {0x40, 0x2a},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t init_start_cmds_len = G_N_ELEMENTS(init_start_cmds);
static const struct elan_cmd read_cmds[] = {
/* raw frame sizes are calculated from image dimesions reported by the
* device */
{
.cmd = {0x00, 0x09},
.response_len = -1,
.response_in = ELAN_EP_IMG_IN,
},
};
const size_t read_cmds_len = G_N_ELEMENTS(read_cmds);
/* issued after data reads during init and calibration */
static const struct elan_cmd init_end_cmds[] = {
{
.cmd = {0x40, 0x24},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t init_end_cmds_len = G_N_ELEMENTS(init_end_cmds);
/* same command 2 times
* original driver may observe return value to determine how many times it
* should be repeated */
static const struct elan_cmd calibrate_start_cmds[] = {
{
.cmd = {0x40, 0x23},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
{
.cmd = {0x40, 0x23},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t calibrate_start_cmds_len =
G_N_ELEMENTS(calibrate_start_cmds);
/* issued after data reads during init and calibration */
static const struct elan_cmd calibrate_end_cmds[] = {
{
.cmd = {0x40, 0x24},
.response_len = 0x2,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t calibrate_end_cmds_len =
G_N_ELEMENTS(calibrate_end_cmds);
static const struct elan_cmd capture_start_cmds[] = {
/* led on */
{
.cmd = {0x40, 0x31},
.response_len = 0x0,
.response_in = ELAN_EP_CMD_IN,
},
};
static size_t capture_start_cmds_len = G_N_ELEMENTS(capture_start_cmds);
static const struct elan_cmd capture_wait_finger_cmds[] = {
/* wait for finger
* subsequent read will not complete until finger is placed on the reader */
{
.cmd = {0x40, 0x3f},
.response_len = 0x1,
.response_in = ELAN_EP_CMD_IN,
},
};
static size_t capture_wait_finger_cmds_len =
G_N_ELEMENTS(capture_wait_finger_cmds);
static const struct elan_cmd deactivate_cmds[] = {
/* led off */
{
.cmd = {0x00, 0x0b},
.response_len = 0x0,
.response_in = ELAN_EP_CMD_IN,
},
};
static const size_t deactivate_cmds_len = G_N_ELEMENTS(deactivate_cmds);
static void elan_cmd_cb(struct libusb_transfer *transfer);
static void elan_cmd_read(struct fpi_ssm *ssm);
static void elan_run_next_cmd(struct fpi_ssm *ssm);
static void elan_capture(struct fp_img_dev *dev);
#endif

1505
libfprint/drivers/etes603.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -17,16 +17,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <libusb.h>
#define FP_COMPONENT "fdu2000"
#include <fp_internal.h>
#include "driver_ids.h"
#include "drivers_api.h"
#ifndef HAVE_MEMMEM
gpointer
@@ -176,25 +169,25 @@ capture(struct fp_img_dev *dev, gboolean unconditional,
image = g_malloc0(RAW_IMAGE_SIZE);
if ((r = bulk_write_safe(dev->udev, LED_ON))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), LED_ON))) {
fp_err("Command: LED_ON");
goto out;
}
if ((r = bulk_write_safe(dev->udev, CAPTURE_READY))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), CAPTURE_READY))) {
fp_err("Command: CAPTURE_READY");
goto out;
}
read:
if ((r = bulk_write_safe(dev->udev, CAPTURE_READ))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), CAPTURE_READ))) {
fp_err("Command: CAPTURE_READ");
goto out;
}
/* Now we are ready to read from dev */
r = libusb_bulk_transfer(dev->udev, &msg, &bytes, BULK_TIMEOUT * 10);
r = libusb_bulk_transfer(fpi_imgdev_get_usb_dev(dev), &msg, &bytes, BULK_TIMEOUT * 10);
if (r < 0 || bytes < 1)
goto read;
@@ -235,12 +228,12 @@ read:
}
}
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), LED_OFF))) {
fp_err("Command: LED_OFF");
goto out;
}
@@ -261,25 +254,27 @@ static
gint dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
gint r;
//if ( (r = usb_set_configuration(dev->udev, 1)) < 0 )
//if ( (r = usb_set_configuration(fpi_imgdev_get_usb_dev(dev), 1)) < 0 )
// goto out;
if ( (r = libusb_claim_interface(dev->udev, 0)) < 0 )
goto out;
if ( (r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0)) < 0 ) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
//if ( (r = usb_set_altinterface(dev->udev, 1)) < 0 )
//if ( (r = usb_set_altinterface(fpi_imgdev_get_usb_dev(dev), 1)) < 0 )
// goto out;
//if ( (r = usb_clear_halt(dev->udev, EP_CMD)) < 0 )
//if ( (r = usb_clear_halt(fpi_imgdev_get_usb_dev(dev), EP_CMD)) < 0 )
// goto out;
/* Make sure sensor mode is not capture_{ready|read} */
if ((r = bulk_write_safe(dev->udev, CAPTURE_END))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), CAPTURE_END))) {
fp_err("Command: CAPTURE_END");
goto out;
}
if ((r = bulk_write_safe(dev->udev, LED_OFF))) {
if ((r = bulk_write_safe(fpi_imgdev_get_usb_dev(dev), LED_OFF))) {
fp_err("Command: LED_OFF");
goto out;
}
@@ -294,10 +289,10 @@ out:
static
void dev_exit(struct fp_img_dev *dev)
{
if (bulk_write_safe(dev->udev, CAPTURE_END))
if (bulk_write_safe(fpi_imgdev_get_usb_dev(dev), CAPTURE_END))
fp_err("Command: CAPTURE_END");
libusb_release_interface(dev->udev, 0);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
}
static const struct usb_id id_table[] = {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,319 @@
/*
* 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>
*
* TCRD5B (USB ID 147e:1001) support:
* Copyright (C) 2014 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 IMG_WIDTH_2016 288
#define IMG_WIDTH_1000 288
#define IMG_WIDTH_1001 216
struct sonly_regwrite {
uint8_t reg;
uint8_t value;
};
/***** AWAIT FINGER *****/
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_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_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_2016_writev_4[] = {
{ 0x08, 0x00 }, { 0x10, 0x00 }, { 0x12, 0x01 }, { 0x11, 0xbf },
{ 0x12, 0x01 }, { 0x07, 0x10 }, { 0x07, 0x10 }, { 0x04, 0x00 },\
{ 0x05, 0x00 }, { 0x0b, 0x00 },
/* enter finger detection mode */
{ 0x15, 0x20 }, { 0x30, 0xe1 }, { 0x15, 0x24 }, { 0x15, 0x04 },
{ 0x15, 0x84 },
};
/***** CAPTURE MODE *****/
static const struct sonly_regwrite capsm_2016_writev[] = {
/* enter capture mode */
{ 0x09, 0x28 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, { 0x04, 0x00 },
{ 0x05, 0x00 },
};
static const struct sonly_regwrite capsm_1000_writev[] = {
{ 0x08, 0x80 }, { 0x13, 0x55 }, { 0x0b, 0x80 }, /* Enter capture mode */
};
static const struct sonly_regwrite capsm_1001_writev_1[] = {
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x05 },
};
static const struct sonly_regwrite capsm_1001_writev_2[] = {
{ 0x4d, 0xc0 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x0b, 0x00 },
{ 0x04, 0x00 },
{ 0x05, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x09 },
};
static const struct sonly_regwrite capsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4e, 0x08 },
};
static const struct sonly_regwrite capsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x00, 0x5f }, { 0x01, 0xee },
{ 0x03, 0x2c },
{ 0x07, 0x00 }, { 0x08, 0x00 }, { 0x09, 0x29 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x10, 0x00 }, { 0x11, 0x8f }, { 0x12, 0x01 }, { 0x13, 0x45 },
{ 0x15, 0x26 },
{ 0x1e, 0x02 },
{ 0x20, 0x01 },
{ 0x25, 0x8f },
{ 0x27, 0x23 },
{ 0x30, 0xe0 },
{ 0x07, 0x10 },
{ 0x09, 0x21 },
{ 0x13, 0x75 },
{ 0x0b, 0x80 },
};
/***** DEINITIALIZATION *****/
static const struct sonly_regwrite deinitsm_2016_writev[] = {
/* reset + enter low power mode */
{ 0x0b, 0x00 }, { 0x09, 0x20 }, { 0x13, 0x45 }, { 0x13, 0x45 },
};
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 const struct sonly_regwrite deinitsm_1001_writev[] = {
{ 0x0b, 0x00 },
{ 0x13, 0x45 },
{ 0x09, 0x29 },
{ 0x1a, 0x00 },
};
/***** INITIALIZATION *****/
static const struct sonly_regwrite initsm_2016_writev_1[] = {
{ 0x49, 0x00 },
/* BSAPI writes different values to register 0x3e each time. I initially
* thought this was some kind of clever authentication, but just blasting
* these sniffed values each time seems to work. */
{ 0x3e, 0x83 }, { 0x3e, 0x4f }, { 0x3e, 0x0f }, { 0x3e, 0xbf },
{ 0x3e, 0x45 }, { 0x3e, 0x35 }, { 0x3e, 0x1c }, { 0x3e, 0xae },
{ 0x44, 0x01 }, { 0x43, 0x06 }, { 0x43, 0x05 }, { 0x43, 0x04 },
{ 0x44, 0x00 }, { 0x0b, 0x00 },
};
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 const struct sonly_regwrite initsm_1001_writev_1[] = {
{ 0x4a, 0x9d },
{ 0x4f, 0x06 },
{ 0x4f, 0x05 },
{ 0x4f, 0x04 },
{ 0x4a, 0x9c },
{ 0x3e, 0xa6 },
{ 0x3e, 0x01 },
{ 0x3e, 0x68 },
{ 0x3e, 0xfd },
{ 0x3e, 0x72 },
{ 0x3e, 0xef },
{ 0x3e, 0x5d },
{ 0x3e, 0xc5 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0x1f }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_2[] = {
{ 0x4c, 0x03 }, { 0x4d, 0xb8 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_3[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4c, 0xff }, { 0x4d, 0xc0 }, { 0x4e, 0x00 },
};
static const struct sonly_regwrite initsm_1001_writev_4[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
{ 0x09, 0x27 },
{ 0x1a, 0x02 },
{ 0x49, 0x01 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x02 },
{ 0x47, 0x0a },
{ 0x47, 0x00 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x47, 0x04 },
{ 0x49, 0x00 },
{ 0x3e, 0x90 },
{ 0x3e, 0xbd },
{ 0x3e, 0xbf },
{ 0x3e, 0x48 },
{ 0x3e, 0x2a },
{ 0x3e, 0xe3 },
{ 0x3e, 0xd2 },
{ 0x3e, 0x58 },
{ 0x09, 0x2f },
{ 0x1a, 0x00 },
{ 0x1a, 0x02 },
{ 0x4a, 0x9d },
{ 0x4d, 0x40 }, { 0x4e, 0x03 },
};
static const struct sonly_regwrite initsm_1001_writev_5[] = {
{ 0x4a, 0x9c },
{ 0x1a, 0x00 },
};

View File

@@ -20,13 +20,8 @@
#define FP_COMPONENT "upektc"
#include <errno.h>
#include <string.h>
#include <libusb.h>
#include <fp_internal.h>
#include "drivers_api.h"
#include "upektc.h"
#include "driver_ids.h"
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
@@ -63,8 +58,8 @@ enum activate_states {
static void upektc_next_init_cmd(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
upekdev->init_idx += 1;
if (upekdev->init_idx == upekdev->setup_commands_len)
@@ -76,8 +71,8 @@ static void upektc_next_init_cmd(struct fpi_ssm *ssm)
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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
(transfer->length == transfer->actual_length)) {
@@ -105,11 +100,11 @@ static void read_init_data_cb(struct libusb_transfer *transfer)
static void activate_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_INIT:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
@@ -117,7 +112,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), 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);
@@ -138,7 +133,7 @@ static void activate_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), upekdev->ep_in, data,
upekdev->setup_commands[upekdev->init_idx].response_len,
read_init_data_cb, ssm, BULK_TIMEOUT);
@@ -155,11 +150,11 @@ static void activate_run_state(struct fpi_ssm *ssm)
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);
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fp_dbg("status %d", fpi_ssm_get_error(ssm));
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
start_finger_detection(dev);
fpi_ssm_free(ssm);
}
@@ -186,7 +181,7 @@ static int finger_present(unsigned char *img, size_t len, int sum_threshold)
static void finger_det_data_cb(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct upektc_dev *upekdev = dev->priv;
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
@@ -219,7 +214,7 @@ static void finger_det_cmd_cb(struct libusb_transfer *t)
unsigned char *data;
int r;
struct fp_img_dev *dev = t->user_data;
struct upektc_dev *upekdev = dev->priv;
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
fp_dbg("req transfer status %d\n", t->status);
@@ -238,7 +233,7 @@ static void finger_det_cmd_cb(struct libusb_transfer *t)
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), upekdev->ep_in, data, IMAGE_SIZE,
finger_det_data_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -254,9 +249,9 @@ exit_free_transfer:
static void start_finger_detection(struct fp_img_dev *dev)
{
int r;
struct upektc_dev *upekdev = dev->priv;
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct libusb_transfer *transfer;
fp_dbg("");
G_DEBUG_HERE();
if (upekdev->deactivating) {
complete_deactivation(dev);
@@ -268,7 +263,7 @@ static void start_finger_detection(struct fp_img_dev *dev)
fpi_imgdev_session_error(dev, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
finger_det_cmd_cb, dev, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -302,7 +297,7 @@ static void capture_cmd_cb(struct libusb_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 fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
unsigned char *data = transfer->buffer;
struct fp_img *img;
@@ -328,11 +323,11 @@ out:
static void capture_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_WRITE_CMD:
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
@@ -340,7 +335,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_out,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), upekdev->ep_out,
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
capture_cmd_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -361,7 +356,7 @@ static void capture_run_state(struct fpi_ssm *ssm)
}
data = g_malloc(IMAGE_SIZE);
libusb_fill_bulk_transfer(transfer, dev->udev, upekdev->ep_in, data, IMAGE_SIZE,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), upekdev->ep_in, data, IMAGE_SIZE,
capture_read_data_cb, ssm, BULK_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -377,14 +372,14 @@ static void capture_run_state(struct fpi_ssm *ssm)
static void capture_sm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct upektc_dev *upekdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
fp_dbg("Capture completed");
if (upekdev->deactivating)
complete_deactivation(dev);
else if (ssm->error)
fpi_imgdev_session_error(dev, ssm->error);
else if (fpi_ssm_get_error(ssm))
fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm));
else
start_finger_detection(dev);
fpi_ssm_free(ssm);
@@ -392,7 +387,7 @@ static void capture_sm_complete(struct fpi_ssm *ssm)
static void start_capture(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
if (upekdev->deactivating) {
@@ -400,18 +395,18 @@ static void start_capture(struct fp_img_dev *dev)
return;
}
ssm = fpi_ssm_new(dev->dev, capture_run_state, CAPTURE_NUM_STATES);
fp_dbg("");
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
G_DEBUG_HERE();
fpi_ssm_set_user_data(ssm, 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,
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, dev);
upekdev->init_idx = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
@@ -419,15 +414,15 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
upekdev->deactivating = TRUE;
}
static void complete_deactivation(struct fp_img_dev *dev)
{
struct upektc_dev *upekdev = dev->priv;
fp_dbg("");
struct upektc_dev *upekdev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
upekdev->deactivating = FALSE;
fpi_imgdev_deactivate_complete(dev);
@@ -439,32 +434,33 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
int r;
struct upektc_dev *upekdev;
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
dev->priv = upekdev = g_malloc0(sizeof(struct upektc_dev));
upekdev = g_malloc0(sizeof(struct upektc_dev));
fpi_imgdev_set_user_data(dev, upekdev);
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->setup_commands_len = G_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->setup_commands_len = G_N_ELEMENTS(upeket_setup_commands);
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
break;
default:
fp_err("Device variant %d is not known\n", driver_data);
fp_err("Device variant %lu is not known\n", driver_data);
g_free(upekdev);
dev->priv = NULL;
fpi_imgdev_set_user_data(dev, NULL);
return -ENODEV;
break;
}
@@ -474,8 +470,10 @@ 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);
libusb_release_interface(dev->udev, 0);
void *user_data;
user_data = fpi_imgdev_get_user_data(dev);
g_free(user_data);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}

View File

@@ -0,0 +1,701 @@
/*
* 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 "upektc_img"
#include "drivers_api.h"
#include "aeslib.h"
#include "upektc_img.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 upektc_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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
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, fpi_imgdev_get_usb_dev(dev), 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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
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, fpi_imgdev_get_usb_dev(dev), 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_READ_DATA_TERM,
CAPTURE_ACK_00_28,
CAPTURE_ACK_08,
CAPTURE_ACK_FRAME,
CAPTURE_ACK_00_28_TERM,
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_mark_aborted(ssm, -EIO);
return;
}
switch (fpi_ssm_get_cur_state(ssm)) {
case CAPTURE_ACK_00_28_TERM:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA_TERM);
break;
default:
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
break;
}
}
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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
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, fpi_ssm_get_cur_state(ssm));
return;
}
if (fpi_ssm_get_cur_state(ssm) == CAPTURE_READ_DATA_TERM) {
fp_dbg("Terminating SSM\n");
fpi_ssm_mark_completed(ssm);
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 %lu, 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;
case 0x1e:
/* short scan */
fp_err("short scan, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
case 0x1d:
/* too much horisontal movement */
fp_err("too much horisontal movement, aborting\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_CENTER_FINGER);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
break;
default:
/* some error happened, cancel scan */
fp_err("something bad happened, stop scan\n");
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY);
fpi_imgdev_report_finger_status(dev, FALSE);
fpi_ssm_jump_to_state(ssm, CAPTURE_ACK_00_28_TERM);
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 %lu\n", upekdev->image_size);
img = fpi_img_new(IMAGE_SIZE);
img->flags = FP_IMG_PARTIAL;
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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
switch (fpi_ssm_get_cur_state(ssm)) {
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:
case CAPTURE_READ_DATA_TERM:
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:
case CAPTURE_ACK_00_28_TERM:
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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
int err = fpi_ssm_get_error(ssm);
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 upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES);
fpi_ssm_set_user_data(ssm, 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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
switch (fpi_ssm_get_cur_state(ssm)) {
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 = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
int err = fpi_ssm_get_error(ssm);
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 upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
upekdev->image_size = 0;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), deactivate_run_state, DEACTIVATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, 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 *idev = fpi_ssm_get_user_data(ssm);
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(idev);
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
int r;
switch (fpi_ssm_get_cur_state(ssm)) {
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, fpi_dev_get_usb_dev(dev), 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 = fpi_ssm_get_user_data(ssm);
int err = fpi_ssm_get_error(ssm);
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 upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state,
ACTIVATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
upekdev->seq = 0;
fpi_ssm_start(ssm, activate_sm_complete);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
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;
struct upektc_img_dev *upekdev;
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc0(sizeof(struct upektc_img_dev));
fpi_imgdev_set_user_data(dev, upekdev);
fpi_imgdev_open_complete(dev, 0);
return 0;
}
static void dev_deinit(struct fp_img_dev *dev)
{
struct upektc_img_dev *upekdev = fpi_imgdev_get_user_data(dev);
g_free(upekdev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 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;
if (dsc->idProduct == 0x2016 && dsc->bcdDevice == 2)
return 1;
return 0;
}
static const struct usb_id id_table[] = {
{ .vendor = 0x147e, .product = 0x2016 },
{ .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 = 20,
.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

@@ -27,15 +27,7 @@
#define FP_COMPONENT "upekts"
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#include "driver_ids.h"
#include "drivers_api.h"
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
@@ -141,6 +133,7 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
{
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
uint16_t crc;
const char *ciao = "Ciao";
/* 9 bytes extra for: 4 byte 'Ciao', 1 byte A, 1 byte B | lenHI,
* 1 byte lenLO, 2 byte CRC */
@@ -158,7 +151,7 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
buf = g_malloc(urblen);
/* Write header */
strncpy(buf, "Ciao", 4);
memcpy(buf, ciao, strlen(ciao));
len = GUINT16_TO_LE(len);
buf[4] = seq_a;
buf[5] = seq_b | ((len & 0xf00) >> 8);
@@ -173,7 +166,7 @@ static struct libusb_transfer *alloc_send_cmd_transfer(struct fp_dev *dev,
buf[urblen - 2] = crc >> 8;
buf[urblen - 1] = crc & 0xff;
libusb_fill_bulk_transfer(transfer, dev->udev, EP_OUT, buf, urblen,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(dev), EP_OUT, buf, urblen,
callback, user_data, TIMEOUT);
return transfer;
}
@@ -185,7 +178,7 @@ static struct libusb_transfer *alloc_send_cmd28_transfer(struct fp_dev *dev,
uint16_t _innerlen = innerlen;
size_t len = innerlen + 6;
unsigned char *buf = g_malloc0(len);
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
uint8_t seq = upekdev->seq + CMD_SEQ_INCREMENT;
struct libusb_transfer *ret;
@@ -424,7 +417,7 @@ static void read_msg_cb(struct libusb_transfer *transfer)
fp_dbg("didn't fit in buffer, need to extend by %d bytes", needed);
data = g_realloc((gpointer) data, MSG_READ_BUF_SIZE + needed);
libusb_fill_bulk_transfer(etransfer, udata->dev->udev, EP_IN,
libusb_fill_bulk_transfer(etransfer, fpi_dev_get_usb_dev(udata->dev), EP_IN,
data + MSG_READ_BUF_SIZE, needed, read_msg_extend_cb, udata,
TIMEOUT);
@@ -463,7 +456,7 @@ static int __read_msg_async(struct read_msg_data *udata)
return -ENOMEM;
}
libusb_fill_bulk_transfer(transfer, udata->dev->udev, EP_IN, buf,
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(udata->dev), EP_IN, buf,
MSG_READ_BUF_SIZE, read_msg_cb, udata, TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -536,23 +529,23 @@ static void initsm_read_msg_response_cb(struct fpi_ssm *ssm,
enum read_msg_status status, uint8_t seq,
unsigned char expect_subcmd, unsigned char subcmd)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (status != READ_MSG_RESPONSE) {
fp_err("expected response, got %d seq=%x in state %d", status, seq,
ssm->cur_state);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, -1);
} else if (subcmd != expect_subcmd) {
fp_warn("expected response to subcmd 0x%02x, got response to %02x in "
"state %d", expect_subcmd, subcmd, ssm->cur_state);
"state %d", expect_subcmd, subcmd, fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, -1);
} else if (seq != upekdev->seq) {
fp_err("expected response to cmd seq=%02x, got response to %02x "
"in state %d", upekdev->seq, seq, ssm->cur_state);
"in state %d", upekdev->seq, seq, fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, -1);
} else {
fp_dbg("state %d completed", ssm->cur_state);
fp_dbg("state %d completed", fpi_ssm_get_cur_state(ssm));
fpi_ssm_next_state(ssm);
}
}
@@ -600,22 +593,22 @@ static void read28_06_cb(struct fp_dev *dev, enum read_msg_status status,
static void initsm_read_msg_cmd_cb(struct fpi_ssm *ssm,
enum read_msg_status status, uint8_t expect_seq, uint8_t seq)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (status == READ_MSG_ERROR) {
fpi_ssm_mark_aborted(ssm, -1);
return;
} else if (status != READ_MSG_CMD) {
fp_err("expected command, got %d seq=%x in state %d", status, seq,
ssm->cur_state);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, -1);
return;
}
upekdev->seq = seq;
if (seq != expect_seq) {
fp_err("expected seq=%x, got %x in state %d", expect_seq, seq,
ssm->cur_state);
fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, -1);
return;
}
@@ -652,9 +645,9 @@ static void ctrl400_cb(struct libusb_transfer *transfer)
static void initsm_read_msg_handler(struct fpi_ssm *ssm,
read_msg_cb_fn callback)
{
int r = read_msg_async(ssm->dev, callback, ssm);
int r = read_msg_async(fpi_ssm_get_dev(ssm), callback, ssm);
if (r < 0) {
fp_err("async read msg failed in state %d", ssm->cur_state);
fp_err("async read msg failed in state %d", fpi_ssm_get_cur_state(ssm));
fpi_ssm_mark_aborted(ssm, r);
}
}
@@ -664,10 +657,10 @@ static void initsm_send_msg_cb(struct libusb_transfer *transfer)
struct fpi_ssm *ssm = transfer->user_data;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED
&& transfer->length == transfer->actual_length) {
fp_dbg("state %d completed", ssm->cur_state);
fp_dbg("state %d completed", fpi_ssm_get_cur_state(ssm));
fpi_ssm_next_state(ssm);
} else {
fp_err("failed, state=%d rqlength=%d actual_length=%d", ssm->cur_state,
fp_err("failed, state=%d rqlength=%d actual_length=%d", fpi_ssm_get_cur_state(ssm),
transfer->length, transfer->actual_length);
fpi_ssm_mark_aborted(ssm, -1);
}
@@ -677,7 +670,7 @@ static void initsm_send_msg_cb(struct libusb_transfer *transfer)
static void initsm_send_msg28_handler(struct fpi_ssm *ssm,
unsigned char subcmd, const unsigned char *data, uint16_t innerlen)
{
struct fp_dev *dev = ssm->dev;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
struct libusb_transfer *transfer;
int r;
@@ -690,7 +683,7 @@ static void initsm_send_msg28_handler(struct fpi_ssm *ssm,
r = libusb_submit_transfer(transfer);
if (r < 0) {
fp_err("urb submission failed error %d in state %d", r, ssm->cur_state);
fp_err("urb submission failed error %d in state %d", r, fpi_ssm_get_cur_state(ssm));
g_free(transfer->buffer);
libusb_free_transfer(transfer);
fpi_ssm_mark_aborted(ssm, -EIO);
@@ -699,12 +692,12 @@ static void initsm_send_msg28_handler(struct fpi_ssm *ssm,
static void initsm_run_state(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
struct libusb_transfer *transfer;
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case WRITE_CTRL400: ;
unsigned char *data;
@@ -717,7 +710,7 @@ static void initsm_run_state(struct fpi_ssm *ssm)
data = g_malloc(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,
libusb_fill_control_transfer(transfer, fpi_dev_get_usb_dev(dev), data,
ctrl400_cb, ssm, TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -812,7 +805,7 @@ static void read_msg01_cb(struct fp_dev *dev, enum read_msg_status status,
void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (status == READ_MSG_ERROR) {
fpi_ssm_mark_aborted(ssm, -1);
@@ -834,10 +827,10 @@ static void read_msg01_cb(struct fp_dev *dev, enum read_msg_status status,
static void deinitsm_state_handler(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case SEND_RESP07: ;
struct libusb_transfer *transfer;
unsigned char dummy = 0;
@@ -874,14 +867,16 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
struct upekts_dev *upekdev = NULL;
int r;
r = libusb_claim_interface(dev->udev, 0);
if (r < 0)
r = libusb_claim_interface(fpi_dev_get_usb_dev(dev), 0);
if (r < 0) {
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
upekdev = g_malloc(sizeof(*upekdev));
upekdev->seq = 0xf0; /* incremented to 0x00 before first cmd */
dev->priv = upekdev;
dev->nr_enroll_stages = 3;
fpi_dev_set_user_data(dev, upekdev);
fpi_dev_set_nr_enroll_stages(dev, 3);
fpi_drvcb_open_complete(dev, 0);
return 0;
@@ -889,8 +884,10 @@ static int dev_init(struct fp_dev *dev, unsigned long driver_data)
static void dev_exit(struct fp_dev *dev)
{
libusb_release_interface(dev->udev, 0);
g_free(dev->priv);
void *user_data;
libusb_release_interface(fpi_dev_get_usb_dev(dev), 0);
user_data = fpi_dev_get_user_data(dev);
g_free(user_data);
fpi_drvcb_close_complete(dev);
}
@@ -914,8 +911,8 @@ enum enroll_start_sm_states {
/* Called when the device initialization state machine completes */
static void enroll_start_sm_cb_initsm(struct fpi_ssm *initsm)
{
struct fpi_ssm *enroll_start_ssm = initsm->priv;
int error = initsm->error;
struct fpi_ssm *enroll_start_ssm = fpi_ssm_get_user_data(initsm);
int error = fpi_ssm_get_error(initsm);
fpi_ssm_free(initsm);
if (error)
@@ -941,7 +938,7 @@ static void enroll_start_sm_cb_msg28(struct fp_dev *dev,
enum read_msg_status status, uint8_t seq, unsigned char subcmd,
unsigned char *data, size_t data_len, void *user_data)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
struct fpi_ssm *ssm = user_data;
if (status != READ_MSG_RESPONSE) {
@@ -962,13 +959,13 @@ static void enroll_start_sm_cb_msg28(struct fp_dev *dev,
static void enroll_start_sm_run_state(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case RUN_INITSM: ;
struct fpi_ssm *initsm = initsm_new(dev);
initsm->priv = ssm;
fpi_ssm_set_user_data(initsm, ssm);
fpi_ssm_start(initsm, enroll_start_sm_cb_initsm);
break;
case ENROLL_INIT: ;
@@ -1004,12 +1001,12 @@ static void enroll_iterate(struct fp_dev *dev);
static void e_handle_resp00(struct fp_dev *dev, unsigned char *data,
size_t data_len)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
unsigned char status;
int result = 0;
if (data_len != 14) {
fp_err("received 3001 poll response of %d bytes?", data_len);
fp_err("received 3001 poll response of %lu bytes?", data_len);
fpi_drvcb_enroll_stage_completed(dev, -EPROTO, NULL, NULL);
return;
}
@@ -1077,17 +1074,20 @@ 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)) {
fp_err("fingerprint data too short (%d bytes)", data_len);
fp_err("fingerprint data too short (%lu bytes)", data_len);
} else if (memcmp(data, scan_comp, sizeof(scan_comp)) != 0) {
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;
}
@@ -1152,10 +1152,10 @@ static void enroll_iterate(struct fp_dev *dev)
static void enroll_started(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
fpi_drvcb_enroll_started(dev, ssm->error);
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
fpi_drvcb_enroll_started(dev, fpi_ssm_get_error(ssm));
if (!ssm->error)
if (!fpi_ssm_get_error(ssm))
enroll_iterate(dev);
fpi_ssm_free(ssm);
@@ -1163,7 +1163,7 @@ static void enroll_started(struct fpi_ssm *ssm)
static int enroll_start(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
/* do_init state machine first */
struct fpi_ssm *ssm = fpi_ssm_new(dev, enroll_start_sm_run_state,
@@ -1177,7 +1177,7 @@ static int enroll_start(struct fp_dev *dev)
static void enroll_stop_deinit_cb(struct fpi_ssm *ssm)
{
/* don't really care about errors */
fpi_drvcb_enroll_stopped(ssm->dev);
fpi_drvcb_enroll_stopped(fpi_ssm_get_dev(ssm));
fpi_ssm_free(ssm);
}
@@ -1191,7 +1191,7 @@ static int enroll_stop(struct fp_dev *dev)
static void verify_stop_deinit_cb(struct fpi_ssm *ssm)
{
/* don't really care about errors */
fpi_drvcb_verify_stopped(ssm->dev);
fpi_drvcb_verify_stopped(fpi_ssm_get_dev(ssm));
fpi_ssm_free(ssm);
}
@@ -1216,9 +1216,12 @@ enum {
/* Called when the device initialization state machine completes */
static void verify_start_sm_cb_initsm(struct fpi_ssm *initsm)
{
struct fpi_ssm *verify_start_ssm = initsm->priv;
if (initsm->error)
fpi_ssm_mark_aborted(verify_start_ssm, initsm->error);
struct fpi_ssm *verify_start_ssm = fpi_ssm_get_user_data(initsm);
int err;
err = fpi_ssm_get_error(initsm);
if (err)
fpi_ssm_mark_aborted(verify_start_ssm, err);
else
fpi_ssm_next_state(verify_start_ssm);
fpi_ssm_free(initsm);
@@ -1238,23 +1241,24 @@ static void verify_init_2803_cb(struct libusb_transfer *transfer)
static void verify_start_sm_run_state(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
int r;
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case VERIFY_RUN_INITSM: ;
struct fpi_ssm *initsm = initsm_new(dev);
initsm->priv = ssm;
fpi_ssm_set_user_data(initsm, ssm);
fpi_ssm_start(initsm, verify_start_sm_cb_initsm);
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 *print = fpi_dev_get_verify_data(dev);
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);
@@ -1282,7 +1286,7 @@ static void v_handle_resp00(struct fp_dev *dev, unsigned char *data,
int r = 0;
if (data_len != 14) {
fp_err("received 3001 poll response of %d bytes?", data_len);
fp_err("received 3001 poll response of %lu bytes?", data_len);
r = -EPROTO;
goto out;
}
@@ -1353,7 +1357,7 @@ static void verify_rd2800_cb(struct fp_dev *dev, enum read_msg_status msgstat,
uint8_t seq, unsigned char subcmd, unsigned char *data, size_t data_len,
void *user_data)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (msgstat != READ_MSG_RESPONSE) {
fp_err("expected response, got %d seq=%x", msgstat, seq);
@@ -1392,7 +1396,7 @@ static void verify_wr2800_cb(struct libusb_transfer *transfer)
static void verify_iterate(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (upekdev->stop_verify) {
do_verify_stop(dev);
@@ -1427,11 +1431,11 @@ static void verify_iterate(struct fp_dev *dev)
static void verify_started(struct fpi_ssm *ssm)
{
struct fp_dev *dev = ssm->dev;
struct upekts_dev *upekdev = dev->priv;
struct fp_dev *dev = fpi_ssm_get_dev(ssm);
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
fpi_drvcb_verify_started(dev, ssm->error);
if (!ssm->error) {
fpi_drvcb_verify_started(dev, fpi_ssm_get_error(ssm));
if (!fpi_ssm_get_error(ssm)) {
upekdev->first_verify_iteration = TRUE;
verify_iterate(dev);
}
@@ -1441,7 +1445,7 @@ static void verify_started(struct fpi_ssm *ssm)
static int verify_start(struct fp_dev *dev)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(dev, verify_start_sm_run_state,
VERIFY_NUM_STATES);
upekdev->stop_verify = FALSE;
@@ -1451,7 +1455,7 @@ static int verify_start(struct fp_dev *dev)
static int verify_stop(struct fp_dev *dev, gboolean iterating)
{
struct upekts_dev *upekdev = dev->priv;
struct upekts_dev *upekdev = fpi_dev_get_user_data(dev);
if (!iterating)
do_verify_stop(dev);

View File

@@ -20,17 +20,10 @@
#define FP_COMPONENT "uru4000"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <nss.h>
#include <pk11pub.h>
#include <libusb.h>
#include <fp_internal.h>
#include "driver_ids.h"
#include "drivers_api.h"
#define EP_INTR (1 | LIBUSB_ENDPOINT_IN)
#define EP_DATA (2 | LIBUSB_ENDPOINT_IN)
@@ -45,6 +38,8 @@
#define IMAGE_HEIGHT 290
#define IMAGE_WIDTH 384
#define ENC_THRESHOLD 5000
enum {
IRQDATA_SCANPWR_ON = 0x56aa,
IRQDATA_FINGER_ON = 0x0101,
@@ -206,7 +201,7 @@ static int write_regs(struct fp_img_dev *dev, uint16_t first_reg,
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + num_regs);
memcpy(data + LIBUSB_CONTROL_SETUP_SIZE, values, num_regs);
libusb_fill_control_setup(data, CTRL_OUT, USB_RQ, first_reg, 0, num_regs);
libusb_fill_control_transfer(transfer, dev->udev, data, write_regs_cb,
libusb_fill_control_transfer(transfer, fpi_imgdev_get_usb_dev(dev), data, write_regs_cb,
wrdata, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -272,7 +267,7 @@ static int read_regs(struct fp_img_dev *dev, uint16_t first_reg,
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE + num_regs);
libusb_fill_control_setup(data, CTRL_IN, USB_RQ, first_reg, 0, num_regs);
libusb_fill_control_transfer(transfer, dev->udev, data, read_regs_cb,
libusb_fill_control_transfer(transfer, fpi_imgdev_get_usb_dev(dev), data, read_regs_cb,
rrdata, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
@@ -322,7 +317,7 @@ static void challenge_cb(struct fp_img_dev *dev, int status,
uint16_t num_regs, unsigned char *data, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
unsigned char *respdata;
PK11Context *ctx;
int r, outlen;
@@ -361,10 +356,10 @@ static void challenge_cb(struct fp_img_dev *dev, int status,
*/
static void sm_do_challenge_response(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int r;
fp_dbg("");
G_DEBUG_HERE();
r = read_regs(dev, REG_CHALLENGE, CR_LENGTH, challenge_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
@@ -379,7 +374,7 @@ static int start_irq_handler(struct fp_img_dev *dev);
static void irq_handler(struct libusb_transfer *transfer)
{
struct fp_img_dev *dev = transfer->user_data;
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
unsigned char *data = transfer->buffer;
uint16_t type;
int r = 0;
@@ -431,7 +426,7 @@ out:
static int start_irq_handler(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
@@ -440,7 +435,7 @@ static int start_irq_handler(struct fp_img_dev *dev)
return -ENOMEM;
data = g_malloc(IRQ_LENGTH);
libusb_fill_bulk_transfer(transfer, dev->udev, EP_INTR, data, IRQ_LENGTH,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_INTR, data, IRQ_LENGTH,
irq_handler, dev, 0);
urudev->irq_transfer = transfer;
@@ -455,7 +450,7 @@ static int start_irq_handler(struct fp_img_dev *dev)
static void stop_irq_handler(struct fp_img_dev *dev, irqs_stopped_cb_fn cb)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
struct libusb_transfer *transfer = urudev->irq_transfer;
if (transfer) {
libusb_cancel_transfer(transfer);
@@ -489,7 +484,7 @@ static void change_state_write_reg_cb(struct fp_img_dev *dev, int status,
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
switch (state) {
case IMGDEV_STATE_INACTIVE:
@@ -524,7 +519,7 @@ static void sm_write_reg_cb(struct fp_img_dev *dev, int result, void *user_data)
static void sm_write_regs(struct fpi_ssm *ssm, uint16_t first_reg, uint16_t num_regs,
void *data)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int r = write_regs(dev, first_reg, num_regs, data, sm_write_reg_cb, ssm);
if (r < 0)
fpi_ssm_mark_aborted(ssm, r);
@@ -540,7 +535,7 @@ static void sm_read_reg_cb(struct fp_img_dev *dev, int result,
uint16_t num_regs, unsigned char *data, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
if (result) {
fpi_ssm_mark_aborted(ssm, result);
@@ -553,8 +548,8 @@ static void sm_read_reg_cb(struct fp_img_dev *dev, int result,
static void sm_read_regs(struct fpi_ssm *ssm, uint16_t reg, uint16_t num_regs)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
int r;
if (num_regs > sizeof(urudev->last_reg_rd)) {
@@ -664,22 +659,50 @@ static uint32_t do_decode(uint8_t *data, int num_bytes, uint32_t key)
return update_key(key);
}
static int calc_dev2(struct uru4k_image *img)
{
uint8_t *b[2] = { NULL, NULL };
int res = 0, mean = 0, i, r, j, idx;
for (i = r = idx = 0; i < G_N_ELEMENTS(img->block_info) && idx < 2; i++) {
if (img->block_info[i].flags & BLOCKF_NOT_PRESENT)
continue;
for (j = 0; j < img->block_info[i].num_lines && idx < 2; j++)
b[idx++] = img->data[r++];
}
if (!b[0] || !b[1]) {
fp_dbg("NULL! %p %p", b[0], b[1]);
return 0;
}
for (i = 0; i < IMAGE_WIDTH; i++)
mean += (int)b[0][i] + (int)b[1][i];
mean /= IMAGE_WIDTH;
for (i = 0; i < IMAGE_WIDTH; i++) {
int dev = (int)b[0][i] + (int)b[1][i] - mean;
res += dev * dev;
}
return res / IMAGE_WIDTH;
}
static void imaging_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
struct uru4k_image *img = urudev->img_data;
struct fp_img *fpimg;
uint32_t key;
uint8_t flags, num_lines;
int i, r, to;
int i, r, to, dev2;
char buf[5];
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case IMAGING_CAPTURE:
urudev->img_lines_done = 0;
urudev->img_block = 0;
libusb_fill_bulk_transfer(urudev->img_transfer, dev->udev, EP_DATA,
libusb_fill_bulk_transfer(urudev->img_transfer, fpi_imgdev_get_usb_dev(dev), EP_DATA,
urudev->img_data, sizeof(struct uru4k_image), image_transfer_cb, ssm, 0);
r = libusb_submit_transfer(urudev->img_transfer);
if (r < 0)
@@ -698,8 +721,13 @@ static void imaging_run_state(struct fpi_ssm *ssm)
return;
}
if (!urudev->profile->encryption) {
fpi_ssm_jump_to_state(ssm, IMAGING_REPORT_IMAGE);
return;
dev2 = calc_dev2(img);
fp_dbg("dev2: %d", dev2);
if (dev2 < ENC_THRESHOLD) {
fpi_ssm_jump_to_state(ssm, IMAGING_REPORT_IMAGE);
return;
}
fp_info("image seems to be encrypted");
}
buf[0] = img->key_number;
buf[1] = urudev->img_enc_seed;
@@ -719,7 +747,7 @@ static void imaging_run_state(struct fpi_ssm *ssm)
key ^= urudev->img_enc_seed;
fp_dbg("encryption id %02x -> key %08x", img->key_number, key);
while (urudev->img_block < array_n_elements(img->block_info) &&
while (urudev->img_block < G_N_ELEMENTS(img->block_info) &&
urudev->img_lines_done < img->num_lines) {
flags = img->block_info[urudev->img_block].flags;
num_lines = img->block_info[urudev->img_block].num_lines;
@@ -757,7 +785,7 @@ static void imaging_run_state(struct fpi_ssm *ssm)
fpimg = fpi_img_new_for_imgdev(dev);
to = r = 0;
for (i = 0; i < array_n_elements(img->block_info) && r < img->num_lines; i++) {
for (i = 0; i < G_N_ELEMENTS(img->block_info) && r < img->num_lines; i++) {
flags = img->block_info[i].flags;
num_lines = img->block_info[i].num_lines;
if (num_lines == 0)
@@ -784,9 +812,9 @@ static void imaging_run_state(struct fpi_ssm *ssm)
static void imaging_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
int r = ssm->error;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
/* Report error before exiting imaging loop - the error handler
@@ -835,8 +863,8 @@ enum rebootpwr_states {
static void rebootpwr_pause_cb(void *data)
{
struct fpi_ssm *ssm = data;
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
if (!--urudev->rebootpwr_ctr) {
fp_err("could not reboot device power");
@@ -848,10 +876,10 @@ static void rebootpwr_pause_cb(void *data)
static void rebootpwr_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case REBOOTPWR_SET_HWSTAT:
urudev->rebootpwr_ctr = 100;
sm_set_hwstat(ssm, urudev->last_hwstat & 0xf);
@@ -912,8 +940,8 @@ enum powerup_states {
static void powerup_pause_cb(void *data)
{
struct fpi_ssm *ssm = data;
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
if (!--urudev->powerup_ctr) {
fp_err("could not power device up");
@@ -927,10 +955,10 @@ static void powerup_pause_cb(void *data)
static void powerup_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case POWERUP_INIT:
urudev->powerup_ctr = 100;
urudev->powerup_hwstat = urudev->last_hwstat & 0xf;
@@ -997,13 +1025,13 @@ static void init_scanpwr_irq_cb(struct fp_img_dev *dev, int status,
uint16_t type, void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
if (status)
fpi_ssm_mark_aborted(ssm, status);
else if (type != IRQDATA_SCANPWR_ON)
fp_dbg("ignoring interrupt");
else if (ssm->cur_state != INIT_AWAIT_SCAN_POWER) {
else if (fpi_ssm_get_cur_state(ssm) != INIT_AWAIT_SCAN_POWER) {
fp_dbg("early scanpwr interrupt");
urudev->scanpwr_irq_timeouts = -1;
} else {
@@ -1015,8 +1043,8 @@ static void init_scanpwr_irq_cb(struct fp_img_dev *dev, int status,
static void init_scanpwr_timeout(void *user_data)
{
struct fpi_ssm *ssm = user_data;
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
fp_warn("powerup timed out");
urudev->irq_cb = NULL;
@@ -1032,10 +1060,10 @@ static void init_scanpwr_timeout(void *user_data)
static void init_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct uru4k_dev *urudev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case INIT_GET_HWSTAT:
sm_read_reg(ssm, REG_HWSTAT);
break;
@@ -1047,9 +1075,9 @@ static void init_run_state(struct fpi_ssm *ssm)
fpi_ssm_jump_to_state(ssm, INIT_CHECK_HWSTAT_POWERDOWN);
break;
case INIT_REBOOT_POWER: ;
struct fpi_ssm *rebootsm = fpi_ssm_new(dev->dev, rebootpwr_run_state,
struct fpi_ssm *rebootsm = fpi_ssm_new(fpi_imgdev_get_dev(dev), rebootpwr_run_state,
REBOOTPWR_NUM_STATES);
rebootsm->priv = dev;
fpi_ssm_set_user_data(rebootsm, dev);
fpi_ssm_start_subsm(ssm, rebootsm);
break;
case INIT_CHECK_HWSTAT_POWERDOWN:
@@ -1066,9 +1094,9 @@ static void init_run_state(struct fpi_ssm *ssm)
urudev->irq_cb_data = ssm;
urudev->irq_cb = init_scanpwr_irq_cb;
struct fpi_ssm *powerupsm = fpi_ssm_new(dev->dev, powerup_run_state,
struct fpi_ssm *powerupsm = fpi_ssm_new(fpi_imgdev_get_dev(dev), powerup_run_state,
POWERUP_NUM_STATES);
powerupsm->priv = dev;
fpi_ssm_set_user_data(powerupsm, dev);
fpi_ssm_start_subsm(ssm, powerupsm);
break;
case INIT_AWAIT_SCAN_POWER:
@@ -1112,8 +1140,8 @@ static void init_run_state(struct fpi_ssm *ssm)
static void activate_initsm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
int r = ssm->error;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
if (r) {
@@ -1131,7 +1159,7 @@ static void activate_initsm_complete(struct fpi_ssm *ssm)
* call. */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
int r;
@@ -1141,8 +1169,8 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
urudev->scanpwr_irq_timeouts = 0;
urudev->activate_state = state;
ssm = fpi_ssm_new(dev->dev, init_run_state, INIT_NUM_STATES);
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), init_run_state, INIT_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, activate_initsm_complete);
return 0;
}
@@ -1167,7 +1195,7 @@ static void dev_deactivate(struct fp_img_dev *dev)
static int execute_state_change(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
switch (urudev->activate_state) {
@@ -1195,8 +1223,8 @@ static int execute_state_change(struct fp_img_dev *dev)
urudev->img_data = g_malloc(sizeof(struct uru4k_image));
urudev->img_enc_seed = rand();
ssm = fpi_ssm_new(dev->dev, imaging_run_state, IMAGING_NUM_STATES);
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), imaging_run_state, IMAGING_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, imaging_complete);
return write_reg(dev, REG_MODE, MODE_CAPTURE,
@@ -1229,7 +1257,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
int r;
/* Find fingerprint interface */
r = libusb_get_config_descriptor(libusb_get_device(dev->udev), 0, &config);
r = libusb_get_config_descriptor(libusb_get_device(fpi_imgdev_get_usb_dev(dev)), 0, &config);
if (r < 0) {
fp_err("Failed to get config descriptor");
return r;
@@ -1283,9 +1311,9 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
/* Device looks like a supported reader */
r = libusb_claim_interface(dev->udev, iface_desc->bInterfaceNumber);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), iface_desc->bInterfaceNumber);
if (r < 0) {
fp_err("interface claim failed");
fp_err("interface claim failed: %s", libusb_error_name(r));
goto out;
}
@@ -1323,7 +1351,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
}
urudev->param = PK11_ParamFromIV(urudev->cipher, NULL);
dev->priv = urudev;
fpi_imgdev_set_user_data(dev, urudev);
fpi_imgdev_open_complete(dev, 0);
out:
@@ -1333,14 +1361,14 @@ out:
static void dev_deinit(struct fp_img_dev *dev)
{
struct uru4k_dev *urudev = dev->priv;
struct uru4k_dev *urudev = fpi_imgdev_get_user_data(dev);
if (urudev->symkey)
PK11_FreeSymKey (urudev->symkey);
if (urudev->param)
SECITEM_FreeItem(urudev->param, PR_TRUE);
if (urudev->slot)
PK11_FreeSlot(urudev->slot);
libusb_release_interface(dev->udev, urudev->interface);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), urudev->interface);
g_free(urudev);
fpi_imgdev_close_complete(dev);
}

View File

@@ -19,6 +19,8 @@
#define FP_COMPONENT "vcom5s"
#include "drivers_api.h"
/* TODO:
* calibration?
* image size: windows gets 300x300 through vpas enrollment util?
@@ -26,16 +28,6 @@
* powerdown? does windows do anything special on exit?
*/
#include <errno.h>
#include <string.h>
#include <glib.h>
#include <libusb.h>
#include <fp_internal.h>
#include "driver_ids.h"
#define CTRL_IN 0xc0
#define CTRL_OUT 0x40
#define CTRL_TIMEOUT 1000
@@ -96,7 +88,7 @@ static void sm_write_reg_cb(struct libusb_transfer *transfer)
static void sm_write_reg(struct fpi_ssm *ssm, unsigned char reg,
unsigned char value)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
@@ -109,7 +101,7 @@ static void sm_write_reg(struct fpi_ssm *ssm, unsigned char reg,
fp_dbg("set %02x=%02x", reg, value);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_OUT, reg, value, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_write_reg_cb,
libusb_fill_control_transfer(transfer, fpi_imgdev_get_usb_dev(dev), data, sm_write_reg_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -135,7 +127,7 @@ static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
static void sm_exec_cmd(struct fpi_ssm *ssm, unsigned char cmd,
unsigned char param)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
unsigned char *data;
int r;
@@ -148,7 +140,7 @@ static void sm_exec_cmd(struct fpi_ssm *ssm, unsigned char cmd,
fp_dbg("cmd %02x param %02x", cmd, param);
data = g_malloc(LIBUSB_CONTROL_SETUP_SIZE);
libusb_fill_control_setup(data, CTRL_IN, cmd, param, 0, 0);
libusb_fill_control_transfer(transfer, dev->udev, data, sm_exec_cmd_cb,
libusb_fill_control_transfer(transfer, fpi_imgdev_get_usb_dev(dev), data, sm_exec_cmd_cb,
ssm, CTRL_TIMEOUT);
r = libusb_submit_transfer(transfer);
if (r < 0) {
@@ -200,8 +192,8 @@ static void capture_iterate(struct fpi_ssm *ssm);
static void capture_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
fpi_ssm_mark_aborted(ssm, -EIO);
@@ -228,8 +220,8 @@ out:
static void capture_iterate(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
int iteration = vdev->capture_iteration;
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
int r;
@@ -239,7 +231,7 @@ static void capture_iterate(struct fpi_ssm *ssm)
return;
}
libusb_fill_bulk_transfer(transfer, dev->udev, EP_IN,
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), EP_IN,
vdev->capture_img->data + (RQ_SIZE * iteration), RQ_SIZE,
capture_cb, ssm, CTRL_TIMEOUT);
transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
@@ -253,10 +245,10 @@ static void capture_iterate(struct fpi_ssm *ssm)
static void sm_do_capture(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
fp_dbg("");
G_DEBUG_HERE();
vdev->capture_img = fpi_img_new_for_imgdev(dev);
vdev->capture_iteration = 0;
capture_iterate(ssm);
@@ -275,10 +267,10 @@ enum loop_states {
static void loop_run_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case LOOP_SET_CONTRAST:
sm_write_reg(ssm, REG_CONTRAST, 0x01);
break;
@@ -303,9 +295,9 @@ static void loop_run_state(struct fpi_ssm *ssm)
static void loopsm_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct v5s_dev *vdev = dev->priv;
int r = ssm->error;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
int r = fpi_ssm_get_error(ssm);
fpi_ssm_free(ssm);
fp_img_free(vdev->capture_img);
@@ -321,10 +313,10 @@ static void loopsm_complete(struct fpi_ssm *ssm)
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct v5s_dev *vdev = dev->priv;
struct fpi_ssm *ssm = fpi_ssm_new(dev->dev, loop_run_state,
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), loop_run_state,
LOOP_NUM_STATES);
ssm->priv = dev;
fpi_ssm_set_user_data(ssm, dev);
vdev->deactivating = FALSE;
fpi_ssm_start(ssm, loopsm_complete);
vdev->loop_running = TRUE;
@@ -334,7 +326,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
static void dev_deactivate(struct fp_img_dev *dev)
{
struct v5s_dev *vdev = dev->priv;
struct v5s_dev *vdev = fpi_imgdev_get_user_data(dev);
if (vdev->loop_running)
vdev->deactivating = TRUE;
else
@@ -344,11 +336,14 @@ static void dev_deactivate(struct fp_img_dev *dev)
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
{
int r;
dev->priv = g_malloc0(sizeof(struct v5s_dev));
struct v5s_dev *v5s_dev;
r = libusb_claim_interface(dev->udev, 0);
v5s_dev = g_malloc0(sizeof(struct v5s_dev));
fpi_imgdev_set_user_data(dev, v5s_dev);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0)
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
if (r == 0)
fpi_imgdev_open_complete(dev, 0);
@@ -358,8 +353,10 @@ 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);
libusb_release_interface(dev->udev, 0);
struct v5s_dev *v5s_dev;
v5s_dev = fpi_imgdev_get_user_data(dev);
g_free(v5s_dev);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
fpi_imgdev_close_complete(dev);
}

791
libfprint/drivers/vfs0050.c Normal file
View File

@@ -0,0 +1,791 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@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 "vfs0050"
#include "drivers_api.h"
#include "vfs0050.h"
/* USB functions */
/* Callback for async_write */
static void async_write_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Written only %d of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Send data to EP1, the only out endpoint */
static void async_write(struct fpi_ssm *ssm, void *data, int len)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_imgdev_get_usb_dev(idev);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, 0x01, data, len,
async_write_callback, ssm, VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_read_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error =
transfer->status, len = transfer->length;
int ep = transfer->endpoint;
if (error != 0) {
fp_err("USB read transfer on endpoint %d: %s", ep - 0x80,
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
if (transferred != len) {
fp_err("Received %d instead of %d bytes", transferred, len);
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
fpi_ssm_next_state(ssm);
}
/* Receive data from the given ep and compare with expected */
static void async_read(struct fpi_ssm *ssm, int ep, void *data, int len)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_imgdev_get_usb_dev(idev);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, ep, data,
len, async_read_callback, ssm,
VFS_USB_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, ep, data, len,
async_read_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Callback for async_read */
static void async_abort_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
int transferred = transfer->actual_length, error = transfer->status;
int ep = transfer->endpoint;
/* In normal case endpoint is empty */
if (error == LIBUSB_TRANSFER_TIMED_OUT) {
fpi_ssm_next_state(ssm);
return;
}
if (error != 0) {
fp_err("USB write transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Don't stop process, only print warning */
if (transferred > 0)
fp_warn("Endpoint %d had extra %d bytes", ep - 0x80,
transferred);
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
/* Receive data from the given ep and compare with expected */
static void async_abort(struct fpi_ssm *ssm, int ep)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_imgdev_get_usb_dev(idev);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
int len = VFS_USB_BUFFER_SIZE;
unsigned char *data = g_malloc(VFS_USB_BUFFER_SIZE);
ep |= LIBUSB_ENDPOINT_IN;
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |=
LIBUSB_TRANSFER_FREE_TRANSFER | LIBUSB_TRANSFER_FREE_BUFFER;
/* 0x83 is the only interrupt endpoint */
if (ep == EP3_IN)
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, ep, data,
len, async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
else
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, ep, data, len,
async_abort_callback, ssm,
VFS_USB_ABORT_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
}
/* Image processing functions */
/* Pixel getter for fpi_assemble_lines */
static unsigned char vfs0050_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList * line, unsigned int x)
{
return ((struct vfs_line *)line->data)->data[x];
}
/* Deviation getter for fpi_assemble_lines */
static int vfs0050_get_difference(struct fpi_line_asmbl_ctx *ctx,
GSList * line_list_1, GSList * line_list_2)
{
struct vfs_line *line1 = line_list_1->data;
struct vfs_line *line2 = line_list_2->data;
const int shift = (VFS_IMAGE_WIDTH - VFS_NEXT_LINE_WIDTH) / 2 - 1;
int res = 0;
for (int i = 0; i < VFS_NEXT_LINE_WIDTH; ++i) {
int x =
(int)line1->next_line_part[i] - (int)line2->data[shift + i];
res += x * x;
}
return res;
}
#define VFS_NOISE_THRESHOLD 40
/* Checks whether line is noise or not using hardware parameters */
static char is_noise(struct vfs_line *line)
{
int val1 = line->noise_hash_1;
int val2 = line->noise_hash_2;
if (val1 > VFS_NOISE_THRESHOLD
&& val1 < 256 - VFS_NOISE_THRESHOLD
&& val2 > VFS_NOISE_THRESHOLD && val2 < 256 - VFS_NOISE_THRESHOLD)
return 1;
return 0;
}
/* Parameters for fpi_assemble_lines */
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS_IMAGE_WIDTH,
.max_height = VFS_MAX_HEIGHT,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 100,
.get_deviation = vfs0050_get_difference,
.get_pixel = vfs0050_get_pixel,
};
/* Processes image before submitting */
static struct fp_img *prepare_image(struct vfs_dev_t *vdev)
{
int height = vdev->bytes / VFS_LINE_SIZE;
/* Noise cleaning. IMHO, it works pretty well
I've not detected cases when it doesn't work or cuts a part of the finger
Noise arises at the end of scan when some water remains on the scanner */
while (height > 0) {
if (!is_noise(vdev->lines_buffer + height - 1))
break;
--height;
}
if (height > VFS_MAX_HEIGHT)
height = VFS_MAX_HEIGHT;
/* If image is not good enough */
if (height < VFS_IMAGE_WIDTH)
return NULL;
/* Building GSList */
GSList *lines = NULL;
for (int i = height - 1; i >= 0; --i)
lines = g_slist_prepend(lines, vdev->lines_buffer + i);
/* Perform line assembling */
struct fp_img *img = fpi_assemble_lines(&assembling_ctx, lines, height);
g_slist_free(lines);
return img;
}
/* Processes and submits image after fingerprint received */
static void submit_image(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
/* We were not asked to submit image actually */
if (!vdev->active)
return;
struct fp_img *img = prepare_image(vdev);
if (!img)
fpi_imgdev_abort_scan(idev, FP_VERIFY_RETRY_TOO_SHORT);
else
fpi_imgdev_image_captured(idev, img);
/* Finger not on the scanner */
fpi_imgdev_report_finger_status(idev, 0);
}
/* Proto functions */
/* SSM loop for clear_ep2 */
static void clear_ep2_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
short result;
char command04 = 0x04;
switch (fpi_ssm_get_cur_state(ssm)) {
case SUBSM1_COMMAND_04:
async_write(ssm, &command04, sizeof(command04));
break;
case SUBSM1_RETURN_CODE:
async_read(ssm, 1, &result, sizeof(result));
break;
case SUBSM1_ABORT_2:
async_abort(ssm, 2);
break;
default:
fp_err("Unknown SUBSM1 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Send command to clear EP2 */
static void clear_ep2(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct fpi_ssm *subsm =
fpi_ssm_new(fpi_imgdev_get_dev(idev), clear_ep2_ssm, SUBSM1_STATES);
fpi_ssm_set_user_data(subsm, idev);
fpi_ssm_start_subsm(ssm, subsm);
}
static void send_control_packet_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
short result;
unsigned char *commit_result = NULL;
switch (fpi_ssm_get_cur_state(ssm)) {
case SUBSM2_SEND_CONTROL:
async_write(ssm, vdev->control_packet, VFS_CONTROL_PACKET_SIZE);
break;
case SUBSM2_RETURN_CODE:
async_read(ssm, 1, &result, sizeof(result));
break;
case SUBSM2_SEND_COMMIT:
/* next_receive_* packets could be sent only in pair */
if (vdev->control_packet == next_receive_1) {
vdev->control_packet = next_receive_2;
fpi_ssm_jump_to_state(ssm, SUBSM2_SEND_CONTROL);
break;
}
/* commit_out in Windows differs in each commit, but I send the same each time */
async_write(ssm, commit_out, sizeof(commit_out));
break;
case SUBSM2_COMMIT_RESPONSE:
commit_result = g_malloc(VFS_COMMIT_RESPONSE_SIZE);
async_read(ssm, 1, commit_result, VFS_COMMIT_RESPONSE_SIZE);
break;
case SUBSM2_READ_EMPTY_INTERRUPT:
/* I don't know how to check result, it could be different */
g_free(commit_result);
async_read(ssm, 3, vdev->interrupt, VFS_INTERRUPT_SIZE);
break;
case SUBSM2_ABORT_3:
/* Check that interrupt is empty */
if (memcmp
(vdev->interrupt, empty_interrupt, VFS_INTERRUPT_SIZE)) {
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
break;
}
async_abort(ssm, 3);
break;
case SUBSM2_CLEAR_EP2:
/* After turn_on Windows doesn't clear EP2 */
if (vdev->control_packet != turn_on)
clear_ep2(ssm);
else
fpi_ssm_next_state(ssm);
break;
default:
fp_err("Unknown SUBSM2 state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Send device state control packet */
static void send_control_packet(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct fpi_ssm *subsm =
fpi_ssm_new(fpi_imgdev_get_dev(idev), send_control_packet_ssm, SUBSM2_STATES);
fpi_ssm_set_user_data(subsm, idev);
fpi_ssm_start_subsm(ssm, subsm);
}
/* Clears all fprint data */
static void clear_data(struct vfs_dev_t *vdev)
{
g_free(vdev->lines_buffer);
vdev->lines_buffer = NULL;
vdev->memory = vdev->bytes = 0;
}
/* After receiving interrupt from EP3 */
static void interrupt_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
char *interrupt = vdev->interrupt;
int error = transfer->status, transferred = transfer->actual_length;
vdev->wait_interrupt = 0;
/* When we have cancelled transfer, error is ok actually */
if (!vdev->active && error == LIBUSB_TRANSFER_CANCELLED)
return;
if (error != 0) {
fp_err("USB read interrupt transfer: %s",
libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Interrupt size is VFS_INTERRUPT_SIZE bytes in all known cases */
if (transferred != VFS_INTERRUPT_SIZE) {
fp_err("Unknown interrupt size %d", transferred);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Standard interrupts */
if (memcmp(interrupt, interrupt1, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt2, VFS_INTERRUPT_SIZE) == 0 ||
memcmp(interrupt, interrupt3, VFS_INTERRUPT_SIZE) == 0) {
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* When finger is on the scanner before turn_on */
if (interrupt[0] == 0x01) {
fp_warn("Finger is already on the scanner");
/* Go to the next ssm stage */
fpi_ssm_next_state(ssm);
return;
}
/* Unknown interrupt; abort the session */
fp_err("Unknown interrupt '%02x:%02x:%02x:%02x:%02x'!",
interrupt[0] & 0xff, interrupt[1] & 0xff, interrupt[2] & 0xff,
interrupt[3] & 0xff, interrupt[4] & 0xff);
/* Abort ssm */
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
static void receive_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
int transferred = transfer->actual_length, error = transfer->status;
if (error != 0 && error != LIBUSB_TRANSFER_TIMED_OUT) {
fp_err("USB read transfer: %s", libusb_error_name(error));
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
return;
}
/* Check if fingerprint data is over */
if (transferred == 0) {
fpi_ssm_next_state(ssm);
} else {
vdev->bytes += transferred;
/* We need more data */
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
}
/* Stub to keep SSM alive when waiting an interrupt */
static void wait_interrupt(void *data)
{
struct fpi_ssm *ssm = data;
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
/* Keep sleeping while this flag is on */
if (vdev->wait_interrupt)
fpi_ssm_jump_to_state(ssm, fpi_ssm_get_cur_state(ssm));
}
/* SSM stub to prepare device to another scan after orange light was on */
static void another_scan(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_jump_to_state(ssm, SSM_TURN_ON);
}
/* Another SSM stub to continue after waiting for probable vdev->active changes */
static void scan_completed(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/* Main SSM loop */
static void activate_ssm(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct libusb_device_handle *usb_dev = fpi_imgdev_get_usb_dev(idev);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
switch (fpi_ssm_get_cur_state(ssm)) {
case SSM_INITIAL_ABORT_1:
async_abort(ssm, 1);
break;
case SSM_INITIAL_ABORT_2:
async_abort(ssm, 2);
break;
case SSM_INITIAL_ABORT_3:
async_abort(ssm, 3);
break;
case SSM_CLEAR_EP2:
clear_ep2(ssm);
break;
case SSM_TURN_OFF:
/* Set control_packet argument */
vdev->control_packet = turn_off;
send_control_packet(ssm);
break;
case SSM_TURN_ON:
if (!vdev->active) {
/* The only correct exit */
fpi_ssm_mark_completed(ssm);
if (vdev->need_report) {
fpi_imgdev_deactivate_complete(idev);
vdev->need_report = 0;
}
break;
}
/* Set control_packet argument */
vdev->control_packet = turn_on;
send_control_packet(ssm);
break;
case SSM_ASK_INTERRUPT:
/* Activated, light must be blinking now */
/* If we first time here, report that activate completed */
if (vdev->need_report) {
fpi_imgdev_activate_complete(idev, 0);
vdev->need_report = 0;
}
/* Asyncronously enquire an interrupt */
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_interrupt_transfer(vdev->transfer, usb_dev, 0x83,
vdev->interrupt,
VFS_INTERRUPT_SIZE,
interrupt_callback, ssm, 0);
libusb_submit_transfer(vdev->transfer);
/* This flag could be turned off only in callback function */
vdev->wait_interrupt = 1;
/* I've put it here to be sure that data is cleared */
clear_data(vdev);
fpi_ssm_next_state(ssm);
break;
case SSM_WAIT_INTERRUPT:
/* Check if user had interrupted the process */
if (!vdev->active) {
libusb_cancel_transfer(vdev->transfer);
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
if (vdev->wait_interrupt)
fpi_timeout_add(VFS_SSM_TIMEOUT, wait_interrupt, ssm);
break;
case SSM_RECEIVE_FINGER:
if (vdev->memory == 0) {
/* Initialize fingerprint buffer */
g_free(vdev->lines_buffer);
vdev->memory = VFS_USB_BUFFER_SIZE;
vdev->lines_buffer = g_malloc(vdev->memory);
vdev->bytes = 0;
/* Finger is on the scanner */
fpi_imgdev_report_finger_status(idev, 1);
}
/* Increase buffer size while it's insufficient */
while (vdev->bytes + VFS_USB_BUFFER_SIZE > vdev->memory) {
vdev->memory <<= 1;
vdev->lines_buffer =
(struct vfs_line *)g_realloc(vdev->lines_buffer,
vdev->memory);
}
/* Receive chunk of data */
vdev->transfer = libusb_alloc_transfer(0);
vdev->transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_fill_bulk_transfer(vdev->transfer, usb_dev, 0x82,
(void *)vdev->lines_buffer +
vdev->bytes, VFS_USB_BUFFER_SIZE,
receive_callback, ssm,
VFS_USB_TIMEOUT);
libusb_submit_transfer(vdev->transfer);
break;
case SSM_SUBMIT_IMAGE:
submit_image(idev);
clear_data(vdev);
/* Wait for probable vdev->active changing */
fpi_timeout_add(VFS_SSM_TIMEOUT, scan_completed, ssm);
break;
case SSM_NEXT_RECEIVE:
if (!vdev->active) {
/* It's the last scan */
fpi_ssm_jump_to_state(ssm, SSM_CLEAR_EP2);
break;
}
/* Set control_packet argument */
vdev->control_packet = next_receive_1;
send_control_packet(ssm);
break;
case SSM_WAIT_ANOTHER_SCAN:
/* Orange light is on now */
fpi_timeout_add(VFS_SSM_ORANGE_TIMEOUT, another_scan, ssm);
break;
default:
fp_err("Unknown state");
fpi_imgdev_session_error(idev, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
}
}
/* Driver functions */
/* Callback for dev_activate ssm */
static void dev_activate_callback(struct fpi_ssm *ssm)
{
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
vdev->ssm_active = 0;
fpi_ssm_free(ssm);
}
/* Activate device */
static int dev_activate(struct fp_img_dev *idev, enum fp_imgdev_state state)
{
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
/* Initialize flags */
vdev->active = 1;
vdev->need_report = 1;
vdev->ssm_active = 1;
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(idev), activate_ssm, SSM_STATES);
fpi_ssm_set_user_data(ssm, idev);
fpi_ssm_start(ssm, dev_activate_callback);
return 0;
}
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev = fpi_imgdev_get_user_data(idev);
if (!vdev->ssm_active) {
fpi_imgdev_deactivate_complete(idev);
return;
}
/* Initialize flags */
vdev->active = 0;
vdev->need_report = 1;
}
/* Callback for dev_open ssm */
static void dev_open_callback(struct fpi_ssm *ssm)
{
/* Notify open complete */
fpi_imgdev_open_complete(fpi_ssm_get_user_data(ssm), 0);
fpi_ssm_free(ssm);
}
/* Open device */
static int dev_open(struct fp_img_dev *idev, unsigned long driver_data)
{
struct vfs_dev_t *vdev;
/* Claim usb interface */
int error = libusb_claim_interface(fpi_imgdev_get_usb_dev(idev), 0);
if (error < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
return error;
}
/* Initialize private structure */
vdev = g_malloc0(sizeof(struct vfs_dev_t));
fpi_imgdev_set_user_data(idev, vdev);
/* Clearing previous device state */
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(idev), activate_ssm, SSM_STATES);
fpi_ssm_set_user_data(ssm, idev);
fpi_ssm_start(ssm, dev_open_callback);
return 0;
}
/* Close device */
static void dev_close(struct fp_img_dev *idev)
{
struct vfs_dev_t *vdev;
/* Release private structure */
vdev = fpi_imgdev_get_user_data(idev);
g_free(vdev);
/* Release usb interface */
libusb_release_interface(fpi_imgdev_get_usb_dev(idev), 0);
/* Notify close complete */
fpi_imgdev_close_complete(idev);
}
/* Usb id table of device */
static const struct usb_id id_table[] = {
{.vendor = 0x138a,.product = 0x0050},
{0, 0, 0,},
};
/* Device driver definition */
struct fp_img_driver vfs0050_driver = {
/* Driver specification */
.driver = {
.id = VFS0050_ID,
.name = FP_COMPONENT,
.full_name = "Validity VFS0050",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
/* Image specification */
.flags = 0,
.img_width = VFS_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 24,
/* Routine specification */
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

382
libfprint/drivers/vfs0050.h Normal file
View File

@@ -0,0 +1,382 @@
/*
* Validity VFS0050 driver for libfprint
* Copyright (C) 2015-2016 Konstantin Semenov <zemen17@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
*/
/* Timeout for all send/recv operations, except interrupt waiting and abort */
#define VFS_USB_TIMEOUT 100
/* Timeout for usb abort */
#define VFS_USB_ABORT_TIMEOUT 20
/* Default timeout for SSM timers */
#define VFS_SSM_TIMEOUT 100
/* Timeout for orange light */
#define VFS_SSM_ORANGE_TIMEOUT 400
/* Buffer size for abort and fprint receiving */
#define VFS_USB_BUFFER_SIZE 65536
/* Line size from scanner including metainformation: line number, narrow stripe from the center, etc */
#define VFS_LINE_SIZE 148
/* Width of narrow stripe from the center */
#define VFS_NEXT_LINE_WIDTH 32
/* Image width from scanner */
#define VFS_IMAGE_WIDTH 100
/* Maximum image height after assembling */
#define VFS_MAX_HEIGHT 3000
/* Size of control packets: turn_on, turn_off, next_receive_* */
#define VFS_CONTROL_PACKET_SIZE 125
/* Size of result of commit */
#define VFS_COMMIT_RESPONSE_SIZE 1106
/* Size of interrupt from EP3 */
#define VFS_INTERRUPT_SIZE 5
/* EP3 endpoint */
#define EP3_IN 0x83
/* Fingerprint horizontal line */
struct vfs_line {
/* It must be always 0x01 */
unsigned char _0x01;
/* It must be always 0xfe */
unsigned char _0xfe;
/* line number starting from some number in Little-Endian */
unsigned short id;
/* Some hashes which are useful to detect noise */
unsigned char noise_hash_1;
unsigned char noise_hash_2;
/* The first byte of _somedata is always 0x00, the second is strange useless cyclic line number */
unsigned short _somedata;
/* Fingerprint image */
unsigned char data[VFS_IMAGE_WIDTH];
/* Narrow fingerprint part from the center used for variable speed lines assembling */
unsigned char next_line_part[VFS_NEXT_LINE_WIDTH];
/* scan_data is 0xfb except some rare cases, it's skipped */
unsigned char scan_data[8];
} __attribute__ ((__packed__));
/* The main driver structure */
struct vfs_dev_t {
/* One if we were asked to read fingerprint, zero otherwise */
char active;
/* Control packet parameter for send_control_packet */
unsigned char *control_packet;
/* For dev_deactivate to check whether ssm still running or not */
char ssm_active;
/* Current async transfer */
struct libusb_transfer *transfer;
/* Should we call fpi_imgdev_activate_complete or fpi_imgdev_deactivate_complete */
char need_report;
/* Should we wait more for interrupt */
char wait_interrupt;
/* Received fingerprint raw lines */
struct vfs_line *lines_buffer;
/* Current number of received bytes and current memory used by data */
int bytes, memory;
/* USB buffer for fingerprint */
char *usb_buffer;
/* Received interrupt data */
unsigned char interrupt[8];
};
/* SSM states for clear_ep2 */
enum SUBSM1 {
SUBSM1_COMMAND_04,
SUBSM1_RETURN_CODE,
SUBSM1_ABORT_2,
SUBSM1_STATES,
};
/* SSM states for control */
enum SUBSM2 {
SUBSM2_SEND_CONTROL,
SUBSM2_RETURN_CODE, /* If next_receive, send another control packet */
SUBSM2_SEND_COMMIT,
SUBSM2_COMMIT_RESPONSE,
SUBSM2_READ_EMPTY_INTERRUPT,
SUBSM2_ABORT_3,
SUBSM2_CLEAR_EP2,
SUBSM2_STATES,
};
/* SSM states for activate_ssm */
enum SSM_STATE {
SSM_INITIAL_ABORT_1,
SSM_INITIAL_ABORT_2,
SSM_INITIAL_ABORT_3,
SSM_CLEAR_EP2,
SSM_TURN_OFF,
/* Here the device is turned off; if not active, complete ssm */
SSM_TURN_ON,
SSM_ASK_INTERRUPT,
SSM_WAIT_INTERRUPT,
SSM_RECEIVE_FINGER,
SSM_SUBMIT_IMAGE,
/* If not active, jump to CLEAR_EP2 */
SSM_NEXT_RECEIVE,
SSM_WAIT_ANOTHER_SCAN,
/* Jump to TURN_ON */
SSM_STATES
};
/* Blocks of data from USB sniffer */
/* Turns on the light */
static unsigned char turn_on[] = {
0x39, 0x20, 0xBF, 0x02, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0xD1, 0xD1, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xF4, 0x01, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 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, 0x00, 0x00, 0x00,
0x00,
};
/* Power off */
static unsigned char turn_off[] = {
0x39, 0x01, 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, 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, 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,
};
/* Turns on orange light */
static unsigned char next_receive_1[] = {
0x39, 0xB8, 0x0B, 0x00, 0x00, 0xB8, 0x0B, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xB8, 0x0B, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 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, 0x00, 0x00, 0x00,
0x00,
};
/* Packet directly after next_receive_1 */
static unsigned char next_receive_2[] = {
0x39, 0xE8, 0x03, 0x00, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x01, 0xD1, 0x00,
0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xE8, 0x03, 0x00, 0x00, 0x02, 0xD1, 0x00, 0x20, 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, 0x00, 0x00, 0x00,
0x00,
};
/* Commit message */
static unsigned char commit_out[] = {
0x02, 0x94, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x2C, 0x03, 0x00,
0x30, 0x1B, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x03, 0x00, 0x30, 0x3D, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x18, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x28, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x30, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x38, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x3C, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x44, 0x03, 0x00,
0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x48, 0x03, 0x00, 0x30, 0x01, 0x04, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x4C, 0x03, 0x00, 0x30, 0x01, 0x0C, 0x02, 0x00, 0x20, 0x00, 0x08,
0x00, 0x54, 0x03, 0x00,
0x30, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x5C, 0x03, 0x00,
0x30, 0x90, 0x01, 0x02,
0x00, 0x20, 0x00, 0x08, 0x00, 0x60, 0x03, 0x00, 0x30, 0x2C, 0x01, 0x19,
0x00, 0x20, 0x00, 0x08,
0x00, 0x64, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x6C, 0x03, 0x00,
0x30, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x03, 0x00,
0x30, 0x21, 0x80, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x78, 0x03, 0x00, 0x30, 0x09, 0x00, 0x02,
0x00, 0x20, 0x00, 0x08,
0x00, 0x7C, 0x03, 0x00, 0x30, 0x0B, 0x00, 0x19, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x84, 0x03, 0x00,
0x30, 0x3A, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x88, 0x03, 0x00, 0x30, 0x14, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x8C, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x90, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x94, 0x03, 0x00,
0x30, 0x08, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x98, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1,
0x01, 0x20, 0x00, 0x08,
0x00, 0x9C, 0x03, 0x00, 0x30, 0x00, 0x00, 0xA1, 0x01, 0x20, 0x00, 0x08,
0x00, 0xA8, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xAC, 0x03, 0x00,
0x30, 0x64, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xB0, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0xB4, 0x03, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0xB8, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0xBC, 0x03, 0x00,
0x30, 0x05, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0xC0, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x84, 0x03, 0x00, 0x30, 0x3B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x08, 0x07, 0x00,
0x30, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x0C, 0x07, 0x00,
0x30, 0x00, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08, 0x00, 0x14, 0x07, 0x00, 0x30, 0x20, 0x00, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00, 0x30, 0x1A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x70, 0x0D, 0x00,
0x30, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 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, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x2B, 0xFF, 0x2B, 0xFF, 0x2B,
0xED, 0x00, 0x00, 0x2B,
0xFB, 0x00, 0x00, 0x2B, 0xC5, 0x00, 0x00, 0x2B, 0x05, 0x80, 0x70, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0xD3, 0x2E, 0xC0, 0x2C, 0x3B, 0x08, 0xF0, 0x3B, 0x09, 0x24,
0xBB, 0x3B, 0x0B, 0x24,
0xAA, 0x3B, 0x1F, 0xF8, 0x00, 0x3B, 0x3F, 0xF0, 0x00, 0x3B, 0x35, 0xC0,
0x00, 0x38, 0x80, 0x2C,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x38, 0x80, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xC0,
0x3A, 0x80, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x3B, 0x0A, 0x80,
0x2E, 0x83, 0x24, 0xDB,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x2C, 0x31, 0x83, 0x2C, 0x70,
0x00, 0x00, 0x00, 0x00,
0xCB, 0x33, 0x1B, 0x83, 0x2C, 0x70, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x31,
0x83, 0x2C, 0x70, 0x00,
0x00, 0x00, 0x00, 0xCB, 0x00, 0x33, 0x1E, 0x83, 0x2E, 0x25, 0xFF, 0xC4,
0x00, 0x2F, 0x06, 0x84,
0x2E, 0x00, 0x00, 0x10, 0x20, 0x29, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00,
0x00, 0x23, 0x00, 0x00,
0x00, 0x21, 0x00, 0x10, 0x00, 0x48, 0x03, 0x00, 0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x4C, 0x03, 0x00,
0x30, 0xFF, 0xF0, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x21, 0x00, 0x10,
0x00, 0x20, 0x03, 0x00,
0x30, 0x7F, 0x00, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x10, 0x00,
0x00, 0x20, 0x00, 0x08,
0x00, 0x24, 0x03, 0x00, 0x30, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x1C, 0x07, 0x00,
0x30, 0x1A, 0x00, 0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x20, 0x03, 0x00,
0x30, 0xC3, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08,
0x00, 0x80, 0x03, 0x00,
0x30, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x84, 0x00, 0x31, 0x65, 0x77,
0x77, 0x77, 0x78, 0x88,
0x77, 0x77, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x77, 0x67,
0x66, 0x66, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x66, 0x66, 0x66,
0x66, 0x77, 0x66, 0x66,
0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x76, 0x66, 0x56,
0x66, 0x66, 0x56, 0x55,
0x65, 0x66, 0x66, 0x66, 0x65, 0x66, 0x66, 0x55, 0x66, 0x66, 0x65, 0x66,
0x76, 0x76, 0x77, 0x77,
0x66, 0x66, 0x66, 0x76, 0x67, 0x66, 0x77, 0x67, 0x66, 0x66, 0x66, 0x56,
0x65, 0x66, 0x65, 0x66,
0x66, 0x55, 0x55, 0x54, 0x55, 0x65, 0x66, 0x66, 0x66, 0x76, 0x77, 0x87,
0x88, 0x77, 0x66, 0x66,
0x66, 0x66, 0x66, 0x66, 0x66, 0x65, 0x66, 0x55, 0x55, 0x65, 0x56, 0x55,
0x55, 0x55, 0x54, 0x45,
0x54, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77,
0x66, 0x26, 0x00, 0x28,
0x00, 0xFF, 0x00, 0x0F, 0x00, 0xF0, 0xF0, 0x0F, 0x00, 0x20, 0x00, 0x00,
0x00, 0x30, 0x01, 0x02,
0x00, 0x2C, 0x01, 0x28, 0x00, 0x20, 0x80, 0x00, 0x00, 0x0A, 0x00, 0x02,
0x00, 0x0B, 0x00, 0x19,
0x00, 0x40, 0x1F, 0x10, 0x27, 0x00, 0x0F, 0x03, 0x00,
};
/* Known interrupts */
static unsigned char empty_interrupt[] = {
0x00, 0x00, 0x00, 0x00, 0x00,
};
static unsigned char interrupt1[] = {
0x02, 0x00, 0x0E, 0x00, 0xF0,
};
static unsigned char interrupt2[] = {
0x02, 0x04, 0x0A, 0x00, 0xF0,
};
static unsigned char interrupt3[] = {
0x02, 0x00, 0x0A, 0x00, 0xF0,
};

View File

@@ -17,15 +17,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define FP_COMPONENT "vfs101"
#include <fp_internal.h>
#include "driver_ids.h"
#include "drivers_api.h"
/* Input-Output usb endpoint */
#define EP_IN(n) (n | LIBUSB_ENDPOINT_IN)
@@ -64,9 +58,6 @@
/* Best image contrast */
#define VFS_IMG_BEST_CONRAST 128
/* Number of enroll stages */
#define VFS_NR_ENROLL 3
/* Device parameters address */
#define VFS_PAR_000E 0x000e
#define VFS_PAR_0011 0x0011
@@ -189,11 +180,11 @@ static int result_codes[2][RESULT_COUNT] =
static int result_code(struct fp_img_dev *dev, int result)
{
/* Check result value */
if (result < 0 && result >= RESULT_COUNT)
if (result < 0 || result >= RESULT_COUNT)
return result;
/* Return result code */
if (dev->action == IMG_ACTION_ENROLL)
if (fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL)
return result_codes[0][result];
else
return result_codes[1][result];
@@ -209,8 +200,8 @@ static int result_code(struct fp_img_dev *dev, int result)
static void async_send_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Cleanup transfer */
vdev->transfer = NULL;
@@ -253,8 +244,8 @@ out:
/* Submit asynchronous send */
static void async_send(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
int r;
/* Allocation of transfer */
@@ -274,7 +265,7 @@ static void async_send(struct fpi_ssm *ssm)
vdev->buffer[1] = byte(1, vdev->seqnum);
/* Prepare bulk transfer */
libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_OUT(1), vdev->buffer, vdev->length, async_send_cb, ssm, BULK_TIMEOUT);
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_OUT(1), vdev->buffer, vdev->length, async_send_cb, ssm, BULK_TIMEOUT);
/* Submit transfer */
r = libusb_submit_transfer(vdev->transfer);
@@ -293,8 +284,8 @@ static void async_send(struct fpi_ssm *ssm)
static void async_recv_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Cleanup transfer */
vdev->transfer = NULL;
@@ -340,8 +331,8 @@ out:
/* Submit asynchronous recv */
static void async_recv(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
int r;
/* Allocation of transfer */
@@ -356,7 +347,7 @@ static void async_recv(struct fpi_ssm *ssm)
}
/* Prepare bulk transfer */
libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_IN(1), vdev->buffer, 0x0f, async_recv_cb, ssm, BULK_TIMEOUT);
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_IN(1), vdev->buffer, 0x0f, async_recv_cb, ssm, BULK_TIMEOUT);
/* Submit transfer */
r = libusb_submit_transfer(vdev->transfer);
@@ -377,8 +368,8 @@ static void async_load(struct fpi_ssm *ssm);
static void async_load_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Cleanup transfer */
vdev->transfer = NULL;
@@ -441,8 +432,8 @@ out:
/* Submit asynchronous load */
static void async_load(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
unsigned char *buffer;
int r;
@@ -461,7 +452,7 @@ static void async_load(struct fpi_ssm *ssm)
buffer = vdev->buffer + vdev->length;
/* Prepare bulk transfer */
libusb_fill_bulk_transfer(vdev->transfer, dev->udev, EP_IN(2), buffer, VFS_BLOCK_SIZE, async_load_cb, ssm, BULK_TIMEOUT);
libusb_fill_bulk_transfer(vdev->transfer, fpi_imgdev_get_usb_dev(dev), EP_IN(2), buffer, VFS_BLOCK_SIZE, async_load_cb, ssm, BULK_TIMEOUT);
/* Submit transfer */
r = libusb_submit_transfer(vdev->transfer);
@@ -480,8 +471,8 @@ static void async_load(struct fpi_ssm *ssm)
static void async_sleep_cb(void *data)
{
struct fpi_ssm *ssm = data;
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Cleanup timeout */
vdev->timeout = NULL;
@@ -492,8 +483,8 @@ static void async_sleep_cb(void *data)
/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Add timeout */
vdev->timeout = fpi_timeout_add(msec, async_sleep_cb, ssm);
@@ -518,7 +509,7 @@ enum
/* Exec swap sequential state machine */
static void m_swap_state(struct fpi_ssm *ssm)
{
switch (ssm->cur_state)
switch (fpi_ssm_get_cur_state(ssm))
{
case M_SWAP_SEND:
/* Send data */
@@ -535,8 +526,8 @@ static void m_swap_state(struct fpi_ssm *ssm)
/* Start swap sequential state machine */
static void m_swap(struct fpi_ssm *ssm, unsigned char *data, size_t length)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *subsm;
/* Prepare data for sending */
@@ -545,8 +536,8 @@ static void m_swap(struct fpi_ssm *ssm, unsigned char *data, size_t length)
vdev->length = length;
/* Start swap ssm */
subsm = fpi_ssm_new(dev->dev, m_swap_state, M_SWAP_NUM_STATES);
subsm->priv = dev;
subsm = fpi_ssm_new(fpi_imgdev_get_dev(dev), m_swap_state, M_SWAP_NUM_STATES);
fpi_ssm_set_user_data(subsm, dev);
fpi_ssm_start_subsm(ssm, subsm);
}
@@ -592,7 +583,7 @@ static void vfs_abort_print(struct fpi_ssm *ssm)
{
unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00 };
fp_dbg("");
G_DEBUG_HERE();
/* Run swap sequential state machine */
m_swap (ssm, data, 0x06);
@@ -625,7 +616,7 @@ static void vfs_get_finger_state(struct fpi_ssm *ssm)
{
unsigned char data[0x06] = { 0x00, 0x00, 0x00, 0x00, 0x16, 0x00 };
fp_dbg("");
G_DEBUG_HERE();
/* Run swap sequential state machine */
m_swap (ssm, data, 0x06);
@@ -634,10 +625,10 @@ static void vfs_get_finger_state(struct fpi_ssm *ssm)
/* Load raw image from reader */
static void vfs_img_load(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
fp_dbg("");
G_DEBUG_HERE();
/* Reset buffer length */
vdev->length = 0;
@@ -653,10 +644,10 @@ static void vfs_img_load(struct fpi_ssm *ssm)
/* Check if action is completed */
static int action_completed(struct fp_img_dev *dev)
{
struct vfs101_dev *vdev = dev->priv;
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
if ((dev->action == IMG_ACTION_ENROLL) &&
(vdev->enroll_stage < VFS_NR_ENROLL))
if ((fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL) &&
(vdev->enroll_stage < fpi_dev_get_nr_enroll_stages(fpi_imgdev_get_dev(dev))))
/* Enroll not completed, return false */
return FALSE;
@@ -765,8 +756,8 @@ static void img_copy(struct vfs101_dev *vdev, struct fp_img *img)
/* Extract fingerpint image from raw data */
static void img_extract(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
struct fp_img *img;
/* Screen image to remove noise and find top and bottom line */
@@ -796,28 +787,28 @@ static void img_extract(struct fpi_ssm *ssm)
fpi_imgdev_image_captured(dev, img);
/* Check captured result */
if (dev->action_result >= 0 &&
dev->action_result != FP_ENROLL_RETRY &&
dev->action_result != FP_VERIFY_RETRY)
if (fpi_imgdev_get_action_result(dev) >= 0 &&
fpi_imgdev_get_action_result(dev) != FP_ENROLL_RETRY &&
fpi_imgdev_get_action_result(dev) != FP_VERIFY_RETRY)
{
/* Image captured, increase enroll stage */
vdev->enroll_stage++;
/* Check if action is completed */
if (!action_completed(dev))
dev->action_result = FP_ENROLL_PASS;
fpi_imgdev_set_action_result(dev, FP_ENROLL_PASS);
}
else
{
/* Image capture failed */
if (dev->action == IMG_ACTION_ENROLL)
if (fpi_imgdev_get_action(dev) == IMG_ACTION_ENROLL)
/* Return retry */
dev->action_result = result_code(dev, RESULT_RETRY);
fpi_imgdev_set_action_result(dev, result_code(dev, RESULT_RETRY));
else
{
/* Return no match */
vdev->enroll_stage++;
dev->action_result = FP_VERIFY_NO_MATCH;
fpi_imgdev_set_action_result(dev, FP_VERIFY_NO_MATCH);
}
}
@@ -923,8 +914,8 @@ enum
/* Exec loop sequential state machine */
static void m_loop_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Check action state */
if (!vdev->active)
@@ -934,7 +925,7 @@ static void m_loop_state(struct fpi_ssm *ssm)
return;
}
switch (ssm->cur_state)
switch (fpi_ssm_get_cur_state(ssm))
{
case M_LOOP_0_GET_PRINT:
/* Send get print command to the reader */
@@ -1187,8 +1178,8 @@ enum
/* Exec init sequential state machine */
static void m_init_state(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Check action state */
if (!vdev->active)
@@ -1198,7 +1189,7 @@ static void m_init_state(struct fpi_ssm *ssm)
return;
}
switch (ssm->cur_state)
switch (fpi_ssm_get_cur_state(ssm))
{
case M_INIT_0_RECV_DIRTY:
/* Recv eventualy dirty data */
@@ -1428,18 +1419,18 @@ static void m_init_state(struct fpi_ssm *ssm)
/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct vfs101_dev *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm_loop;
if (!ssm->error && vdev->active)
if (!fpi_ssm_get_error(ssm) && vdev->active)
{
/* 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;
ssm_loop = fpi_ssm_new(fpi_imgdev_get_dev(dev), m_loop_state, M_LOOP_NUM_STATES);
fpi_ssm_set_user_data(ssm_loop, dev);
fpi_ssm_start(ssm_loop, m_loop_complete);
}
@@ -1450,7 +1441,7 @@ static void m_init_complete(struct fpi_ssm *ssm)
/* Activate device */
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct vfs101_dev *vdev = dev->priv;
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
struct fpi_ssm *ssm;
/* Check if already active */
@@ -1473,8 +1464,8 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
vdev->enroll_stage = 0;
/* Start init ssm */
ssm = fpi_ssm_new(dev->dev, m_init_state, M_INIT_NUM_STATES);
ssm->priv = dev;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), m_init_state, M_INIT_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, m_init_complete);
return 0;
@@ -1483,7 +1474,7 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
/* Deactivate device */
static void dev_deactivate(struct fp_img_dev *dev)
{
struct vfs101_dev *vdev = dev->priv;
struct vfs101_dev *vdev = fpi_imgdev_get_user_data(dev);
/* Reset active state */
vdev->active = FALSE;
@@ -1503,21 +1494,18 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
int r;
/* Claim usb interface */
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0)
{
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
/* Set enroll stage number */
dev->dev->nr_enroll_stages = VFS_NR_ENROLL;
/* Initialize private structure */
vdev = g_malloc0(sizeof(struct vfs101_dev));
vdev->seqnum = -1;
dev->priv = vdev;
fpi_imgdev_set_user_data(dev, vdev);
/* Notify open complete */
fpi_imgdev_open_complete(dev, 0);
@@ -1528,11 +1516,14 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
/* Close device */
static void dev_close(struct fp_img_dev *dev)
{
struct vfs101_dev *vdev;
/* Release private structure */
g_free(dev->priv);
vdev = fpi_imgdev_get_user_data(dev);
g_free(vdev);
/* Release usb interface */
libusb_release_interface(dev->udev, 0);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);

View File

@@ -21,20 +21,8 @@
#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 "drivers_api.h"
#include "vfs301_proto.h"
#include <unistd.h>
#include <fp_internal.h>
#include "driver_ids.h"
/************************** GENERIC STUFF *************************************/
@@ -49,7 +37,7 @@ static void async_sleep_cb(void *data)
/* Submit asynchronous sleep */
static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct fpi_timeout *timeout;
/* Add timeout */
@@ -65,8 +53,8 @@ static void async_sleep(unsigned int msec, struct fpi_ssm *ssm)
static int submit_image(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
vfs301_dev_t *vdev = dev->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
vfs301_dev_t *vdev = fpi_imgdev_get_user_data(dev);
int height;
struct fp_img *img;
@@ -117,12 +105,12 @@ enum
/* 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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
vfs301_dev_t *vdev = fpi_imgdev_get_user_data(dev);
switch (ssm->cur_state) {
switch (fpi_ssm_get_cur_state(ssm)) {
case M_REQUEST_PRINT:
vfs301_proto_request_fingerprint(dev->udev, vdev);
vfs301_proto_request_fingerprint(fpi_imgdev_get_usb_dev(dev), vdev);
fpi_ssm_next_state(ssm);
break;
@@ -132,7 +120,7 @@ static void m_loop_state(struct fpi_ssm *ssm)
break;
case M_CHECK_PRINT:
if (!vfs301_proto_peek_event(dev->udev, vdev))
if (!vfs301_proto_peek_event(fpi_imgdev_get_usb_dev(dev), vdev))
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
else
fpi_ssm_next_state(ssm);
@@ -140,7 +128,7 @@ static void m_loop_state(struct fpi_ssm *ssm)
case M_READ_PRINT_START:
fpi_imgdev_report_finger_status(dev, TRUE);
vfs301_proto_process_event_start(dev->udev, vdev);
vfs301_proto_process_event_start(fpi_imgdev_get_usb_dev(dev), vdev);
fpi_ssm_next_state(ssm);
break;
@@ -151,8 +139,8 @@ static void m_loop_state(struct fpi_ssm *ssm)
case M_READ_PRINT_POLL:
{
int rv = vfs301_proto_process_event_poll(dev->udev, vdev);
assert(rv != VFS301_FAILURE);
int rv = vfs301_proto_process_event_poll(fpi_imgdev_get_usb_dev(dev), vdev);
g_assert(rv != VFS301_FAILURE);
if (rv == VFS301_ONGOING)
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
else
@@ -182,12 +170,12 @@ static void m_loop_complete(struct fpi_ssm *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;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
vfs301_dev_t *vdev = fpi_imgdev_get_user_data(dev);
assert(ssm->cur_state == 0);
g_assert(fpi_ssm_get_cur_state(ssm) == 0);
vfs301_proto_init(dev->udev, vdev);
vfs301_proto_init(fpi_imgdev_get_usb_dev(dev), vdev);
fpi_ssm_mark_completed(ssm);
}
@@ -195,16 +183,16 @@ static void m_init_state(struct fpi_ssm *ssm)
/* Complete init sequential state machine */
static void m_init_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = ssm->priv;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct fpi_ssm *ssm_loop;
if (!ssm->error) {
if (!fpi_ssm_get_error(ssm)) {
/* 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;
ssm_loop = fpi_ssm_new(fpi_imgdev_get_dev(dev), m_loop_state, M_LOOP_NUM_STATES);
fpi_ssm_set_user_data(ssm_loop, dev);
fpi_ssm_start(ssm_loop, m_loop_complete);
}
@@ -218,8 +206,8 @@ 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;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), m_init_state, 1);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, m_init_complete);
return 0;
@@ -237,19 +225,16 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
int r;
/* Claim usb interface */
r = libusb_claim_interface(dev->udev, 0);
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r < 0) {
/* Interface not claimed, return error */
fp_err("could not claim interface 0");
fp_err("could not claim interface 0: %s", libusb_error_name(r));
return r;
}
/* Set enroll stage number */
dev->dev->nr_enroll_stages = 1;
/* Initialize private structure */
vdev = g_malloc0(sizeof(vfs301_dev_t));
dev->priv = vdev;
fpi_imgdev_set_user_data(dev, vdev);
vdev->scanline_buf = malloc(0);
vdev->scanline_count = 0;
@@ -262,12 +247,15 @@ static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
static void dev_close(struct fp_img_dev *dev)
{
vfs301_dev_t *vdev;
/* Release private structure */
free(((vfs301_dev_t*)dev->priv)->scanline_buf);
g_free(dev->priv);
vdev = fpi_imgdev_get_user_data(dev);
free(vdev->scanline_buf);
g_free(vdev);
/* Release usb interface */
libusb_release_interface(dev->udev, 0);
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
/* Notify close complete */
fpi_imgdev_close_complete(dev);

View File

@@ -28,18 +28,15 @@
* - describe some interesting structures better
*/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <glib.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 *****************************************/
@@ -51,7 +48,7 @@ static void usb_print_packet(int dir, int rv, const unsigned char *data, int len
#ifdef PRINT_VERBOSE
int i;
for (i = 0; i < min(length, 128); i++) {
for (i = 0; i < MIN(length, 128); i++) {
fprintf(stderr, "%.2X ", data[i]);
if (i % 8 == 7)
fprintf(stderr, " ");
@@ -68,7 +65,7 @@ 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));
g_assert(max_bytes <= sizeof(dev->recv_buf));
int r = libusb_bulk_transfer(
devh, endpoint,
@@ -99,7 +96,7 @@ static int usb_send(
usb_print_packet(1, r, data, length);
#endif
assert(r == 0);
g_assert(r == 0);
if (r < 0)
return r;
@@ -131,7 +128,7 @@ static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
len++;
break;
default:
assert(!"unsupported");
g_assert_not_reached();
break;
}
}
@@ -147,8 +144,8 @@ static void translate_str(const char **srcL, unsigned char *data, int *len)
while (*srcL != NULL) {
src = *srcL;
while (*src != '\0') {
assert(*src != '\0');
assert(*(src +1) != '\0');
g_assert(*src != '\0');
g_assert(*(src +1) != '\0');
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
data++;
@@ -193,7 +190,7 @@ static void vfs301_proto_generate(int type, int subtype, unsigned char *data, in
vfs301_02D0_06,
vfs301_02D0_07,
};
assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
g_assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
translate_str(dataLs[subtype - 1], data, len);
}
break;
@@ -214,10 +211,10 @@ static void vfs301_proto_generate(int type, int subtype, unsigned char *data, in
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);
g_assert(*field == 0xDE);
g_assert(*(field + 1) == 0xAD);
g_assert(*(field + 2) == 0xDE);
g_assert(*(field + 3) == 0xAD);
*field = (unsigned char)((subtype >> 8) & 0xFF);
*(field + 1) = (unsigned char)(subtype & 0xFF);
@@ -225,15 +222,15 @@ static void vfs301_proto_generate(int type, int subtype, unsigned char *data, in
*(field + 3) = *(field + 1);
break;
default:
assert(0);
g_assert(0);
break;
}
break;
case 0x06:
assert(!"Not generated");
g_assert_not_reached();
break;
default:
assert(!"Unknown message type");
g_assert_not_reached();
break;
}
}
@@ -296,7 +293,7 @@ void vfs301_extract_image(
int last_line;
int i;
assert(vfs->scanline_count >= 1);
g_assert(vfs->scanline_count >= 1);
*output_height = 1;
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
@@ -344,7 +341,7 @@ static int img_process_data(
}
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
assert(dev->scanline_buf != NULL);
g_assert(dev->scanline_buf != NULL);
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
i < no_lines;
@@ -391,7 +388,7 @@ static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
int len = dev->recv_len;
if (first_block) {
assert(len >= VFS301_FP_FRAME_SIZE);
g_assert(len >= VFS301_FP_FRAME_SIZE);
/* Skip bytes until start_sequence is found */
for (i = 0; i < VFS301_FP_FRAME_SIZE; i++, buf++, len--) {
@@ -417,14 +414,14 @@ int vfs301_proto_peek_event(
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
USB_SEND(0x17, -1);
assert(USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7) == 0);
g_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");
g_assert_not_reached();
}
}

925
libfprint/drivers/vfs5011.c Normal file
View File

@@ -0,0 +1,925 @@
/*
* Validity Sensors, Inc. VFS5011 Fingerprint Reader driver for libfprint
* Copyright (C) 2013 Arseniy Lartsev <arseniy@chalmers.se>
* AceLan Kao <acelan.kao@canonical.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 "drivers_api.h"
#include "vfs5011_proto.h"
/* =================== sync/async USB transfer sequence ==================== */
enum {
ACTION_SEND,
ACTION_RECEIVE,
};
struct usb_action {
int type;
const char *name;
int endpoint;
int size;
unsigned char *data;
int correct_reply_size;
};
#define SEND(ENDPOINT, COMMAND) \
{ \
.type = ACTION_SEND, \
.endpoint = ENDPOINT, \
.name = #COMMAND, \
.size = sizeof(COMMAND), \
.data = COMMAND \
},
#define RECV(ENDPOINT, SIZE) \
{ \
.type = ACTION_RECEIVE, \
.endpoint = ENDPOINT, \
.size = SIZE, \
.data = NULL \
},
#define RECV_CHECK(ENDPOINT, SIZE, EXPECTED) \
{ \
.type = ACTION_RECEIVE, \
.endpoint = ENDPOINT, \
.size = SIZE, \
.data = EXPECTED, \
.correct_reply_size = sizeof(EXPECTED) \
},
struct usbexchange_data {
int stepcount;
struct fp_img_dev *device;
struct usb_action *actions;
void *receive_buf;
int timeout;
};
static void start_scan(struct fp_img_dev *dev);
static void async_send_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_SEND) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
goto out;
}
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (transfer->length != transfer->actual_length) {
/* Data sended mismatch with expected, return protocol error */
fp_err("length mismatch, got %d, expected %d",
transfer->actual_length, transfer->length);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
/* success */
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void async_recv_cb(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = transfer->user_data;
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
struct usb_action *action;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* Transfer not completed, return IO error */
fp_err("transfer not completed, status = %d", transfer->status);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
goto out;
}
action = &data->actions[fpi_ssm_get_cur_state(ssm)];
if (action->type != ACTION_RECEIVE) {
fp_err("Radiation detected!");
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
goto out;
}
if (action->data != NULL) {
if (transfer->actual_length != action->correct_reply_size) {
fp_err("Got %d bytes instead of %d",
transfer->actual_length,
action->correct_reply_size);
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
if (memcmp(transfer->buffer, action->data,
action->correct_reply_size) != 0) {
fp_dbg("Wrong reply:");
fpi_imgdev_session_error(data->device, -EIO);
fpi_ssm_mark_aborted(ssm, -EIO);
goto out;
}
} else
fp_dbg("Got %d bytes out of %d", transfer->actual_length,
transfer->length);
fpi_ssm_next_state(ssm);
out:
libusb_free_transfer(transfer);
}
static void usbexchange_loop(struct fpi_ssm *ssm)
{
struct usbexchange_data *data = fpi_ssm_get_user_data(ssm);
if (fpi_ssm_get_cur_state(ssm) >= data->stepcount) {
fp_err("Bug detected: state %d out of range, only %d steps",
fpi_ssm_get_cur_state(ssm), data->stepcount);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
return;
}
struct usb_action *action = &data->actions[fpi_ssm_get_cur_state(ssm)];
struct libusb_transfer *transfer;
int ret = -EINVAL;
switch (action->type) {
case ACTION_SEND:
fp_dbg("Sending %s", action->name);
transfer = libusb_alloc_transfer(0);
if (transfer == NULL) {
fp_err("Failed to allocate transfer");
fpi_imgdev_session_error(data->device, -ENOMEM);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(data->device),
action->endpoint, action->data,
action->size, async_send_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
case ACTION_RECEIVE:
fp_dbg("Receiving %d bytes", action->size);
transfer = libusb_alloc_transfer(0);
if (transfer == NULL) {
fp_err("Failed to allocate transfer");
fpi_imgdev_session_error(data->device, -ENOMEM);
fpi_ssm_mark_aborted(ssm, -ENOMEM);
return;
}
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(data->device),
action->endpoint, data->receive_buf,
action->size, async_recv_cb, ssm,
data->timeout);
ret = libusb_submit_transfer(transfer);
break;
default:
fp_err("Bug detected: invalid action %d", action->type);
fpi_imgdev_session_error(data->device, -EINVAL);
fpi_ssm_mark_aborted(ssm, -EINVAL);
return;
}
if (ret != 0) {
fp_err("USB transfer error: %s", strerror(ret));
fpi_imgdev_session_error(data->device, ret);
fpi_ssm_mark_aborted(ssm, ret);
}
}
static void usb_exchange_async(struct fpi_ssm *ssm,
struct usbexchange_data *data)
{
struct fpi_ssm *subsm = fpi_ssm_new(fpi_imgdev_get_dev(data->device),
usbexchange_loop,
data->stepcount);
fpi_ssm_set_user_data(subsm, data);
fpi_ssm_start_subsm(ssm, subsm);
}
/* ====================== utils ======================= */
/* Calculade squared standand deviation of sum of two lines */
static int vfs5011_get_deviation2(struct fpi_line_asmbl_ctx *ctx, GSList *row1, GSList *row2)
{
unsigned char *buf1, *buf2;
int res = 0, mean = 0, i;
const int size = 64;
buf1 = row1->data + 56;
buf2 = row2->data + 168;
for (i = 0; i < size; i++)
mean += (int)buf1[i] + (int)buf2[i];
mean /= size;
for (i = 0; i < size; i++) {
int dev = (int)buf1[i] + (int)buf2[i] - mean;
res += dev*dev;
}
return res / size;
}
static unsigned char vfs5011_get_pixel(struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
unsigned char *data = row->data + 8;
return data[x];
}
/* ====================== main stuff ======================= */
enum {
CAPTURE_LINES = 256,
MAXLINES = 2000,
MAX_CAPTURE_LINES = 100000,
};
static struct fpi_line_asmbl_ctx assembling_ctx = {
.line_width = VFS5011_IMAGE_WIDTH,
.max_height = MAXLINES,
.resolution = 10,
.median_filter_size = 25,
.max_search_offset = 30,
.get_deviation = vfs5011_get_deviation2,
.get_pixel = vfs5011_get_pixel,
};
struct vfs5011_data {
unsigned char *total_buffer;
unsigned char *capture_buffer;
unsigned char *row_buffer;
unsigned char *lastline;
GSList *rows;
int lines_captured, lines_recorded, empty_lines;
int max_lines_captured, max_lines_recorded;
int lines_total, lines_total_allocated;
gboolean loop_running;
gboolean deactivating;
struct usbexchange_data init_sequence;
struct libusb_transfer *flying_transfer;
};
enum {
DEV_ACTIVATE_REQUEST_FPRINT,
DEV_ACTIVATE_INIT_COMPLETE,
DEV_ACTIVATE_READ_DATA,
DEV_ACTIVATE_DATA_COMPLETE,
DEV_ACTIVATE_PREPARE_NEXT_CAPTURE,
DEV_ACTIVATE_NUM_STATES
};
enum {
DEV_OPEN_START,
DEV_OPEN_NUM_STATES
};
static void capture_init(struct vfs5011_data *data, int max_captured,
int max_recorded)
{
fp_dbg("capture_init");
data->lastline = NULL;
data->lines_captured = 0;
data->lines_recorded = 0;
data->empty_lines = 0;
data->lines_total = 0;
data->lines_total_allocated = 0;
data->total_buffer = NULL;
data->max_lines_captured = max_captured;
data->max_lines_recorded = max_recorded;
}
static int process_chunk(struct vfs5011_data *data, int transferred)
{
enum {
DEVIATION_THRESHOLD = 15*15,
DIFFERENCE_THRESHOLD = 600,
STOP_CHECK_LINES = 50
};
fp_dbg("process_chunk: got %d bytes", transferred);
int lines_captured = transferred/VFS5011_LINE_SIZE;
int i;
for (i = 0; i < lines_captured; i++) {
unsigned char *linebuf = data->capture_buffer
+ i * VFS5011_LINE_SIZE;
if (fpi_std_sq_dev(linebuf + 8, VFS5011_IMAGE_WIDTH)
< DEVIATION_THRESHOLD) {
if (data->lines_captured == 0)
continue;
else
data->empty_lines++;
} else
data->empty_lines = 0;
if (data->empty_lines >= STOP_CHECK_LINES) {
fp_dbg("process_chunk: got %d empty lines, finishing",
data->empty_lines);
return 1;
}
data->lines_captured++;
if (data->lines_captured > data->max_lines_captured) {
fp_dbg("process_chunk: captured %d lines, finishing",
data->lines_captured);
return 1;
}
if ((data->lastline == NULL)
|| (fpi_mean_sq_diff_norm(
data->lastline + 8,
linebuf + 8,
VFS5011_IMAGE_WIDTH) >= DIFFERENCE_THRESHOLD)) {
data->lastline = g_malloc(VFS5011_LINE_SIZE);
data->rows = g_slist_prepend(data->rows, data->lastline);
g_memmove(data->lastline, linebuf, VFS5011_LINE_SIZE);
data->lines_recorded++;
if (data->lines_recorded >= data->max_lines_recorded) {
fp_dbg("process_chunk: recorded %d lines, finishing",
data->lines_recorded);
return 1;
}
}
}
return 0;
}
void submit_image(struct fpi_ssm *ssm, struct vfs5011_data *data)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct fp_img *img;
if (data->lines_recorded == 0) {
/* == FP_ENROLL_RETRY_TOO_SHORT */
fpi_imgdev_session_error(dev, FP_VERIFY_RETRY_TOO_SHORT);
return;
}
g_assert (data->rows != NULL);
data->rows = g_slist_reverse(data->rows);
img = fpi_assemble_lines(&assembling_ctx, data->rows, data->lines_recorded);
g_slist_free_full(data->rows, g_free);
data->rows = NULL;
fp_dbg("Image captured, commiting");
fpi_imgdev_image_captured(dev, img);
}
static void chunk_capture_callback(struct libusb_transfer *transfer)
{
struct fpi_ssm *ssm = (struct fpi_ssm *)transfer->user_data;
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) ||
(transfer->status == LIBUSB_TRANSFER_TIMED_OUT)) {
if (transfer->actual_length > 0)
fpi_imgdev_report_finger_status(dev, TRUE);
if (process_chunk(data, transfer->actual_length))
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_DATA_COMPLETE);
else
fpi_ssm_jump_to_state(ssm, DEV_ACTIVATE_READ_DATA);
} else {
if (!data->deactivating) {
fp_err("Failed to capture data");
fpi_ssm_mark_aborted(ssm, -1);
} else {
fpi_ssm_mark_completed(ssm);
}
}
libusb_free_transfer(transfer);
data->flying_transfer = NULL;
}
static int capture_chunk_async(struct vfs5011_data *data,
libusb_device_handle *handle, int nline,
int timeout, struct fpi_ssm *ssm)
{
fp_dbg("capture_chunk_async: capture %d lines, already have %d",
nline, data->lines_recorded);
enum {
DEVIATION_THRESHOLD = 15*15,
DIFFERENCE_THRESHOLD = 600,
STOP_CHECK_LINES = 50
};
data->flying_transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(data->flying_transfer, handle, VFS5011_IN_ENDPOINT_DATA,
data->capture_buffer,
nline * VFS5011_LINE_SIZE,
chunk_capture_callback, ssm, timeout);
return libusb_submit_transfer(data->flying_transfer);
}
static void async_sleep_cb(void *data)
{
struct fpi_ssm *ssm = data;
fpi_ssm_next_state(ssm);
}
/*
* Device initialization. Windows driver only does it when the device is
* plugged in, but it doesn't harm to do this every time before scanning the
* image.
*/
struct usb_action vfs5011_initialization[] = {
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_19)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* B5C457F9 */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_00)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFF */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64) /* 0000FFFFFFFFFF */
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_02)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_03)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_04)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 256)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_05)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_01)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_06)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 17216)
RECV(VFS5011_IN_ENDPOINT_DATA, 32)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_07)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 45056)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_08)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 16896)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_09)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 4928)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_10)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 5632)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_11)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 5632)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_12)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 3328)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_13)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_03)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_14)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_02)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_27)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_15)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_16)
RECV(VFS5011_IN_ENDPOINT_CTRL, 2368)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_17)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_init_18)
/* 0000 */
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8) //00D3054000
*/
};
/* Initiate recording the image */
struct usb_action vfs5011_initiate_capture[] = {
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_04)
RECV(VFS5011_IN_ENDPOINT_DATA, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 84032)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_00)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_cmd_1A)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_01)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_02)
RECV(VFS5011_IN_ENDPOINT_CTRL, 2368)
RECV(VFS5011_IN_ENDPOINT_CTRL, 64)
RECV(VFS5011_IN_ENDPOINT_DATA, 4800)
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_03)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 64, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
*/
SEND(VFS5011_OUT_ENDPOINT, vfs5011_prepare_04)
RECV_CHECK(VFS5011_IN_ENDPOINT_CTRL, 2368, VFS5011_NORMAL_CONTROL_REPLY)
/*
* Windows driver does this and it works
* But in this driver this call never returns...
* RECV(VFS5011_IN_ENDPOINT_CTRL2, 8);
*/
};
/* ====================== lifprint interface ======================= */
static void activate_loop(struct fpi_ssm *ssm)
{
enum {READ_TIMEOUT = 0};
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
int r;
struct fpi_timeout *timeout;
data = fpi_imgdev_get_user_data(dev);
fp_dbg("main_loop: state %d", fpi_ssm_get_cur_state(ssm));
if (data->deactivating) {
fp_dbg("deactivating, marking completed");
fpi_ssm_mark_completed(ssm);
return;
}
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_ACTIVATE_REQUEST_FPRINT:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = 1000;
usb_exchange_async(ssm, &data->init_sequence);
break;
case DEV_ACTIVATE_INIT_COMPLETE:
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
capture_init(data, MAX_CAPTURE_LINES, MAXLINES);
fpi_imgdev_activate_complete(dev, 0);
fpi_ssm_next_state(ssm);
break;
case DEV_ACTIVATE_READ_DATA:
r = capture_chunk_async(data, fpi_imgdev_get_usb_dev(dev), CAPTURE_LINES,
READ_TIMEOUT, ssm);
if (r != 0) {
fp_err("Failed to capture data");
fpi_imgdev_session_error(dev, r);
fpi_ssm_mark_aborted(ssm, r);
}
break;
case DEV_ACTIVATE_DATA_COMPLETE:
timeout = fpi_timeout_add(1, async_sleep_cb, ssm);
if (timeout == NULL) {
/* Failed to add timeout */
fp_err("failed to add timeout");
fpi_imgdev_session_error(dev, -1);
fpi_ssm_mark_aborted(ssm, -1);
}
break;
case DEV_ACTIVATE_PREPARE_NEXT_CAPTURE:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initiate_capture);
data->init_sequence.actions = vfs5011_initiate_capture;
data->init_sequence.device = dev;
if (data->init_sequence.receive_buf == NULL)
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
}
}
static void activate_loop_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
int r = fpi_ssm_get_error(ssm);
data = fpi_imgdev_get_user_data(dev);
fp_dbg("finishing");
if (data->init_sequence.receive_buf != NULL)
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
if (!data->deactivating && !r) {
submit_image(ssm, data);
fpi_imgdev_report_finger_status(dev, FALSE);
}
fpi_ssm_free(ssm);
data->loop_running = FALSE;
if (data->deactivating) {
fpi_imgdev_deactivate_complete(dev);
} else if (r) {
fpi_imgdev_session_error(dev, r);
} else {
start_scan(dev);
}
}
static void open_loop(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
switch (fpi_ssm_get_cur_state(ssm)) {
case DEV_OPEN_START:
data->init_sequence.stepcount =
G_N_ELEMENTS(vfs5011_initialization);
data->init_sequence.actions = vfs5011_initialization;
data->init_sequence.device = dev;
data->init_sequence.receive_buf =
g_malloc0(VFS5011_RECEIVE_BUF_SIZE);
data->init_sequence.timeout = VFS5011_DEFAULT_WAIT_TIMEOUT;
usb_exchange_async(ssm, &data->init_sequence);
break;
};
}
static void open_loop_complete(struct fpi_ssm *ssm)
{
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
g_free(data->init_sequence.receive_buf);
data->init_sequence.receive_buf = NULL;
fpi_imgdev_open_complete(dev, 0);
fpi_ssm_free(ssm);
}
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
{
struct vfs5011_data *data;
int r;
data = (struct vfs5011_data *)g_malloc0(sizeof(*data));
data->capture_buffer =
(unsigned char *)g_malloc0(CAPTURE_LINES * VFS5011_LINE_SIZE);
fpi_imgdev_set_user_data(dev, data);
r = libusb_reset_device(fpi_imgdev_get_usb_dev(dev));
if (r != 0) {
fp_err("Failed to reset the device");
return r;
}
r = libusb_claim_interface(fpi_imgdev_get_usb_dev(dev), 0);
if (r != 0) {
fp_err("Failed to claim interface: %s", libusb_error_name(r));
return r;
}
struct fpi_ssm *ssm;
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), open_loop, DEV_OPEN_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fpi_ssm_start(ssm, open_loop_complete);
return 0;
}
static void dev_close(struct fp_img_dev *dev)
{
libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0);
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
if (data != NULL) {
g_free(data->capture_buffer);
g_slist_free_full(data->rows, g_free);
g_free(data);
}
fpi_imgdev_close_complete(dev);
}
static void start_scan(struct fp_img_dev *dev)
{
struct vfs5011_data *data;
struct fpi_ssm *ssm;
data = fpi_imgdev_get_user_data(dev);
data->loop_running = TRUE;
fp_dbg("creating ssm");
ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_loop, DEV_ACTIVATE_NUM_STATES);
fpi_ssm_set_user_data(ssm, dev);
fp_dbg("starting ssm");
fpi_ssm_start(ssm, activate_loop_complete);
fp_dbg("ssm done, getting out");
}
static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state)
{
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
fp_dbg("device initialized");
data->deactivating = FALSE;
start_scan(dev);
return 0;
}
static void dev_deactivate(struct fp_img_dev *dev)
{
int r;
struct vfs5011_data *data;
data = fpi_imgdev_get_user_data(dev);
if (data->loop_running) {
data->deactivating = TRUE;
if (data->flying_transfer) {
r = libusb_cancel_transfer(data->flying_transfer);
if (r < 0)
fp_dbg("cancel failed error %d", r);
}
} else
fpi_imgdev_deactivate_complete(dev);
}
static const struct usb_id id_table[] = {
{ .vendor = 0x138a, .product = 0x0010 /* Validity device from some Toshiba laptops */ },
{ .vendor = 0x138a, .product = 0x0011 /* vfs5011 */ },
{ .vendor = 0x138a, .product = 0x0017 /* Validity device from Lenovo T440 laptops */ },
{ .vendor = 0x138a, .product = 0x0018 /* one more Validity device */ },
{ 0, 0, 0, },
};
struct fp_img_driver vfs5011_driver = {
.driver = {
.id = VFS5011_ID,
.name = "vfs5011",
.full_name = "Validity VFS5011",
.id_table = id_table,
.scan_type = FP_SCAN_TYPE_SWIPE,
},
.flags = 0,
.img_width = VFS5011_IMAGE_WIDTH,
.img_height = -1,
.bz3_threshold = 20,
.open = dev_open,
.close = dev_close,
.activate = dev_activate,
.deactivate = dev_deactivate,
};

File diff suppressed because it is too large Load Diff

304
libfprint/drivers_api.h Normal file
View File

@@ -0,0 +1,304 @@
/*
* Driver API definitions
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright (C) 2018 Bastien Nocera <hadess@hadess.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
*/
#ifndef __DRIVERS_API_H__
#define __DRIVERS_API_H__
#include <config.h>
#ifdef FP_COMPONENT
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "libfprint-"FP_COMPONENT
#endif
#include <stdint.h>
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include "fprint.h"
#include "assembling.h"
#include "drivers/driver_ids.h"
#define fp_dbg g_debug
#define fp_info g_debug
#define fp_warn g_warning
#define fp_err g_error
#define BUG_ON(condition) g_assert(!(condition))
#define BUG() g_assert_not_reached()
enum fp_dev_state {
DEV_STATE_INITIAL = 0,
DEV_STATE_ERROR,
DEV_STATE_INITIALIZING,
DEV_STATE_INITIALIZED,
DEV_STATE_DEINITIALIZING,
DEV_STATE_DEINITIALIZED,
DEV_STATE_ENROLL_STARTING,
DEV_STATE_ENROLLING,
DEV_STATE_ENROLL_STOPPING,
DEV_STATE_VERIFY_STARTING,
DEV_STATE_VERIFYING,
DEV_STATE_VERIFY_DONE,
DEV_STATE_VERIFY_STOPPING,
DEV_STATE_IDENTIFY_STARTING,
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_dev;
libusb_device_handle *fpi_dev_get_usb_dev(struct fp_dev *dev);
void *fpi_dev_get_user_data (struct fp_dev *dev);
void fpi_dev_set_user_data (struct fp_dev *dev, void *user_data);
int fpi_dev_get_nr_enroll_stages(struct fp_dev *dev);
void fpi_dev_set_nr_enroll_stages(struct fp_dev *dev, int nr_enroll_stages);
struct fp_print_data *fpi_dev_get_verify_data(struct fp_dev *dev);
enum fp_dev_state fpi_dev_get_dev_state(struct fp_dev *dev);
enum fp_imgdev_state {
IMGDEV_STATE_INACTIVE,
IMGDEV_STATE_AWAIT_FINGER_ON,
IMGDEV_STATE_CAPTURE,
IMGDEV_STATE_AWAIT_FINGER_OFF,
};
enum fp_imgdev_action {
IMG_ACTION_NONE = 0,
IMG_ACTION_ENROLL,
IMG_ACTION_VERIFY,
IMG_ACTION_IDENTIFY,
IMG_ACTION_CAPTURE,
};
enum fp_imgdev_enroll_state {
IMG_ACQUIRE_STATE_NONE = 0,
IMG_ACQUIRE_STATE_ACTIVATING,
IMG_ACQUIRE_STATE_AWAIT_FINGER_ON,
IMG_ACQUIRE_STATE_AWAIT_IMAGE,
IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF,
IMG_ACQUIRE_STATE_DONE,
IMG_ACQUIRE_STATE_DEACTIVATING,
};
enum fp_imgdev_verify_state {
IMG_VERIFY_STATE_NONE = 0,
IMG_VERIFY_STATE_ACTIVATING
};
struct fp_img_dev;
libusb_device_handle *fpi_imgdev_get_usb_dev(struct fp_img_dev *dev);
void fpi_imgdev_set_user_data(struct fp_img_dev *imgdev,
void *user_data);
void *fpi_imgdev_get_user_data(struct fp_img_dev *imgdev);
struct fp_dev *fpi_imgdev_get_dev(struct fp_img_dev *imgdev);
enum fp_imgdev_enroll_state fpi_imgdev_get_action_state(struct fp_img_dev *imgdev);
enum fp_imgdev_action fpi_imgdev_get_action(struct fp_img_dev *imgdev);
int fpi_imgdev_get_action_result(struct fp_img_dev *imgdev);
void fpi_imgdev_set_action_result(struct fp_img_dev *imgdev, int action_result);
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
struct usb_id {
uint16_t vendor;
uint16_t product;
unsigned long driver_data;
};
enum fp_driver_type {
DRIVER_PRIMITIVE = 0,
DRIVER_IMAGING = 1,
};
struct fp_driver {
const uint16_t id;
const char *name;
const char *full_name;
const struct usb_id * const id_table;
enum fp_driver_type type;
enum fp_scan_type scan_type;
void *priv;
/* Device operations */
int (*discover)(struct libusb_device_descriptor *dsc, uint32_t *devtype);
int (*open)(struct fp_dev *dev, unsigned long driver_data);
void (*close)(struct fp_dev *dev);
int (*enroll_start)(struct fp_dev *dev);
int (*enroll_stop)(struct fp_dev *dev);
int (*verify_start)(struct fp_dev *dev);
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);
};
/* flags for fp_img_driver.flags */
#define FP_IMGDRV_SUPPORTS_UNCONDITIONAL_CAPTURE (1 << 0)
struct fp_img_driver {
struct fp_driver driver;
uint16_t flags;
int img_width;
int img_height;
int bz3_threshold;
/* Device operations */
int (*open)(struct fp_img_dev *dev, unsigned long driver_data);
void (*close)(struct fp_img_dev *dev);
int (*activate)(struct fp_img_dev *dev, enum fp_imgdev_state state);
int (*change_state)(struct fp_img_dev *dev, enum fp_imgdev_state state);
void (*deactivate)(struct fp_img_dev *dev);
};
enum fp_print_data_type {
PRINT_DATA_RAW = 0, /* memset-imposed default */
PRINT_DATA_NBIS_MINUTIAE,
};
struct fp_print_data_item {
size_t length;
unsigned char data[0];
};
struct fp_print_data {
uint16_t driver_id;
uint32_t devtype;
enum fp_print_data_type type;
GSList *prints;
};
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);
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_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
#define FP_IMG_PARTIAL (1<<4)
#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
| FP_IMG_COLORS_INVERTED)
struct fp_img {
int width;
int height;
size_t length;
uint16_t flags;
struct fp_minutiae *minutiae;
unsigned char *binarized;
unsigned char data[0];
};
struct fp_img *fpi_img_new(size_t length);
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *dev);
struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize);
struct fp_img *fpi_im_resize(struct fp_img *img, unsigned int w_factor, unsigned int h_factor);
/* polling and timeouts */
typedef void (*fpi_timeout_fn)(void *data);
struct fpi_timeout;
struct fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout_fn callback,
void *data);
void fpi_timeout_cancel(struct fpi_timeout *timeout);
/* async drv <--> lib comms */
struct fpi_ssm;
typedef void (*ssm_completed_fn)(struct fpi_ssm *ssm);
typedef void (*ssm_handler_fn)(struct fpi_ssm *ssm);
/* sequential state machine: state machine that iterates sequentially over
* a predefined series of states. can be aborted by either completion or
* abortion error conditions. */
/* for library and drivers */
struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states);
void fpi_ssm_free(struct fpi_ssm *machine);
void fpi_ssm_start(struct fpi_ssm *machine, ssm_completed_fn callback);
void fpi_ssm_start_subsm(struct fpi_ssm *parent, struct fpi_ssm *child);
/* for drivers */
void fpi_ssm_next_state(struct fpi_ssm *machine);
void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state);
void fpi_ssm_mark_completed(struct fpi_ssm *machine);
void fpi_ssm_mark_aborted(struct fpi_ssm *machine, int error);
struct fp_dev *fpi_ssm_get_dev(struct fpi_ssm *machine);
void fpi_ssm_set_user_data(struct fpi_ssm *machine,
void *user_data);
void *fpi_ssm_get_user_data(struct fpi_ssm *machine);
int fpi_ssm_get_error(struct fpi_ssm *machine);
int fpi_ssm_get_cur_state(struct fpi_ssm *machine);
void fpi_drvcb_open_complete(struct fp_dev *dev, int status);
void fpi_drvcb_close_complete(struct fp_dev *dev);
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status);
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
struct fp_print_data *data, struct fp_img *img);
void fpi_drvcb_enroll_stopped(struct fp_dev *dev);
void fpi_drvcb_verify_started(struct fp_dev *dev, int status);
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
struct fp_img *img);
void fpi_drvcb_verify_stopped(struct fp_dev *dev);
void fpi_drvcb_identify_started(struct fp_dev *dev, int status);
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);
void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
gboolean present);
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result);
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
/* utils */
int fpi_std_sq_dev(const unsigned char *buf, int size);
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size);
#endif

View File

@@ -19,11 +19,11 @@
#define FP_COMPONENT "drv"
#include "fp_internal.h"
#include <config.h>
#include <errno.h>
#include "fp_internal.h"
/* SSM: sequential state machine
* Asynchronous driver design encourages some kind of state machine behind it.
* In most cases, the state machine is entirely linear - you only go to the
@@ -83,6 +83,25 @@ struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
return machine;
}
struct fp_dev *
fpi_ssm_get_dev(struct fpi_ssm *machine)
{
return machine->dev;
}
void
fpi_ssm_set_user_data(struct fpi_ssm *machine,
void *user_data)
{
machine->priv = user_data;
}
void *
fpi_ssm_get_user_data(struct fpi_ssm *machine)
{
return machine->priv;
}
/* Free a ssm */
void fpi_ssm_free(struct fpi_ssm *machine)
{
@@ -169,3 +188,12 @@ void fpi_ssm_jump_to_state(struct fpi_ssm *machine, int state)
__ssm_call_handler(machine);
}
int fpi_ssm_get_cur_state(struct fpi_ssm *machine)
{
return machine->cur_state;
}
int fpi_ssm_get_error(struct fpi_ssm *machine)
{
return machine->error;
}

1
libfprint/empty_file Normal file
View File

@@ -0,0 +1 @@

View File

@@ -21,57 +21,31 @@
#define __FPRINT_INTERNAL_H__
#include <config.h>
#include <stdint.h>
#ifdef FP_COMPONENT
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "libfprint-"FP_COMPONENT
#endif
#include <stdint.h>
#include <errno.h>
#include <glib.h>
#include <libusb.h>
#include <fprint.h>
#define array_n_elements(array) (sizeof(array) / sizeof(array[0]))
#include "fprint.h"
#include "drivers/driver_ids.h"
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
enum fpi_log_level {
FPRINT_LOG_LEVEL_DEBUG,
FPRINT_LOG_LEVEL_INFO,
FPRINT_LOG_LEVEL_WARNING,
FPRINT_LOG_LEVEL_ERROR,
};
#define fp_dbg g_debug
#define fp_info g_debug
#define fp_warn g_warning
#define fp_err g_error
void fpi_log(enum fpi_log_level, const char *component, const char *function,
const char *format, ...);
#ifndef FP_COMPONENT
#define FP_COMPONENT NULL
#endif
#ifdef ENABLE_LOGGING
#define _fpi_log(level, fmt...) fpi_log(level, FP_COMPONENT, __FUNCTION__, fmt)
#else
#define _fpi_log(level, fmt...)
#endif
#ifdef ENABLE_DEBUG_LOGGING
#define fp_dbg(fmt...) _fpi_log(FPRINT_LOG_LEVEL_DEBUG, fmt)
#else
#define fp_dbg(fmt...)
#endif
#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) \
if ((condition)) fp_err("BUG at %s:%d", __FILE__, __LINE__)
#else
#define BUG_ON(condition)
#endif
#define BUG() BUG_ON(1)
#define BUG_ON(condition) g_assert(!(condition))
#define BUG() g_assert_not_reached()
enum fp_dev_state {
DEV_STATE_INITIAL = 0,
@@ -91,6 +65,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);
@@ -108,27 +86,31 @@ 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? */
fp_dev_open_cb open_cb;
void *open_cb_data;
fp_dev_close_cb close_cb;
fp_operation_stop_cb close_cb;
void *close_cb_data;
fp_enroll_stage_cb enroll_stage_cb;
void *enroll_stage_cb_data;
fp_enroll_stop_cb enroll_stop_cb;
fp_operation_stop_cb enroll_stop_cb;
void *enroll_stop_cb_data;
fp_verify_cb verify_cb;
fp_img_operation_cb verify_cb;
void *verify_cb_data;
fp_verify_stop_cb verify_stop_cb;
fp_operation_stop_cb verify_stop_cb;
void *verify_stop_cb_data;
fp_identify_cb identify_cb;
void *identify_cb_data;
fp_identify_stop_cb identify_stop_cb;
fp_operation_stop_cb identify_stop_cb;
void *identify_stop_cb_data;
fp_img_operation_cb capture_cb;
void *capture_cb_data;
fp_operation_stop_cb capture_stop_cb;
void *capture_stop_cb_data;
/* FIXME: better place to put this? */
struct fp_print_data **identify_gallery;
@@ -146,6 +128,7 @@ enum fp_imgdev_action {
IMG_ACTION_ENROLL,
IMG_ACTION_VERIFY,
IMG_ACTION_IDENTIFY,
IMG_ACTION_CAPTURE,
};
enum fp_imgdev_enroll_state {
@@ -170,7 +153,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? */
@@ -179,8 +164,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);
@@ -215,6 +198,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);
@@ -237,51 +222,7 @@ struct fp_img_driver {
void (*deactivate)(struct fp_img_dev *dev);
};
#ifdef ENABLE_UPEKTS
extern struct fp_driver upekts_driver;
#endif
#ifdef ENABLE_UPEKE2
extern struct fp_driver upeke2_driver;
#endif
#ifdef ENABLE_UPEKTC
extern struct fp_img_driver upektc_driver;
#endif
#ifdef ENABLE_UPEKSONLY
extern struct fp_img_driver upeksonly_driver;
#endif
#ifdef ENABLE_URU4000
extern struct fp_img_driver uru4000_driver;
#endif
#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_AES4000
extern struct fp_img_driver aes4000_driver;
#endif
#ifdef ENABLE_FDU2000
extern struct fp_img_driver fdu2000_driver;
#endif
#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
#include "drivers_definitions.h"
extern libusb_context *fpi_usb_ctx;
extern GSList *opened_devices;
@@ -310,15 +251,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;
@@ -326,12 +271,33 @@ 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);
struct fp_minutia {
int x;
int y;
int ex;
int ey;
int direction;
double reliability;
int type;
int appearing;
int feature_id;
int *nbrs;
int *ridge_counts;
int num_nbrs;
};
struct fp_minutiae {
int alloc;
int num;
@@ -343,6 +309,7 @@ struct fp_minutiae {
#define FP_IMG_H_FLIPPED (1<<1)
#define FP_IMG_COLORS_INVERTED (1<<2)
#define FP_IMG_BINARIZED_FORM (1<<3)
#define FP_IMG_PARTIAL (1<<4)
#define FP_IMG_STANDARDIZATION_FLAGS (FP_IMG_V_FLIPPED | FP_IMG_H_FLIPPED \
| FP_IMG_COLORS_INVERTED)
@@ -358,10 +325,8 @@ struct fp_img {
};
struct fp_img *fpi_img_new(size_t length);
struct fp_img *fpi_img_new_for_imgdev(struct fp_img_dev *dev);
struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize);
gboolean fpi_img_is_sane(struct fp_img *img);
int fpi_img_detect_minutiae(struct fp_img *img);
int fpi_img_to_print_data(struct fp_img_dev *imgdev, struct fp_img *img,
struct fp_print_data **ret);
int fpi_img_compare_print_data(struct fp_print_data *enrolled_print,
@@ -377,11 +342,6 @@ void fpi_poll_exit(void);
typedef void (*fpi_timeout_fn)(void *data);
struct fpi_timeout;
struct fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout_fn callback,
void *data);
void fpi_timeout_cancel(struct fpi_timeout *timeout);
/* async drv <--> lib comms */
struct fpi_ssm;
@@ -409,8 +369,6 @@ struct fpi_ssm *fpi_ssm_new(struct fp_dev *dev, ssm_handler_fn handler,
int nr_states);
void fpi_ssm_free(struct fpi_ssm *machine);
void fpi_ssm_start(struct fpi_ssm *machine, ssm_completed_fn callback);
void fpi_ssm_start_subsm(struct fpi_ssm *parent, struct fpi_ssm *child);
int fpi_ssm_has_completed(struct fpi_ssm *machine);
/* for drivers */
void fpi_ssm_next_state(struct fpi_ssm *machine);
@@ -436,15 +394,10 @@ 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);
/* 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);
void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status);
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev);
void fpi_imgdev_report_finger_status(struct fp_img_dev *imgdev,
gboolean present);
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img);
void fpi_imgdev_session_error(struct fp_img_dev *imgdev, int error);
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);
#endif

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
* 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 <locale.h>
#include "fp_internal.h"
GHashTable *printed = NULL;
static GList *insert_driver (GList *list,
struct fp_driver *driver)
{
int i;
for (i = 0; driver->id_table[i].vendor != 0; i++) {
char *key;
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));
list = g_list_prepend (list, g_strdup_printf ("%s | %s\n", key, driver->full_name));
}
return list;
}
int main (int argc, char **argv)
{
struct fp_driver **driver_list;
guint i;
GList *list, *l;
setlocale (LC_ALL, "");
driver_list = fprint_get_drivers ();
printed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_print ("%% lifprint — Supported Devices\n");
g_print ("%% Bastien Nocera, Daniel Drake\n");
g_print ("%% 2018\n");
g_print ("\n");
g_print ("# Supported Devices\n");
g_print ("\n");
g_print ("This is a list of supported devices in libfprint's development version. Those drivers might not all be available in the stable, released version. If in doubt, contact your distribution or systems integrator for details.\n");
g_print ("\n");
g_print ("## USB devices\n");
g_print ("\n");
g_print ("USB ID | Driver\n");
g_print ("------------ | ------------\n");
list = NULL;
for (i = 0; driver_list[i] != NULL; i++)
list = insert_driver (list, driver_list[i]);
list = g_list_sort (list, (GCompareFunc) g_strcmp0);
for (l = list; l != NULL; l = l->next)
g_print ("%s", (char *) l->data);
g_list_free_full (list, g_free);
g_hash_table_destroy (printed);
return 0;
}

View File

@@ -24,7 +24,15 @@
#include "fp_internal.h"
static const struct usb_id whitelist_id_table[] = {
{ .vendor = 0x08ff, .product = 0x2810 },
/* Unsupported (for now) Elantech finger print readers */
{ .vendor = 0x04f3, .product = 0x0c03 },
{ .vendor = 0x04f3, .product = 0x0c16 },
{ .vendor = 0x04f3, .product = 0x0c26 },
/* Unsupported (for now) Validity Sensors finger print readers */
{ .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */
{ .vendor = 0x138a, .product = 0x0091 },
{ .vendor = 0x138a, .product = 0x0094 },
{ .vendor = 0x138a, .product = 0x0097 }, /* Found on e.g. Lenovo T470s */
{ 0, 0, 0, },
};
@@ -74,7 +82,8 @@ static void print_driver (struct fp_driver *driver)
if (num_printed == 0)
printf ("# %s\n", driver->full_name);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ATTRS{dev}==\"*\", ATTR{power/control}=\"auto\"\n", driver->id_table[i].vendor, driver->id_table[i].product);
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);
printf ("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", ENV{LIBFPRINT_DRIVER}=\"%s\"\n", driver->id_table[i].vendor, driver->id_table[i].product, driver->full_name);
num_printed++;
}

View File

@@ -27,42 +27,100 @@ extern "C" {
#include <stdint.h>
#include <sys/time.h>
/* structs that applications are not allowed to peek into */
#define LIBFPRINT_DEPRECATED __attribute__((__deprecated__))
/**
* fp_dscv_dev:
*
* #fp_dscv_dev is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_dscv_dev;
/**
* fp_dscv_print:
*
* #fp_dscv_print is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_dscv_print;
/**
* fp_dev:
*
* #fp_dev is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_dev;
/**
* fp_driver:
*
* #fp_driver is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_driver;
/**
* fp_print_data:
*
* #fp_print_data is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_print_data;
/**
* fp_img:
*
* #fp_img is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_img;
/* misc/general stuff */
/** \ingroup print_data
/**
* fp_finger:
* @LEFT_THUMB: Left thumb
* @LEFT_INDEX: Left index finger
* @LEFT_MIDDLE: Left middle finger
* @LEFT_RING: Left ring finger
* @LEFT_LITTLE: Left little finger
* @RIGHT_THUMB: Right thumb
* @RIGHT_INDEX: Right index finger
* @RIGHT_MIDDLE: Right middle finger
* @RIGHT_RING: Right ring finger
* @RIGHT_LITTLE: Right little finger
*
* Numeric codes used to refer to fingers (and thumbs) of a human. These are
* purposely not available as strings, to avoid getting the library tangled up
* in localization efforts.
*/
enum fp_finger {
LEFT_THUMB = 1, /** thumb (left hand) */
LEFT_INDEX, /** index finger (left hand) */
LEFT_MIDDLE, /** middle finger (left hand) */
LEFT_RING, /** ring finger (left hand) */
LEFT_LITTLE, /** little finger (left hand) */
RIGHT_THUMB, /** thumb (right hand) */
RIGHT_INDEX, /** index finger (right hand) */
RIGHT_MIDDLE, /** middle finger (right hand) */
RIGHT_RING, /** ring finger (right hand) */
RIGHT_LITTLE, /** little finger (right hand) */
LEFT_THUMB = 1,
LEFT_INDEX,
LEFT_MIDDLE,
LEFT_RING,
LEFT_LITTLE,
RIGHT_THUMB,
RIGHT_INDEX,
RIGHT_MIDDLE,
RIGHT_RING,
RIGHT_LITTLE,
};
/** \ingroup dev
/**
* fp_scan_type:
* @FP_SCAN_TYPE_PRESS: the reader has a surface area that covers the whole finger
* @FP_SCAN_TYPE_SWIPE: the reader requires swiping the finger on a smaller area
*
* Numeric codes used to refer to the scan type of the device. Devices require
* either swiping or pressing the finger on the device. This is useful for
* front-ends.
*/
enum fp_scan_type {
FP_SCAN_TYPE_PRESS = 0, /** press */
FP_SCAN_TYPE_SWIPE, /** swipe */
FP_SCAN_TYPE_PRESS = 0,
FP_SCAN_TYPE_SWIPE,
};
/* Drivers */
@@ -75,28 +133,24 @@ enum fp_scan_type fp_driver_get_scan_type(struct fp_driver *drv);
struct fp_dscv_dev **fp_discover_devs(void);
void fp_dscv_devs_free(struct fp_dscv_dev **devs);
struct fp_driver *fp_dscv_dev_get_driver(struct fp_dscv_dev *dev);
uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev);
uint32_t fp_dscv_dev_get_devtype(struct fp_dscv_dev *dev);
int fp_dscv_dev_supports_print_data(struct fp_dscv_dev *dev,
struct fp_print_data *print);
int fp_dscv_dev_supports_dscv_print(struct fp_dscv_dev *dev,
struct fp_dscv_print *print);
struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
struct fp_dscv_dev *fp_dscv_dev_for_print_data(struct fp_dscv_dev **devs,
struct fp_print_data *print);
struct fp_print_data *print) LIBFPRINT_DEPRECATED;
struct fp_dscv_dev *fp_dscv_dev_for_dscv_print(struct fp_dscv_dev **devs,
struct fp_dscv_print *print);
static inline uint16_t fp_dscv_dev_get_driver_id(struct fp_dscv_dev *dev)
{
return fp_driver_get_driver_id(fp_dscv_dev_get_driver(dev));
}
struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
/* Print discovery */
struct fp_dscv_print **fp_discover_prints(void);
void fp_dscv_prints_free(struct fp_dscv_print **prints);
uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print);
uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print);
enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print);
int fp_dscv_print_delete(struct fp_dscv_print *print);
struct fp_dscv_print **fp_discover_prints(void) LIBFPRINT_DEPRECATED;
void fp_dscv_prints_free(struct fp_dscv_print **prints) LIBFPRINT_DEPRECATED;
uint16_t fp_dscv_print_get_driver_id(struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
uint32_t fp_dscv_print_get_devtype(struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
enum fp_finger fp_dscv_print_get_finger(struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
int fp_dscv_print_delete(struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
/* Device handling */
struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev);
@@ -105,67 +159,84 @@ struct fp_driver *fp_dev_get_driver(struct fp_dev *dev);
int fp_dev_get_nr_enroll_stages(struct fp_dev *dev);
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);
int fp_dev_supports_dscv_print(struct fp_dev *dev, struct fp_dscv_print *print) LIBFPRINT_DEPRECATED;
/**
* fp_capture_result:
* Whether a capture failed or completed.
*
* @FP_CAPTURE_COMPLETE: Capture completed successfully, the capture data has been returned to the caller.
* @FP_CAPTURE_FAIL: Capture failed
*
*/
enum fp_capture_result {
FP_CAPTURE_COMPLETE = 0,
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);
struct fp_img **img);
int fp_dev_get_img_width(struct fp_dev *dev);
int fp_dev_get_img_height(struct fp_dev *dev);
/** \ingroup dev
/**
* fp_enroll_result:
* @FP_ENROLL_COMPLETE: Enrollment completed successfully, the enrollment data has been
* returned to the caller.
* @FP_ENROLL_FAIL: Enrollment failed due to incomprehensible data; this may occur when
* the user scans a different finger on each enroll stage.
* @FP_ENROLL_PASS: Enroll stage passed; more stages are need to complete the process.
* @FP_ENROLL_RETRY: The enrollment scan did not succeed due to poor scan quality or
* other general user scanning problem.
* @FP_ENROLL_RETRY_TOO_SHORT: The enrollment scan did not succeed because the finger swipe was
* too short.
* @FP_ENROLL_RETRY_CENTER_FINGER: The enrollment scan did not succeed because the finger was not
* centered on the scanner.
* @FP_ENROLL_RETRY_REMOVE_FINGER: The verification scan did not succeed due to quality or pressure
* problems; the user should remove their finger from the scanner before
* retrying.
*
*
* Enrollment result codes returned from fp_enroll_finger().
* Result codes with RETRY in the name suggest that the scan failed due to
* user error. Applications will generally want to inform the user of the
* problem and then retry the enrollment stage. For more info on the semantics
* of interpreting these result codes and tracking enrollment process, see
* \ref enrolling.
* [Enrolling](libfprint-Devices-operations.html#enrolling)
*/
enum fp_enroll_result {
/** Enrollment completed successfully, the enrollment data has been
* returned to the caller. */
FP_ENROLL_COMPLETE = 1,
/** Enrollment failed due to incomprehensible data; this may occur when
* the user scans a different finger on each enroll stage. */
FP_ENROLL_FAIL,
/** Enroll stage passed; more stages are need to complete the process. */
FP_ENROLL_PASS,
/** The enrollment scan did not succeed due to poor scan quality or
* other general user scanning problem. */
FP_ENROLL_RETRY = 100,
/** The enrollment scan did not succeed because the finger swipe was
* too short. */
FP_ENROLL_RETRY_TOO_SHORT,
/** The enrollment scan did not succeed because the finger was not
* centered on the scanner. */
FP_ENROLL_RETRY_CENTER_FINGER,
/** The verification scan did not succeed due to quality or pressure
* problems; the user should remove their finger from the scanner before
* retrying. */
FP_ENROLL_RETRY_REMOVE_FINGER,
};
int fp_enroll_finger_img(struct fp_dev *dev, struct fp_print_data **print_data,
struct fp_img **img);
int fp_enroll_finger(struct fp_dev *dev,
struct fp_print_data **print_data);
/** \ingroup dev
* Performs an enroll stage. See \ref enrolling for an explanation of enroll
* stages. This function is just a shortcut to calling fp_enroll_finger_img()
* with a NULL image parameter. Be sure to read the description of
* fp_enroll_finger_img() in order to understand its behaviour.
/**
* fp_verify_result:
* @FP_VERIFY_NO_MATCH: The scan completed successfully, but the newly scanned fingerprint
* does not match the fingerprint being verified against.
* In the case of identification, this return code indicates that the
* scanned finger could not be found in the print gallery.
* @FP_VERIFY_MATCH: The scan completed successfully and the newly scanned fingerprint does
* match the fingerprint being verified, or in the case of identification,
* the scanned fingerprint was found in the print gallery.
* @FP_VERIFY_RETRY: The scan did not succeed due to poor scan quality or other general
* user scanning problem.
* @FP_VERIFY_RETRY_TOO_SHORT: The scan did not succeed because the finger swipe was too short.
* @FP_VERIFY_RETRY_CENTER_FINGER: The scan did not succeed because the finger was not centered on the
* scanner.
* @FP_VERIFY_RETRY_REMOVE_FINGER: The scan did not succeed due to quality or pressure problems; the user
* should remove their finger from the scanner before retrying.
*
* \param dev the device
* \param print_data a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
* \return negative code on error, otherwise a code from #fp_enroll_result
*/
static inline int fp_enroll_finger(struct fp_dev *dev,
struct fp_print_data **print_data)
{
return fp_enroll_finger_img(dev, print_data, NULL);
}
/** \ingroup dev
* Verification result codes returned from fp_verify_finger(). Return codes
* are also shared with fp_identify_finger().
* Result codes with RETRY in the name suggest that the scan failed due to
@@ -173,78 +244,31 @@ static inline int fp_enroll_finger(struct fp_dev *dev,
* problem and then retry the verify operation.
*/
enum fp_verify_result {
/** The scan completed successfully, but the newly scanned fingerprint
* does not match the fingerprint being verified against.
* In the case of identification, this return code indicates that the
* scanned finger could not be found in the print gallery. */
FP_VERIFY_NO_MATCH = 0,
/** The scan completed successfully and the newly scanned fingerprint does
* match the fingerprint being verified, or in the case of identification,
* the scanned fingerprint was found in the print gallery. */
FP_VERIFY_MATCH = 1,
/** The scan did not succeed due to poor scan quality or other general
* user scanning problem. */
FP_VERIFY_RETRY = FP_ENROLL_RETRY,
/** The scan did not succeed because the finger swipe was too short. */
FP_VERIFY_RETRY_TOO_SHORT = FP_ENROLL_RETRY_TOO_SHORT,
/** The scan did not succeed because the finger was not centered on the
* scanner. */
FP_VERIFY_RETRY_CENTER_FINGER = FP_ENROLL_RETRY_CENTER_FINGER,
/** The scan did not succeed due to quality or pressure problems; the user
* should remove their finger from the scanner before retrying. */
FP_VERIFY_RETRY_REMOVE_FINGER = FP_ENROLL_RETRY_REMOVE_FINGER,
};
int fp_verify_finger_img(struct fp_dev *dev,
struct fp_print_data *enrolled_print, struct fp_img **img);
/** \ingroup dev
* Performs a new scan and verify it against a previously enrolled print. This
* function is just a shortcut to calling fp_verify_finger_img() with a NULL
* image output parameter.
* \param dev the device to perform the scan.
* \param enrolled_print the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
* \return negative code on error, otherwise a code from #fp_verify_result
* \sa fp_verify_finger_img()
*/
static inline int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print)
{
return fp_verify_finger_img(dev, enrolled_print, NULL);
}
int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print);
int fp_dev_supports_identification(struct fp_dev *dev);
int fp_identify_finger_img(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset,
struct fp_img **img);
/** \ingroup dev
* Performs a new scan and attempts to identify the scanned finger against a
* collection of previously enrolled fingerprints. This function is just a
* shortcut to calling fp_identify_finger_img() with a NULL image output
* parameter.
* \param dev the device to perform the scan.
* \param print_gallery NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan.
* \param match_offset output location to store the array index of the matched
* gallery print (if any was found). Only valid if FP_VERIFY_MATCH was
* returned.
* \return negative code on error, otherwise a code from #fp_verify_result
* \sa fp_identify_finger_img()
*/
static inline int fp_identify_finger(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset)
{
return fp_identify_finger_img(dev, print_gallery, match_offset, NULL);
}
int fp_identify_finger(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset);
/* Data handling */
int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger,
struct fp_print_data **data);
int fp_print_data_from_dscv_print(struct fp_dscv_print *print,
struct fp_print_data **data);
struct fp_print_data **data) LIBFPRINT_DEPRECATED;
int fp_print_data_save(struct fp_print_data *data, enum fp_finger finger);
int fp_print_data_delete(struct fp_dev *dev, enum fp_finger finger);
void fp_print_data_free(struct fp_print_data *data);
@@ -256,21 +280,13 @@ uint32_t fp_print_data_get_devtype(struct fp_print_data *data);
/* Image handling */
/** \ingroup img */
struct fp_minutia {
int x;
int y;
int ex;
int ey;
int direction;
double reliability;
int type;
int appearing;
int feature_id;
int *nbrs;
int *ridge_counts;
int num_nbrs;
};
/**
* fp_minutia:
*
* #fp_minutia is an opaque structure type. You must access it using the
* functions below.
*/
struct fp_minutia;
int fp_img_get_height(struct fp_img *img);
int fp_img_get_width(struct fp_img *img);
@@ -283,6 +299,14 @@ void fp_img_free(struct fp_img *img);
/* Polling and timing */
/**
* fp_pollfd:
* @fd: a file descriptor
* @events: Event flags to poll for from `<poll.h>`
*
* A structure representing a file descriptor and the events to poll
* for, as returned by fp_get_pollfds().
*/
struct fp_pollfd {
int fd;
short events;
@@ -293,7 +317,24 @@ int fp_handle_events(void);
size_t fp_get_pollfds(struct fp_pollfd **pollfds);
int fp_get_next_timeout(struct timeval *tv);
/**
* fp_pollfd_added_cb:
* @fd: the new file descriptor
* @events: events to monitor for, see `<poll.h>` for the possible values
*
* Type definition for a function that will be called when a new
* event source is added. The @events argument is a flag as defined in
* `<poll.h>` such as `POLLIN`, or `POLLOUT`. See fp_set_pollfd_notifiers().
*/
typedef void (*fp_pollfd_added_cb)(int fd, short events);
/**
* fp_pollfd_removed_cb:
* @fd: the file descriptor to stop monitoring
*
* Type definition for a function that will be called when an
* event source is removed. See fp_set_pollfd_notifiers().
*/
typedef void (*fp_pollfd_removed_cb)(int fd);
void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
fp_pollfd_removed_cb removed_cb);
@@ -301,45 +342,102 @@ void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
/* Library */
int fp_init(void);
void fp_exit(void);
void fp_set_debug(int level);
void fp_set_debug(int level) LIBFPRINT_DEPRECATED;
/* Asynchronous I/O */
/**
* fp_operation_stop_cb:
* @dev: the #fp_dev device
* @user_data: user data passed to the callback
*
* Type definition for a function that will be called when fp_async_dev_close(),
* fp_async_verify_stop(), fp_async_identify_stop() or fp_async_capture_stop()
* finishes.
*/
typedef void (*fp_operation_stop_cb)(struct fp_dev *dev, void *user_data);
/**
* fp_img_operation_cb:
* @dev: the #fp_dev device
* @result: an #fp_verify_result for fp_async_verify_start(), or an #fp_capture_result
* for fp_async_capture_start(), or a negative value on error
* @img: the captured #fp_img if capture or verification was successful
* @user_data: user data passed to the callback
*
* Type definition for a function that will be called when fp_async_verify_start()
* or fp_async_capture_start() finished.
*/
typedef void (*fp_img_operation_cb)(struct fp_dev *dev, int result,
struct fp_img *img, void *user_data);
/**
* fp_dev_open_cb:
* @dev: the #fp_dev device
* @status: 0 on success, or a negative value on error
* @user_data: user data passed to the callback
*
* Type definition for a function that will be called when fp_async_dev_open
* finishes.
*/
typedef void (*fp_dev_open_cb)(struct fp_dev *dev, int status, void *user_data);
int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
void *user_data);
typedef void (*fp_dev_close_cb)(struct fp_dev *dev, void *user_data);
void fp_async_dev_close(struct fp_dev *dev, fp_dev_close_cb callback,
void fp_async_dev_close(struct fp_dev *dev, fp_operation_stop_cb callback,
void *user_data);
/**
* fp_enroll_stage_cb:
* @dev: the #fp_dev device
* @result: a #fp_enroll_result on success, or a negative value on failure
* @print: the enrollment data from the final stage
* @img: an #fp_img to free with fp_img_free()
* @user_data: user data passed to the callback
*
* Type definition for a function that will be called when
* fp_async_enroll_start() finishes.
*/
typedef void (*fp_enroll_stage_cb)(struct fp_dev *dev, int result,
struct fp_print_data *print, struct fp_img *img, void *user_data);
int fp_async_enroll_start(struct fp_dev *dev, fp_enroll_stage_cb callback,
void *user_data);
typedef void (*fp_enroll_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_enroll_stop(struct fp_dev *dev, fp_enroll_stop_cb callback,
int fp_async_enroll_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
void *user_data);
typedef void (*fp_verify_cb)(struct fp_dev *dev, int result,
struct fp_img *img, void *user_data);
int fp_async_verify_start(struct fp_dev *dev, struct fp_print_data *data,
fp_verify_cb callback, void *user_data);
fp_img_operation_cb callback, void *user_data);
typedef void (*fp_verify_stop_cb)(struct fp_dev *dev, void *user_data);
int fp_async_verify_stop(struct fp_dev *dev, fp_verify_stop_cb callback,
int fp_async_verify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
void *user_data);
/**
* fp_identify_cb:
* @dev: the #fp_dev device
* @result: a #fp_verify_result on success, or a negative value on error.
* @match_offset: the array index of the matched gallery print (if any was found).
* Only valid if %FP_VERIFY_MATCH was returned.
* @img: the scan image, it must be freed with fp_img_free() after use.
* @user_data: user data passed to the callback
*
* Type definition for a function that will be called when fp_async_identify_start()
* finishes.
*/
typedef void (*fp_identify_cb)(struct fp_dev *dev, int result,
size_t match_offset, struct fp_img *img, void *user_data);
int fp_async_identify_start(struct fp_dev *dev, struct fp_print_data **gallery,
fp_identify_cb callback, void *user_data);
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,
int fp_async_identify_stop(struct fp_dev *dev, fp_operation_stop_cb callback,
void *user_data);
int fp_async_capture_start(struct fp_dev *dev, int unconditional, fp_img_operation_cb callback, void *user_data);
int fp_async_capture_stop(struct fp_dev *dev, fp_operation_stop_cb callback, void *user_data);
#ifdef __cplusplus
}
#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 w_factor, unsigned int h_factor)
{
int new_width = img->width * w_factor;
int new_height = img->height * h_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 = newimg->data + y * newimg->width + x;
p = pixels + y * rowstride + x * 3;
r[0] = p[0];
}
}
g_object_unref (resized);
return newimg;
}

View File

@@ -28,19 +28,22 @@
#include "nbis/include/bozorth.h"
#include "nbis/include/lfs.h"
/** @defgroup img Image operations
/**
* SECTION:img
* @title: Image operations
*
* libfprint offers several ways of retrieving images from imaging devices,
* one example being the fp_dev_img_capture() function. The functions
* documented below allow you to work with such images.
*
* \section img_fmt Image format
* # Image format # {#img_fmt}
* All images are represented as 8-bit greyscale data.
*
* \section img_std Image standardization
* # Image standardization # {#img_std}
* In some contexts, images you are provided through libfprint are raw images
* from the hardware. The orientation of these varies from device-to-device,
* as does the color scheme (black-on-white or white-on-black?). libfprint
* provides the fp_img_standardize function to convert images into standard
* provides the fp_img_standardize() function to convert images into standard
* form, which is defined to be: finger flesh as black on white surroundings,
* natural upright orientation.
*/
@@ -82,9 +85,11 @@ struct fp_img *fpi_img_resize(struct fp_img *img, size_t newsize)
return g_realloc(img, sizeof(*img) + newsize);
}
/** \ingroup img
/**
* fp_img_free:
* @img: the image to destroy. If NULL, function simply returns.
*
* Frees an image. Must be called when you are finished working with an image.
* \param img the image to destroy. If NULL, function simply returns.
*/
API_EXPORTED void fp_img_free(struct fp_img *img)
{
@@ -98,43 +103,55 @@ API_EXPORTED void fp_img_free(struct fp_img *img)
g_free(img);
}
/** \ingroup img
/**
* fp_img_get_height:
* @img: an image
*
* Gets the pixel height of an image.
* \param img an image
* \returns the height of the image
*
* Returns: the height of the image
*/
API_EXPORTED int fp_img_get_height(struct fp_img *img)
{
return img->height;
}
/** \ingroup img
/**
* fp_img_get_width:
* @img: an image
*
* Gets the pixel width of an image.
* \param img an image
* \returns the width of the image
*
* Returns: the width of the image
*/
API_EXPORTED int fp_img_get_width(struct fp_img *img)
{
return img->width;
}
/** \ingroup img
/**
* fp_img_get_data:
* @img: an image
*
* Gets the greyscale data for an image. This data must not be modified or
* freed, and must not be used after fp_img_free() has been called.
* \param img an image
* \returns a pointer to libfprint's internal data for the image
*
* Returns: a pointer to libfprint's internal data for the image
*/
API_EXPORTED unsigned char *fp_img_get_data(struct fp_img *img)
{
return img->data;
}
/** \ingroup img
/**
* fp_img_save_to_file:
* @img: the image to save
* @path: the path to save the image. Existing files will be overwritten.
*
* A quick convenience function to save an image to a file in
* <a href="http://netpbm.sourceforge.net/doc/pgm.html">PGM format</a>.
* \param img the image to save
* \param path the path to save the image. Existing files will be overwritten.
* \returns 0 on success, non-zero on error.
* [PGM format](http://netpbm.sourceforge.net/doc/pgm.html).
*
* Returns: 0 on success, non-zero on error.
*/
API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path)
{
@@ -149,12 +166,14 @@ API_EXPORTED int fp_img_save_to_file(struct fp_img *img, char *path)
r = fprintf(fd, "P5 %d %d 255\n", img->width, img->height);
if (r < 0) {
fclose(fd);
fp_err("pgm header write failed, error %d", r);
return r;
}
r = fwrite(img->data, 1, write_size, fd);
if (r < write_size) {
fclose(fd);
fp_err("short write (%d)", r);
return -EIO;
}
@@ -209,12 +228,14 @@ static void invert_colors(struct fp_img *img)
img->data[i] = 0xff - img->data[i];
}
/** \ingroup img
* \ref img_std "Standardizes" an image by normalizing its orientation, colors,
/**
* fp_img_standardize:
* @img: the image to standardize
*
* [Standardizes](libfprint-Image-operations.html#img_std) an image by normalizing its orientation, colors,
* etc. It is safe to call this multiple times on an image, libfprint keeps
* track of the work it needs to do to make an image standard and will not
* perform these operations more than once for a given image.
* \param img the image to standardize
*/
API_EXPORTED void fp_img_standardize(struct fp_img *img)
{
@@ -267,7 +288,7 @@ static void minutiae_to_xyt(struct fp_minutiae *minutiae, int bwidth,
xyt->nrows = nmin;
}
int fpi_img_detect_minutiae(struct fp_img *img)
static int fpi_img_detect_minutiae(struct fp_img *img)
{
struct fp_minutiae *minutiae;
int r;
@@ -283,6 +304,9 @@ int fpi_img_detect_minutiae(struct fp_img *img)
return -EINVAL;
}
/* Remove perimeter points from partial image */
g_lfsparms_V2.remove_perimeter_pts = img->flags & FP_IMG_PARTIAL ? TRUE : FALSE;
/* 25.4 mm per inch */
timer = g_timer_new();
r = get_minutiae(&minutiae, &quality_map, &direction_map,
@@ -313,6 +337,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) {
@@ -327,9 +352,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!
@@ -342,63 +369,96 @@ 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;
}
/** \ingroup img
/**
* fp_img_binarize:
* @img: a standardized image
*
* Get a binarized form of a standardized scanned image. This is where the
* fingerprint image has been "enhanced" and is a set of pure black ridges
* on a pure white background. Internally, image processing happens on top
* of the binarized image.
*
* The image must have been \ref img_std "standardized" otherwise this function
* will fail.
* The image must have been [standardized](libfprint-Image-operations.html#img_std)
* otherwise this function will fail.
*
* It is safe to binarize an image and free the original while continuing
* to use the binarized version.
*
* You cannot binarize an image twice.
*
* \param img a standardized image
* \returns a new image representing the binarized form of the original, or
* NULL on error. Must be freed with fp_img_free() after use.
* Returns: a new image representing the binarized form of the original, or
* %NULL on error. Must be freed with fp_img_free() after use.
*/
API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
{
@@ -430,15 +490,19 @@ API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
return ret;
}
/** \ingroup img
/**
* fp_img_get_minutiae:
* @img: a standardized image
* @nr_minutiae: an output location to store minutiae list length
*
* Get a list of minutiae detected in an image. A minutia point is a feature
* detected on a fingerprint, typically where ridges end or split.
* libfprint's image processing code relies upon comparing sets of minutiae,
* so accurate placement of minutia points is critical for good imaging
* performance.
*
* The image must have been \ref img_std "standardized" otherwise this function
* will fail.
* The image must have been [standardized](libfprint-Image-operations.html#img_std)
* otherwise this function will fail.
*
* You cannot pass a binarized image to this function. Instead, pass the
* original image.
@@ -448,9 +512,7 @@ API_EXPORTED struct fp_img *fp_img_binarize(struct fp_img *img)
* valid while the parent image has not been freed, and the minutiae data
* must not be modified or freed.
*
* \param img a standardized image
* \param nr_minutiae an output location to store minutiae list length
* \returns a list of minutiae points. Must not be modified or freed.
* Returns: a list of minutiae points. Must not be modified or freed.
*/
API_EXPORTED struct fp_minutia **fp_img_get_minutiae(struct fp_img *img,
int *nr_minutiae)
@@ -474,3 +536,87 @@ API_EXPORTED struct fp_minutia **fp_img_get_minutiae(struct fp_img *img,
return img->minutiae->list;
}
libusb_device_handle *
fpi_imgdev_get_usb_dev(struct fp_img_dev *dev)
{
return dev->udev;
}
void
fpi_imgdev_set_user_data(struct fp_img_dev *imgdev,
void *user_data)
{
imgdev->priv = user_data;
}
void *
fpi_imgdev_get_user_data(struct fp_img_dev *imgdev)
{
return imgdev->priv;
}
struct fp_dev *
fpi_imgdev_get_dev(struct fp_img_dev *imgdev)
{
return imgdev->dev;
}
enum fp_imgdev_enroll_state
fpi_imgdev_get_action_state(struct fp_img_dev *imgdev)
{
return imgdev->action_state;
}
enum fp_imgdev_action
fpi_imgdev_get_action(struct fp_img_dev *imgdev)
{
return imgdev->action;
}
int
fpi_imgdev_get_action_result(struct fp_img_dev *imgdev)
{
return imgdev->action_result;
}
void
fpi_imgdev_set_action_result(struct fp_img_dev *imgdev,
int action_result)
{
imgdev->action_result = action_result;
}
/* Calculate squared standand deviation */
int fpi_std_sq_dev(const unsigned char *buf, int size)
{
int res = 0, mean = 0, i;
if (size > (INT_MAX / 65536)) {
fp_err("%s: we might get an overflow!", __func__);
return -EOVERFLOW;
}
for (i = 0; i < size; i++)
mean += buf[i];
mean /= size;
for (i = 0; i < size; i++) {
int dev = (int)buf[i] - mean;
res += dev*dev;
}
return res / size;
}
/* Calculate normalized mean square difference of two lines */
int fpi_mean_sq_diff_norm(unsigned char *buf1, unsigned char *buf2, int size)
{
int res = 0, i;
for (i = 0; i < size; i++) {
int dev = (int)buf1[i] - (int)buf2[i];
res += dev * dev;
}
return res / size;
}

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,25 +146,37 @@ 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);
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);
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_ON);
}
break;
case IMG_ACTION_VERIFY:
fpi_drvcb_report_verify_result(imgdev->dev, r, img);
imgdev->action_result = 0;
fp_print_data_free(data);
break;
case IMG_ACTION_IDENTIFY:
fpi_drvcb_report_identify_result(imgdev->dev, r,
imgdev->identify_match_offset, img);
imgdev->action_result = 0;
fp_print_data_free(data);
break;
case IMG_ACTION_CAPTURE:
fpi_drvcb_report_capture_result(imgdev->dev, r, img);
imgdev->action_result = 0;
break;
default:
fp_err("unhandled action %d", imgdev->action);
break;
@@ -206,11 +220,18 @@ static void identify_process_img(struct fp_img_dev *imgdev)
imgdev->identify_match_offset = match_offset;
}
void fpi_imgdev_abort_scan(struct fp_img_dev *imgdev, int result)
{
imgdev->action_result = result;
imgdev->action_state = IMG_ACQUIRE_STATE_AWAIT_FINGER_OFF;
dev_change_state(imgdev, IMGDEV_STATE_AWAIT_FINGER_OFF);
}
void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
{
struct fp_print_data *print;
int r;
fp_dbg("");
G_DEBUG_HERE();
if (imgdev->action_state != IMG_ACQUIRE_STATE_AWAIT_IMAGE) {
fp_dbg("ignoring due to current state %d", imgdev->action_state);
@@ -231,24 +252,41 @@ void fpi_imgdev_image_captured(struct fp_img_dev *imgdev, struct fp_img *img)
fp_img_standardize(img);
imgdev->acquire_img = img;
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;
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);
@@ -256,6 +294,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;
@@ -280,6 +321,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;
@@ -300,6 +344,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;
@@ -313,7 +360,7 @@ void fpi_imgdev_activate_complete(struct fp_img_dev *imgdev, int status)
void fpi_imgdev_deactivate_complete(struct fp_img_dev *imgdev)
{
fp_dbg("");
G_DEBUG_HERE();
switch (imgdev->action) {
case IMG_ACTION_ENROLL:
@@ -325,6 +372,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;
@@ -385,6 +435,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)
@@ -400,8 +451,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;
}
@@ -421,6 +474,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;
@@ -446,6 +507,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;
@@ -457,5 +526,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;
}

206
libfprint/meson.build Normal file
View File

@@ -0,0 +1,206 @@
libfprint_sources = [
'fp_internal.h',
'drivers_api.h',
'async.c',
'core.c',
'data.c',
'drv.c',
'img.c',
'imgdev.c',
'poll.c',
'sync.c',
'assembling.c',
'assembling.h',
'drivers/driver_ids.h',
]
nbis_sources = [
'nbis/include/bozorth.h',
'nbis/include/bz_array.h',
'nbis/include/defs.h',
'nbis/include/lfs.h',
'nbis/include/log.h',
'nbis/include/morph.h',
'nbis/include/sunrast.h',
'nbis/bozorth3/bozorth3.c',
'nbis/bozorth3/bz_alloc.c',
'nbis/bozorth3/bz_drvrs.c',
'nbis/bozorth3/bz_gbls.c',
'nbis/bozorth3/bz_io.c',
'nbis/bozorth3/bz_sort.c',
'nbis/mindtct/binar.c',
'nbis/mindtct/block.c',
'nbis/mindtct/contour.c',
'nbis/mindtct/detect.c',
'nbis/mindtct/dft.c',
'nbis/mindtct/free.c',
'nbis/mindtct/globals.c',
'nbis/mindtct/imgutil.c',
'nbis/mindtct/init.c',
'nbis/mindtct/line.c',
'nbis/mindtct/log.c',
'nbis/mindtct/loop.c',
'nbis/mindtct/maps.c',
'nbis/mindtct/matchpat.c',
'nbis/mindtct/minutia.c',
'nbis/mindtct/morph.c',
'nbis/mindtct/quality.c',
'nbis/mindtct/remove.c',
'nbis/mindtct/ridges.c',
'nbis/mindtct/shape.c',
'nbis/mindtct/sort.c',
'nbis/mindtct/util.c',
]
aeslib = false
aesx660 = false
aes3k = false
drivers_sources = []
drivers_cflags = []
foreach driver: drivers
if driver == 'upekts'
drivers_sources += [ 'drivers/upekts.c' ]
endif
if driver == 'upektc'
drivers_sources += [ 'drivers/upektc.c', 'drivers/upektc.h' ]
endif
if driver == 'upeksonly'
drivers_sources += [ 'drivers/upeksonly.c', 'drivers/upeksonly.h' ]
endif
if driver == 'uru4000'
drivers_sources += [ 'drivers/uru4000.c' ]
endif
if driver == 'aes1610'
drivers_sources += [ 'drivers/aes1610.c' ]
aeslib = true
endif
if driver == 'aes1660'
drivers_sources += [ 'drivers/aes1660.c', 'drivers/aes1660.h' ]
aeslib = true
aesx660 = true
endif
if driver == 'aes2501'
drivers_sources += [ 'drivers/aes2501.c', 'drivers/aes2501.h' ]
aeslib = true
endif
if driver == 'aes2550'
drivers_sources += [ 'drivers/aes2550.c', 'drivers/aes2550.h' ]
aeslib = true
endif
if driver == 'aes2660'
drivers_sources += [ 'drivers/aes2660.c', 'drivers/aes2660.h' ]
aeslib = true
aesx660 = true
endif
if driver == 'aes3500'
drivers_sources += [ 'drivers/aes3500.c' ]
aeslib = true
aes3k = true
endif
if driver == 'aes4000'
drivers_sources += [ 'drivers/aes4000.c' ]
aeslib = true
aes3k = true
endif
if driver == 'fdu2000'
drivers_sources += [ 'drivers/fdu2000.c' ]
endif
if driver == 'vcom5s'
drivers_sources += [ 'drivers/vcom5s.c' ]
endif
if driver == 'vfs101'
drivers_sources += [ 'drivers/vfs101.c' ]
endif
if driver == 'vfs301'
drivers_sources += [ 'drivers/vfs301.c', 'drivers/vfs301_proto.c', 'drivers/vfs301_proto.h', 'drivers/vfs301_proto_fragments.h' ]
endif
if driver == 'vfs5011'
drivers_sources += [ 'drivers/vfs5011.c', 'drivers/vfs5011_proto.h' ]
endif
if driver == 'upektc_img'
drivers_sources += [ 'drivers/upektc_img.c', 'drivers/upektc_img.h' ]
endif
if driver == 'etes603'
drivers_sources += [ 'drivers/etes603.c' ]
endif
if driver == 'vfs0050'
drivers_sources += [ 'drivers/vfs0050.c', 'drivers/vfs0050.h' ]
endif
if driver == 'elan'
drivers_sources += [ 'drivers/elan.c', 'drivers/elan.h' ]
endif
endforeach
if aeslib
drivers_sources += [ 'aeslib.c', 'aeslib.h' ]
endif
if aesx660
drivers_sources += ['drivers/aesx660.c', 'drivers/aesx660.h' ]
endif
if aes3k
drivers_sources += ['drivers/aes3k.c', 'drivers/aes3k.h' ]
endif
other_sources = []
if imaging_dep.found()
other_sources += [ 'pixman.c' ]
endif
libfprint_sources += configure_file(input: 'empty_file',
output: 'drivers_definitions.h',
capture: true,
command: [
'/bin/echo',
drivers_struct_list
])
libfprint_sources += configure_file(input: 'empty_file',
output: 'drivers_arrays.h',
capture: true,
command: [
'/bin/echo',
drivers_primitive_array + '\n\n' + drivers_img_array
])
deps = [ mathlib_dep, glib_dep, libusb_dep, nss_dep, imaging_dep ]
libfprint = library('fprint',
libfprint_sources + drivers_sources + nbis_sources + other_sources,
soversion: soversion,
version: libversion,
c_args: common_cflags + drivers_cflags,
include_directories: [
root_inc,
include_directories('nbis/include'),
],
dependencies: deps,
install: true)
libfprint_dep = declare_dependency(link_with: libfprint,
include_directories: root_inc)
install_headers(['fprint.h'], subdir: 'libfprint')
udev_rules = executable('fprint-list-udev-rules',
'fprint-list-udev-rules.c',
include_directories: [
root_inc,
],
dependencies: [ deps, libfprint_dep ],
install: false)
if get_option('udev_rules')
custom_target('udev-rules',
output: '60-fprint-autosuspend.rules',
capture: true,
command: [ udev_rules ],
install: true,
install_dir: udev_rules_dir)
endif
supported_devices = executable('fprint-list-supported-devices',
'fprint-list-supported-devices.c',
include_directories: [
root_inc,
],
dependencies: [ deps, libfprint_dep ],
install: false)

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -23,7 +23,7 @@ Do not remove this notice.
* 734.3(b)(3)(i). libfprint qualifies as publicly available technology as per
* the definition in section 734.7(a)(1).
*
* For further information, see http://reactivated.net/fprint/US_export_control
* For further information, see https://fprint.freedesktop.org/us-export-control.html
*/
/*******************************************************************************

View File

@@ -236,6 +236,8 @@ typedef struct lfsparms{
int pores_steps_bwd;
double pores_min_dist2;
double pores_max_ratio;
int remove_perimeter_pts;
int min_pp_distance;
/* Ridge Counting Controls */
int max_nbrs;
@@ -585,6 +587,9 @@ typedef struct lfsparms{
/* contour points to be considered a pore. */
#define PORES_MAX_RATIO 2.25
/* Points which are closer than this distance to scan perimeter will be removed */
#define PERIMETER_PTS_DISTANCE 10
/***** RIDGE COUNTING CONSTANTS *****/

View File

@@ -129,6 +129,8 @@ LFSPARMS g_lfsparms = {
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
FALSE, /* not removing perimeter points by default */
PERIMETER_PTS_DISTANCE,
/* Ridge Counting Controls */
MAX_NBRS,
@@ -213,6 +215,8 @@ LFSPARMS g_lfsparms_V2 = {
PORES_STEPS_BWD,
PORES_MIN_DIST2,
PORES_MAX_RATIO,
FALSE, /* not removing perimeter points by default */
PERIMETER_PTS_DISTANCE,
/* Ridge Counting Controls */
MAX_NBRS,

View File

@@ -151,7 +151,7 @@ char get_south8_2(char *ptr, const int row, const int iw, const int ih,
{
if (row >= ih-1) /* catch case where image is undefined southwards */
return failcode; /* use plane geometry and return code. */
else
return *(ptr+iw);
}
@@ -175,7 +175,7 @@ char get_north8_2(char *ptr, const int row, const int iw,
{
if (row < 1) /* catch case where image is undefined northwards */
return failcode; /* use plane geometry and return code. */
else
return *(ptr-iw);
}
@@ -199,7 +199,7 @@ char get_east8_2(char *ptr, const int col, const int iw,
{
if (col >= iw-1) /* catch case where image is undefined eastwards */
return failcode; /* use plane geometry and return code. */
else
return *(ptr+ 1);
}
@@ -221,6 +221,6 @@ char get_west8_2(char *ptr, const int col, const int failcode)
{
if (col < 1) /* catch case where image is undefined westwards */
return failcode; /* use plane geometry and return code. */
else
return *(ptr- 1);
}

View File

@@ -1350,6 +1350,159 @@ static int remove_overlaps(MINUTIAE *minutiae,
return(0);
}
static void mark_minutiae_in_range(MINUTIAE *minutiae, int *to_remove, int x, int y,
const LFSPARMS *lfsparms)
{
int i, dist;
for (i = 0; i < minutiae->num; i++) {
if (to_remove[i])
continue;
dist = (int)sqrt((x - minutiae->list[i]->x) * (x - minutiae->list[i]->x) +
(y - minutiae->list[i]->y) * (y - minutiae->list[i]->y));
if (dist < lfsparms->min_pp_distance) {
to_remove[i] = 1;
}
}
}
/*************************************************************************
**************************************************************************
#cat: remove_perimeter_pts - Takes a list of true and false minutiae and
#cat: attempts to detect and remove those false minutiae that
#cat: belong to image edge
Input:
minutiae - list of true and false minutiae
bdata - binary image data (0==while & 1==black)
iw - width (in pixels) of image
ih - height (in pixels) of image
lfsparms - parameters and thresholds for controlling LFS
Output:
minutiae - list of pruned minutiae
Return Code:
Zero - successful completion
Negative - system error
**************************************************************************/
static int remove_perimeter_pts(MINUTIAE *minutiae,
unsigned char *bdata, const int iw, const int ih,
const LFSPARMS *lfsparms)
{
int i, j, ret, *to_remove;
int *left, *left_up, *left_down;
int *right, *right_up, *right_down;
int removed = 0;
int left_min, right_max;
if (!lfsparms->remove_perimeter_pts)
return(0);
to_remove = calloc(minutiae->num, sizeof(int));
left = calloc(ih, sizeof(int));
left_up = calloc(ih, sizeof(int));
left_down = calloc(ih, sizeof(int));
right = calloc(ih, sizeof(int));
right_up = calloc(ih, sizeof(int));
right_down = calloc(ih, sizeof(int));
/* Pass downwards */
left_min = iw - 1;
right_max = 0;
for (i = 0; i < ih; i++) {
for (j = 0; j < left_min; j++) {
if ((bdata[i * iw + j] != 0)) {
left_min = j;
break;
}
}
if (left_min == (iw - 1))
left_down[i] = -1;
else
left_down[i] = left_min;
for (j = iw - 1; j >= right_max; j--) {
if ((bdata[i * iw + j] != 0)) {
right_max = j;
break;
}
}
if (right_max == 0)
right_down[i] = -1;
else
right_down[i] = right_max;
}
/* Pass upwards */
left_min = iw - 1;
right_max = 0;
for (i = ih - 1; i >= 0; i--) {
for (j = 0; j < left_min; j++) {
if ((bdata[i * iw + j] != 0)) {
left_min = j;
break;
}
}
if (left_min == (iw - 1))
left_up[i] = -1;
else
left_up[i] = left_min;
for (j = iw - 1; j >= right_max; j--) {
if ((bdata[i * iw + j] != 0)) {
right_max = j;
break;
}
}
if (right_max == 0)
right_up[i] = -1;
else
right_up[i] = right_max;
}
/* Merge */
left_min = left_down[ih - 1];
right_max = right_down[ih - 1];
for (i = 0; i < ih; i++) {
if (left_down[i] != left_min)
left[i] = left_down[i];
else
left[i] = left_up[i];
if (right_down[i] != right_max)
right[i] = right_down[i];
else
right[i] = right_up[i];
}
free(left_up);
free(left_down);
free(right_up);
free(right_down);
/* Mark minitiae close to the edge */
for (i = 0; i < ih; i++) {
if (left[i] != -1)
mark_minutiae_in_range(minutiae, to_remove, left[i], i, lfsparms);
if (right[i] != -1)
mark_minutiae_in_range(minutiae, to_remove, right[i], i, lfsparms);
}
free(left);
free(right);
for (i = minutiae->num - 1; i >= 0; i--) {
/* If the current minutia index is flagged for removal ... */
if (to_remove[i]){
removed ++;
/* Remove the minutia from the minutiae list. */
if((ret = remove_minutia(i, minutiae))){
free(to_remove);
return(ret);
}
}
}
free(to_remove);
return (0);
}
/*************************************************************************
**************************************************************************
#cat: remove_pores_V2 - Attempts to detect and remove minutia points located on
@@ -2104,6 +2257,11 @@ int remove_false_minutia_V2(MINUTIAE *minutiae,
return(ret);
}
/* 11. Remove minutiae on image edge */
if((ret = remove_perimeter_pts(minutiae, bdata, iw, ih, lfsparms))) {
return (ret);
}
return(0);
}

View File

@@ -1,6 +1,7 @@
/*
* 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
@@ -17,50 +18,45 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <magick/ImageMagick.h>
#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)
{
Image *mimg;
Image *resized;
ExceptionInfo exception;
MagickBooleanType ret;
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;
/* 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. */
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);
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);
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;
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;
}
memcpy(newimg->data, pixman_image_get_data(resized), new_width * new_height);
DestroyImage(mimg);
DestroyImage(resized);
pixman_image_unref(orig);
pixman_image_unref(resized);
return newimg;
}

View File

@@ -19,6 +19,8 @@
#define FP_COMPONENT "poll"
#include "fp_internal.h"
#include <config.h>
#include <errno.h>
#include <time.h>
@@ -27,10 +29,10 @@
#include <glib.h>
#include <libusb.h>
#include "fp_internal.h"
/**
* @defgroup poll Polling and timing operations
* SECTION:events
* @title: Initialisation and events handling
*
* These functions are only applicable to users of libfprint's asynchronous
* API.
*
@@ -50,11 +52,15 @@
* If there are no events pending, fp_handle_events() will block for a few
* seconds (and will handle any new events should anything occur in that time).
* If you wish to customise this timeout, you can use
* fp_handle_events_timeout() instead. If you wish to do a nonblocking
* fp_handle_events_timeout() instead. If you wish to do a non-blocking
* iteration, call fp_handle_events_timeout() with a zero timeout.
*
* TODO: document how application is supposed to know when to call these
* functions.
* How to integrate events handling depends on your main loop implementation.
* The sister fprintd project includes an implementation of main loop handling
* that integrates into GLib's main loop. The
* [libusb documentation](http://libusb.sourceforge.net/api-1.0/group__poll.html#details)
* also includes more details about how to integrate libfprint events into
* your main loop.
*/
/* this is a singly-linked list of pending timers, sorted with the timer that
@@ -124,7 +130,7 @@ struct fpi_timeout *fpi_timeout_add(unsigned int msec, fpi_timeout_fn callback,
void fpi_timeout_cancel(struct fpi_timeout *timeout)
{
fp_dbg("");
G_DEBUG_HERE();
active_timers = g_slist_remove(active_timers, timeout);
g_free(timeout);
}
@@ -161,7 +167,7 @@ static int get_next_timeout_expiry(struct timeval *out,
timerclear(out);
} else {
timersub(&next_timeout->expiry, &tv, out);
fp_dbg("next timeout in %d.%06ds", out->tv_sec, out->tv_usec);
fp_dbg("next timeout in %ld.%06lds", out->tv_sec, out->tv_usec);
}
return 1;
@@ -170,7 +176,7 @@ static int get_next_timeout_expiry(struct timeval *out,
/* handle a timeout that has expired */
static void handle_timeout(struct fpi_timeout *timeout)
{
fp_dbg("");
G_DEBUG_HERE();
timeout->callback(timeout->data);
active_timers = g_slist_remove(active_timers, timeout);
g_free(timeout);
@@ -192,14 +198,16 @@ static int handle_timeouts(void)
return 0;
}
/** \ingroup poll
/**
* fp_handle_events_timeout:
* @timeout: Maximum timeout for this blocking function
*
* Handle any pending events. If a non-zero timeout is specified, the function
* will potentially block for the specified amount of time, although it may
* return sooner if events have been handled. The function acts as non-blocking
* for a zero timeout.
*
* \param timeout Maximum timeout for this blocking function
* \returns 0 on success, non-zero on error.
* Returns: 0 on success, non-zero on error.
*/
API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
{
@@ -236,12 +244,14 @@ API_EXPORTED int fp_handle_events_timeout(struct timeval *timeout)
return handle_timeouts();
}
/** \ingroup poll
/**
* fp_handle_events:
*
* Convenience function for calling fp_handle_events_timeout() with a sensible
* default timeout value of two seconds (subject to change if we decide another
* value is more sensible).
*
* \returns 0 on success, non-zero on error.
* Returns: 0 on success, non-zero on error.
*/
API_EXPORTED int fp_handle_events(void)
{
@@ -251,10 +261,14 @@ API_EXPORTED int fp_handle_events(void)
return fp_handle_events_timeout(&tv);
}
/* FIXME: docs
* returns 0 if no timeouts active
* returns 1 if timeout returned
* zero timeout means events are to be handled immediately */
/**
* fp_get_next_timeout:
* @tv: a %timeval structure containing the duration to the next timeout.
*
* A zero filled @tv timeout means events are to be handled immediately
*
* Returns: returns 0 if no timeouts active, or 1 if timeout returned.
*/
API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
{
struct timeval fprint_timeout;
@@ -286,16 +300,18 @@ API_EXPORTED int fp_get_next_timeout(struct timeval *tv)
return 1;
}
/** \ingroup poll
/**
* fp_get_pollfds:
* @pollfds: output location for a list of pollfds. If non-%NULL, must be
* released with free() when done.
*
* Retrieve a list of file descriptors that should be polled for events
* interesting to libfprint. This function is only for users who wish to
* combine libfprint's file descriptor set with other event sources - more
* combine libfprint's file descriptor set with other event sources more
* simplistic users will be able to call fp_handle_events() or a variant
* directly.
*
* \param pollfds output location for a list of pollfds. If non-NULL, must be
* released with free() when done.
* \returns the number of pollfds in the resultant list, or negative on error.
* Returns: the number of pollfds in the resultant list, or negative on error.
*/
API_EXPORTED size_t fp_get_pollfds(struct fp_pollfd **pollfds)
{
@@ -326,7 +342,14 @@ API_EXPORTED size_t fp_get_pollfds(struct fp_pollfd **pollfds)
return cnt;
}
/* FIXME: docs */
/**
* fp_set_pollfd_notifiers:
* @added_cb: a #fp_pollfd_added_cb callback or %NULL
* @removed_cb: a #fp_pollfd_removed_cb callback or %NULL
*
* This sets the callback functions to call for every new or removed
* file descriptor used as an event source.
*/
API_EXPORTED void fp_set_pollfd_notifiers(fp_pollfd_added_cb added_cb,
fp_pollfd_removed_cb removed_cb)
{

View File

@@ -19,11 +19,11 @@
#define FP_COMPONENT "sync"
#include "fp_internal.h"
#include <config.h>
#include <errno.h>
#include "fp_internal.h"
struct sync_open_data {
struct fp_dev *dev;
int status;
@@ -37,12 +37,15 @@ static void sync_open_cb(struct fp_dev *dev, int status, void *user_data)
odata->status = status;
}
/** \ingroup dev
/**
* fp_dev_open:
* @ddev: the discovered device to open
*
* Opens and initialises a device. This is the function you call in order
* to convert a \ref dscv_dev "discovered device" into an actual device handle
* to convert a #fp_dscv_dev discovered device into an actual device handle
* that you can perform operations with.
* \param ddev the discovered device to open
* \returns the opened device handle, or NULL on error
*
* Returns: the opened device handle, or %NULL on error
*/
API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
{
@@ -50,7 +53,7 @@ API_EXPORTED struct fp_dev *fp_dev_open(struct fp_dscv_dev *ddev)
struct sync_open_data *odata = g_malloc0(sizeof(*odata));
int r;
fp_dbg("");
G_DEBUG_HERE();
r = fp_async_dev_open(ddev, sync_open_cb, odata);
if (r)
goto out;
@@ -71,15 +74,17 @@ out:
static void sync_close_cb(struct fp_dev *dev, void *user_data)
{
fp_dbg("");
G_DEBUG_HERE();
gboolean *closed = user_data;
*closed = TRUE;
}
/** \ingroup dev
/**
* fp_dev_close:
* @dev: the device to close. If %NULL, function simply returns.
*
* Close a device. You must call this function when you are finished using
* a fingerprint device.
* \param dev the device to close. If NULL, function simply returns.
*/
API_EXPORTED void fp_dev_close(struct fp_dev *dev)
{
@@ -88,7 +93,7 @@ API_EXPORTED void fp_dev_close(struct fp_dev *dev)
if (!dev)
return;
fp_dbg("");
G_DEBUG_HERE();
fp_async_dev_close(dev, sync_close_cb, &closed);
while (!closed)
if (fp_handle_events() < 0)
@@ -116,13 +121,21 @@ static void sync_enroll_cb(struct fp_dev *dev, int result,
static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
{
gboolean *stopped = user_data;
fp_dbg("");
G_DEBUG_HERE();
*stopped = TRUE;
}
/** \ingroup dev
* Performs an enroll stage. See \ref enrolling for an explanation of enroll
* stages.
/**
* fp_enroll_finger_img:
* @dev: the device
* @print_data: a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
* @img: location to store the scan image. accepts %NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
*
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
* for an explanation of enroll stages.
*
* If no enrollment is in process, this kicks of the process and runs the
* first stage. If an enrollment is already in progress, calling this
@@ -137,41 +150,35 @@ static void enroll_stop_cb(struct fp_dev *dev, void *user_data)
* The RETRY codes from #fp_enroll_result may be returned from any enroll
* stage. These codes indicate that the scan was not succesful in that the
* user did not position their finger correctly or similar. When a RETRY code
* is returned, the enrollment stage is <b>not</b> advanced, so the next call
* is returned, the enrollment stage is <emphasis role="strong">not</emphasis> advanced, so the next call
* into this function will retry the current stage again. The current stage may
* need to be retried several times.
*
* The fp_enroll_result#FP_ENROLL_FAIL code may be returned from any enroll
* The %FP_ENROLL_FAIL code may be returned from any enroll
* stage. This code indicates that even though the scans themselves have been
* acceptable, data processing applied to these scans produces incomprehensible
* results. In other words, the user may have been scanning a different finger
* for each stage or something like that. Like negative error codes, this
* return code indicates that the enrollment process has been aborted.
*
* The fp_enroll_result#FP_ENROLL_PASS code will only ever be returned for
* The %FP_ENROLL_PASS code will only ever be returned for
* non-final stages. This return code indicates that the scan was acceptable
* and the next call into this function will advance onto the next enroll
* stage.
*
* The fp_enroll_result#FP_ENROLL_COMPLETE code will only ever be returned
* The %FP_ENROLL_COMPLETE code will only ever be returned
* from the final enroll stage. It indicates that enrollment completed
* successfully, and that print_data has been assigned to point to the
* resultant enrollment data. The print_data parameter will not be modified
* during any other enrollment stages, hence it is actually legal to pass NULL
* as this argument for all but the final stage.
*
*
* If the device is an imaging device, it can also return the image from
* the scan, even when the enroll fails with a RETRY or FAIL code. It is legal
* to call this function even on non-imaging devices, just don't expect them to
* provide images.
*
* \param dev the device
* \param print_data a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
* \param img location to store the scan image. accepts NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
* \return negative code on error, otherwise a code from #fp_enroll_result
* Returns: negative code on error, otherwise a code from #fp_enroll_result
*/
API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
struct fp_print_data **print_data, struct fp_img **img)
@@ -182,7 +189,7 @@ API_EXPORTED int fp_enroll_finger_img(struct fp_dev *dev,
gboolean stopped = FALSE;
struct sync_enroll_data *edata = NULL;
int r;
fp_dbg("");
G_DEBUG_HERE();
/* FIXME __enroll_stage is ugly, can we replace it by some function that
* says whether we're enrolling or not, and then put __enroll_stage into
@@ -280,6 +287,25 @@ err:
return r;
}
/**
* fp_enroll_finger:
* @dev: the device
* @print_data: a location to return the resultant enrollment data from
* the final stage. Must be freed with fp_print_data_free() after use.
*
* Performs an enroll stage. See [Enrolling](libfprint-Devices-operations.html#enrolling)
* for an explanation of enroll stages. This function is just a shortcut to
* calling fp_enroll_finger_img() with a %NULL image parameter. Be sure to read
* the description of fp_enroll_finger_img() in order to understand its behaviour.
*
* Returns: negative code on error, otherwise a code from #fp_enroll_result
*/
API_EXPORTED int fp_enroll_finger(struct fp_dev *dev,
struct fp_print_data **print_data)
{
return fp_enroll_finger_img(dev, print_data, NULL);
}
struct sync_verify_data {
gboolean populated;
int result;
@@ -298,24 +324,26 @@ static void sync_verify_cb(struct fp_dev *dev, int result, struct fp_img *img,
static void verify_stop_cb(struct fp_dev *dev, void *user_data)
{
gboolean *stopped = user_data;
fp_dbg("");
G_DEBUG_HERE();
*stopped = TRUE;
}
/** \ingroup dev
/**
* fp_verify_finger_img:
* @dev: the device to perform the scan.
* @enrolled_print: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
* @img: location to store the scan image. accepts %NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
* Performs a new scan and verify it against a previously enrolled print.
* If the device is an imaging device, it can also return the image from
* the scan, even when the verify fails with a RETRY code. It is legal to
* call this function even on non-imaging devices, just don't expect them to
* provide images.
*
* \param dev the device to perform the scan.
* \param enrolled_print the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
* \param img location to store the scan image. accepts NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
* \return negative code on error, otherwise a code from #fp_verify_result
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_verify_finger_img(struct fp_dev *dev,
struct fp_print_data *enrolled_print, struct fp_img **img)
@@ -392,6 +420,26 @@ err:
return r;
}
/**
* fp_verify_finger:
* @dev: the device to perform the scan.
* @enrolled_print: the print to verify against. Must have been previously
* enrolled with a device compatible to the device selected to perform the scan.
*
* Performs a new scan and verify it against a previously enrolled print. This
* function is just a shortcut to calling fp_verify_finger_img() with a NULL
* image output parameter.
*
* See also fp_verify_finger_img().
*
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_verify_finger(struct fp_dev *dev,
struct fp_print_data *enrolled_print)
{
return fp_verify_finger_img(dev, enrolled_print, NULL);
}
struct sync_identify_data {
gboolean populated;
int result;
@@ -412,11 +460,23 @@ static void sync_identify_cb(struct fp_dev *dev, int result,
static void identify_stop_cb(struct fp_dev *dev, void *user_data)
{
gboolean *stopped = user_data;
fp_dbg("");
G_DEBUG_HERE();
*stopped = TRUE;
}
/** \ingroup dev
/**
* fp_identify_finger_img:
* @dev: the device to perform the scan.
* @print_gallery: NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan.
* @match_offset: output location to store the array index of the matched
* gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
* returned.
* @img: location to store the scan image. accepts %NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
* Performs a new scan and attempts to identify the scanned finger against
* a collection of previously enrolled fingerprints.
* If the device is an imaging device, it can also return the image from
@@ -425,7 +485,7 @@ static void identify_stop_cb(struct fp_dev *dev, void *user_data)
* provide images.
*
* This function returns codes from #fp_verify_result. The return code
* fp_verify_result#FP_VERIFY_MATCH indicates that the scanned fingerprint
* %FP_VERIFY_MATCH indicates that the scanned fingerprint
* does appear in the print gallery, and the match_offset output parameter
* will indicate the index into the print gallery array of the matched print.
*
@@ -435,17 +495,7 @@ static void identify_stop_cb(struct fp_dev *dev, void *user_data)
* Not all devices support identification. -ENOTSUP will be returned when
* this is the case.
*
* \param dev the device to perform the scan.
* \param print_gallery NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan.
* \param match_offset output location to store the array index of the matched
* gallery print (if any was found). Only valid if FP_VERIFY_MATCH was
* returned.
* \param img location to store the scan image. accepts NULL for no image
* storage. If an image is returned, it must be freed with fp_img_free() after
* use.
* \return negative code on error, otherwise a code from #fp_verify_result
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_identify_finger_img(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset,
@@ -512,3 +562,128 @@ err:
return r;
}
/**
* fp_identify_finger:
* @dev: the device to perform the scan.
* @print_gallery: %NULL-terminated array of pointers to the prints to
* identify against. Each one must have been previously enrolled with a device
* compatible to the device selected to perform the scan.
* @match_offset: output location to store the array index of the matched
* gallery print (if any was found). Only valid if %FP_VERIFY_MATCH was
* returned.
* Performs a new scan and attempts to identify the scanned finger against a
* collection of previously enrolled fingerprints. This function is just a
* shortcut to calling fp_identify_finger_img() with a %NULL image output
* parameter.
*
* See also fp_identify_finger_img().
*
* Returns: negative code on error, otherwise a code from #fp_verify_result
*/
API_EXPORTED int fp_identify_finger(struct fp_dev *dev,
struct fp_print_data **print_gallery, size_t *match_offset)
{
return fp_identify_finger_img(dev, print_gallery, match_offset, NULL);
}
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;
G_DEBUG_HERE();
*stopped = TRUE;
}
/**
* fp_dev_img_capture:
* @dev: the device
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
* @img: a location to return the captured image. Must be freed with
* fp_img_free() after use.
*
* Captures a #fp_img from a device. The returned image is the raw
* image provided by the device, you may wish to [standardize](libfprint-Image-operations.html#img_std) it.
*
* If set, the @unconditional 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.
*
* See fp_dev_supports_imaging().
*
* Returns: 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.
*/
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;
}

129
meson.build Normal file
View File

@@ -0,0 +1,129 @@
project('libfprint', [ 'c', 'cpp' ],
version: '0.8.0',
license: 'LGPLv2.1+',
default_options: [
'buildtype=debugoptimized',
'warning_level=1',
'c_std=c99',
],
meson_version: '>= 0.45.0')
add_project_arguments([ '-D_GNU_SOURCE' ], language: 'c')
add_project_arguments([ '-DG_LOG_DOMAIN="libfprint"' ], language: 'c')
libfprint_conf = configuration_data()
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
host_system = host_machine.system()
common_cflags = cc.get_supported_arguments([
'-fgnu89-inline',
'-fvisibility=hidden',
'-std=gnu99',
'-Wall',
'-Wundef',
'-Wunused',
'-Wstrict-prototypes',
'-Werror-implicit-function-declaration',
'-Wno-pointer-sign',
'-Wshadow'
])
# maintaining compatibility with the previous libtool versioning
# current = binary - interface
# revision = interface
soversion = 0
current = 0
revision = 0
libversion = '@0@.@1@.@2@'.format(soversion, current, revision)
# Dependencies
glib_dep = dependency('glib-2.0', version: '>= 2.28')
libusb_dep = dependency('libusb-1.0', version: '>= 0.9.1')
mathlib_dep = cc.find_library('m', required: false)
# Drivers
drivers = get_option('drivers').split(',')
all_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan' ]
primitive_drivers = [ 'upekts' ]
if drivers == [ 'all' ]
drivers = all_drivers
endif
nss_dep = []
imaging_dep = []
foreach driver: drivers
if driver == 'uru4000'
nss_dep = dependency('nss', required: false)
if not nss_dep.found()
error('NSS is required for the URU4000/URU4500 driver')
endif
endif
if driver == 'aes3500' or driver == 'aes4000'
imaging_dep = dependency('pixman-1', required: false)
if not imaging_dep.found()
error('pixman is required for imaging support')
endif
endif
if not all_drivers.contains(driver)
error('Invalid driver \'' + driver + '\'')
endif
endforeach
# Export the drivers' structures to the core code
drivers_struct_list = ''
drivers_img_array = 'static struct fp_img_driver * const img_drivers[] = {\n'
drivers_primitive_array = 'static struct fp_driver * const primitive_drivers[] = {\n'
foreach driver: drivers
if primitive_drivers.contains(driver)
drivers_struct_list += 'extern struct fp_driver ' + driver + '_driver;\n'
drivers_primitive_array += ' &' + driver + '_driver,\n'
else
drivers_struct_list += 'extern struct fp_img_driver ' + driver + '_driver;\n'
drivers_img_array += ' &' + driver + '_driver,\n'
endif
endforeach
drivers_img_array += '};'
drivers_primitive_array += '};'
root_inc = include_directories('.')
if get_option('udev_rules')
udev_rules_dir = get_option('udev_rules_dir')
if udev_rules_dir == 'auto'
udev_dep = dependency('udev')
udev_rules_dir = udev_dep.get_pkgconfig_variable('udevdir') + 'rules.d'
endif
endif
if get_option('x11-examples')
x11_dep = cc.find_library('X11')
xv_dep = dependency('xv', required: false)
if not xv_dep.found()
error('XV is required for X11 examples')
endif
endif
libfprint_conf.set('API_EXPORTED', '__attribute__((visibility("default")))')
configure_file(output: 'config.h', configuration: libfprint_conf)
subdir('libfprint')
subdir('examples')
if get_option('doc')
gnome = import('gnome')
subdir('doc')
endif
pkgconfig = import('pkgconfig')
pkgconfig.generate(
name: 'libfprint',
description: 'Generic C API for fingerprint reader access',
version: meson.project_version(),
libraries: libfprint,
subdirs: 'libfprint',
filebase: 'libfprint',
install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
)

20
meson_options.txt Normal file
View File

@@ -0,0 +1,20 @@
option('drivers',
description: 'Drivers to integrate',
type: 'string',
value: 'all')
option('udev_rules',
description: 'Whether to create a udev rules file',
type: 'boolean',
value: true)
option('udev_rules_dir',
description: 'Installation path for udev rules',
type: 'string',
value: 'auto')
option('x11-examples',
description: 'Whether to build X11 example applications',
type: 'boolean',
value: true)
option('doc',
description: 'Whether to build the API documentation',
type: 'boolean',
value: true)