mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2025-11-15 07:38:12 +00:00
Compare commits
162 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74b5c92787 | ||
|
|
ac1f97e2eb | ||
|
|
40f486b108 | ||
|
|
b62e67401c | ||
|
|
dd0a0134a6 | ||
|
|
5e24000799 | ||
|
|
c5cdfcb120 | ||
|
|
549a6694d2 | ||
|
|
14e34e1d15 | ||
|
|
e5393bf46a | ||
|
|
878a201bb1 | ||
|
|
b0e4619e0a | ||
|
|
aec65777a7 | ||
|
|
31bad8ddd2 | ||
|
|
79d65c907f | ||
|
|
92231d984f | ||
|
|
2fbc77955e | ||
|
|
c91819f551 | ||
|
|
b3f6ff5a36 | ||
|
|
52f84bee3c | ||
|
|
dda6857fee | ||
|
|
0215483fb3 | ||
|
|
37bb59df13 | ||
|
|
db34837d2d | ||
|
|
ba49677794 | ||
|
|
1a376c1bfa | ||
|
|
ef807d9d0e | ||
|
|
2a4893d946 | ||
|
|
cee061b363 | ||
|
|
1bbdf304ab | ||
|
|
6155068f9e | ||
|
|
0c4e3bb1c4 | ||
|
|
4547ff0c19 | ||
|
|
b7cce4d91d | ||
|
|
475250ce71 | ||
|
|
f40f231a63 | ||
|
|
d83d92adf2 | ||
|
|
d15282bff1 | ||
|
|
00637c4f0b | ||
|
|
2e035a7f45 | ||
|
|
17577175f8 | ||
|
|
933fab6ed4 | ||
|
|
7e5661caff | ||
|
|
19dfb138a6 | ||
|
|
0930f2614c | ||
|
|
1b20521e5c | ||
|
|
0e351db91a | ||
|
|
9cca501650 | ||
|
|
2481cbe4ab | ||
|
|
bc3959d1e0 | ||
|
|
1ac815e457 | ||
|
|
55b83062d0 | ||
|
|
391f77ce5e | ||
|
|
bc30a3d2e5 | ||
|
|
ff5de4ff03 | ||
|
|
f433a4d67c | ||
|
|
994061af44 | ||
|
|
501020921e | ||
|
|
53c09405b2 | ||
|
|
1f0079a274 | ||
|
|
15afe43cf0 | ||
|
|
eb8f7ba3b2 | ||
|
|
901a6f7fed | ||
|
|
fc92f62136 | ||
|
|
ff09456cf5 | ||
|
|
32fcfde86b | ||
|
|
363a1b3371 | ||
|
|
c376c6fb02 | ||
|
|
8e6e23b8d0 | ||
|
|
63e5d56441 | ||
|
|
e9bfd943fc | ||
|
|
614e2286c2 | ||
|
|
768a74c4bf | ||
|
|
612e9e11de | ||
|
|
06c72d54be | ||
|
|
be68bacc94 | ||
|
|
317d7bc988 | ||
|
|
b44e417bca | ||
|
|
9d67ce484d | ||
|
|
78b8602cf6 | ||
|
|
83a0a7681b | ||
|
|
c5e0e41ce7 | ||
|
|
b3fe4a1e91 | ||
|
|
f59bf389d9 | ||
|
|
231b8f9f92 | ||
|
|
dac153d24a | ||
|
|
3661d146a7 | ||
|
|
3bf55a3e07 | ||
|
|
5226467fc2 | ||
|
|
54deaa1b24 | ||
|
|
7ff667f58d | ||
|
|
58ba9b02ed | ||
|
|
d35da0ce99 | ||
|
|
041e6a1078 | ||
|
|
1c73c36ed7 | ||
|
|
69de32f700 | ||
|
|
4bfee76ead | ||
|
|
4d7afd9b4f | ||
|
|
cfe60c0640 | ||
|
|
d82847a6b4 | ||
|
|
5a7e6e07ff | ||
|
|
2162aa9f48 | ||
|
|
a1f36c71c9 | ||
|
|
e4eedef27e | ||
|
|
5e29695969 | ||
|
|
49a46668ad | ||
|
|
76269decdd | ||
|
|
8454a25ecf | ||
|
|
a0bbbd7d32 | ||
|
|
12f6dae8cd | ||
|
|
9570c36fd4 | ||
|
|
487dae0d2f | ||
|
|
d71018bd8f | ||
|
|
61fa57b05d | ||
|
|
fc1781e317 | ||
|
|
ffef6c2bcc | ||
|
|
67d29f7936 | ||
|
|
9437c98d54 | ||
|
|
f7d00a828d | ||
|
|
0f0a4b2da6 | ||
|
|
c9cdbaf880 | ||
|
|
83f29dad9f | ||
|
|
c2a11c5918 | ||
|
|
3746b2ad5c | ||
|
|
b51fa446e3 | ||
|
|
6fc5293e83 | ||
|
|
aab3daa28b | ||
|
|
e40f7bd1f7 | ||
|
|
6664f87d8f | ||
|
|
6e8d5cd6a1 | ||
|
|
9f7e1ecf40 | ||
|
|
d9567002e4 | ||
|
|
a656a4a9f3 | ||
|
|
2944a35e74 | ||
|
|
391373fb0c | ||
|
|
985e8c4577 | ||
|
|
bd0d4258e4 | ||
|
|
9bbd9b208a | ||
|
|
59fe0fb699 | ||
|
|
f1fdd71613 | ||
|
|
bb66780cb5 | ||
|
|
45fb6d7908 | ||
|
|
9f408bf51b | ||
|
|
ee32166267 | ||
|
|
a3c90f2b24 | ||
|
|
dc537ef2c9 | ||
|
|
061a457658 | ||
|
|
82ae7c1c09 | ||
|
|
79d79c3c87 | ||
|
|
35e356f625 | ||
|
|
948ab02d1a | ||
|
|
a6101026d2 | ||
|
|
e0966cb20f | ||
|
|
e278e8321c | ||
|
|
e1728e7c25 | ||
|
|
e215b05094 | ||
|
|
fe3fdd1f50 | ||
|
|
5ff45658c0 | ||
|
|
d12b294783 | ||
|
|
2bba4fb073 | ||
|
|
aeca32fc12 | ||
|
|
12c1088777 |
20
.gitlab-ci.yml
Normal file
20
.gitlab-ci.yml
Normal 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
96
HACKING
@@ -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
52
HACKING.md
Normal 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
238
INSTALL
@@ -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.
|
||||
|
||||
25
Makefile.am
25
Makefile.am
@@ -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
63
NEWS
@@ -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
6
README
@@ -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
|
||||
|
||||
|
||||
10
autogen.sh
10
autogen.sh
@@ -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
|
||||
372
configure.ac
372
configure.ac
@@ -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
|
||||
|
||||
@@ -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
115
doc/advanced-topics.xml
Normal 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>
|
||||
1294
doc/doxygen.cfg
1294
doc/doxygen.cfg
File diff suppressed because it is too large
Load Diff
30
doc/getting-started.xml
Normal file
30
doc/getting-started.xml
Normal 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
106
doc/intro.xml
Normal 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
53
doc/libfprint-docs.xml
Normal 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
136
doc/libfprint-sections.txt
Normal 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
67
doc/meson.build
Normal 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)
|
||||
8
doc/xml/gtkdocentities.ent.in
Normal file
8
doc/xml/gtkdocentities.ent.in
Normal 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
10
doc/xml/meson.build
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
29
examples/meson.build
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
434
libfprint/assembling.c
Normal 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
65
libfprint/assembling.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
796
libfprint/core.c
796
libfprint/core.c
File diff suppressed because it is too large
Load Diff
358
libfprint/data.c
358
libfprint/data.c
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
183
libfprint/drivers/aes3500.c
Normal 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
163
libfprint/drivers/aes3k.c
Normal 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
58
libfprint/drivers/aes3k.h
Normal 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
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
625
libfprint/drivers/elan.c
Normal 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
176
libfprint/drivers/elan.h
Normal 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
1505
libfprint/drivers/etes603.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
319
libfprint/drivers/upeksonly.h
Normal file
319
libfprint/drivers/upeksonly.h
Normal 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 },
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
701
libfprint/drivers/upektc_img.c
Normal file
701
libfprint/drivers/upektc_img.c
Normal 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,
|
||||
};
|
||||
144
libfprint/drivers/upektc_img.h
Normal file
144
libfprint/drivers/upektc_img.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
791
libfprint/drivers/vfs0050.c
Normal 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
382
libfprint/drivers/vfs0050.h
Normal 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,
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
925
libfprint/drivers/vfs5011.c
Normal 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,
|
||||
};
|
||||
|
||||
6186
libfprint/drivers/vfs5011_proto.h
Normal file
6186
libfprint/drivers/vfs5011_proto.h
Normal file
File diff suppressed because it is too large
Load Diff
304
libfprint/drivers_api.h
Normal file
304
libfprint/drivers_api.h
Normal 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
|
||||
|
||||
@@ -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
1
libfprint/empty_file
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
90
libfprint/fprint-list-supported-devices.c
Normal file
90
libfprint/fprint-list-supported-devices.c
Normal 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;
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
262
libfprint/img.c
262
libfprint/img.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
206
libfprint/meson.build
Normal 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)
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -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 *****/
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
277
libfprint/sync.c
277
libfprint/sync.c
@@ -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
129
meson.build
Normal 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
20
meson_options.txt
Normal 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)
|
||||
Reference in New Issue
Block a user