Compare commits
No commits in common. "openkylin/yangtze" and "pristine-tar" have entirely different histories.
openkylin/
...
pristine-t
113
.ci/check-abi
113
.ci/check-abi
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def format_title(title):
|
||||
box = {
|
||||
'tl': '╔', 'tr': '╗', 'bl': '╚', 'br': '╝', 'h': '═', 'v': '║',
|
||||
}
|
||||
hline = box['h'] * (len(title) + 2)
|
||||
|
||||
return '\n'.join([
|
||||
f"{box['tl']}{hline}{box['tr']}",
|
||||
f"{box['v']} {title} {box['v']}",
|
||||
f"{box['bl']}{hline}{box['br']}",
|
||||
])
|
||||
|
||||
|
||||
def rm_rf(path):
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def sanitize_path(name):
|
||||
return name.replace('/', '-')
|
||||
|
||||
|
||||
def get_current_revision():
|
||||
revision = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
|
||||
encoding='utf-8').strip()
|
||||
|
||||
if revision == 'HEAD':
|
||||
# This is a detached HEAD, get the commit hash
|
||||
revision = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')
|
||||
|
||||
return revision
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def checkout_git_revision(revision):
|
||||
current_revision = get_current_revision()
|
||||
subprocess.check_call(['git', 'checkout', '-q', revision])
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
subprocess.check_call(['git', 'checkout', '-q', current_revision])
|
||||
|
||||
|
||||
def build_install(revision):
|
||||
build_dir = '_build'
|
||||
dest_dir = os.path.abspath(sanitize_path(revision))
|
||||
print(format_title(f'# Building and installing {revision} in {dest_dir}'),
|
||||
end='\n\n', flush=True)
|
||||
|
||||
with checkout_git_revision(revision):
|
||||
rm_rf(build_dir)
|
||||
rm_rf(revision)
|
||||
|
||||
subprocess.check_call(['meson', build_dir,
|
||||
'--prefix=/usr', '--libdir=lib',
|
||||
'-Dx11-examples=false', '-Ddoc=false', '-Dgtk-examples=false'])
|
||||
subprocess.check_call(['ninja', '-v', '-C', build_dir])
|
||||
subprocess.check_call(['ninja', '-v', '-C', build_dir, 'install'],
|
||||
env={'DESTDIR': dest_dir})
|
||||
|
||||
return dest_dir
|
||||
|
||||
|
||||
def compare(old_tree, new_tree):
|
||||
print(format_title(f'# Comparing the two ABIs'), end='\n\n', flush=True)
|
||||
|
||||
old_headers = os.path.join(old_tree, 'usr', 'include')
|
||||
old_lib = os.path.join(old_tree, 'usr', 'lib', 'libfprint.so')
|
||||
|
||||
new_headers = os.path.join(new_tree, 'usr', 'include')
|
||||
new_lib = os.path.join(new_tree, 'usr', 'lib', 'libfprint.so')
|
||||
|
||||
subprocess.check_call([
|
||||
'abidiff', '--headers-dir1', old_headers, '--headers-dir2', new_headers,
|
||||
'--drop-private-types', '--fail-no-debug-info', '--no-added-syms', old_lib, new_lib])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('old', help='the previous revision, considered the reference')
|
||||
parser.add_argument('new', help='the new revision, to compare to the reference')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.old == args.new:
|
||||
print("Let's not waste time comparing something to itself")
|
||||
sys.exit(0)
|
||||
|
||||
old_tree = build_install(args.old)
|
||||
new_tree = build_install(args.new)
|
||||
|
||||
try:
|
||||
compare(old_tree, new_tree)
|
||||
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
print(f'Hurray! {args.old} and {args.new} are ABI-compatible!')
|
11
AUTHORS
11
AUTHORS
|
@ -1,11 +0,0 @@
|
|||
Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
|
||||
Copyright (C) 2006-2007 Timo Hoenig <thoenig@suse.de>
|
||||
Copyright (C) 2006 Pavel Machek <pavel@suse.cz>
|
||||
Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
|
||||
Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
Copyright (C) 2007 Cyrille Bagard
|
||||
Copyright (C) 2007-2008,2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
|
||||
Copyright (C) 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
|
||||
Copyright (C) 2010 Hugo Grostabussiat <dw23.devel@gmail.com>
|
||||
Copyright (C) 2012 Timo Teräs <timo.teras@iki.fi>
|
504
COPYING
504
COPYING
|
@ -1,504 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
73
HACKING.md
73
HACKING.md
|
@ -1,73 +0,0 @@
|
|||
# Contributing to libfprint
|
||||
|
||||
## GLib
|
||||
|
||||
Although the library uses GLib internally, libfprint is designed to provide
|
||||
a completely neutral interface to its application users. So, the public
|
||||
APIs should never return GLib data types.
|
||||
|
||||
## License clarification
|
||||
|
||||
Although this library's license could allow for shims that hook up into
|
||||
proprietary blobs to add driver support for some unsupported devices, the
|
||||
intent of the original authors, and of current maintainers of the library,
|
||||
was for this license to allow _integration into_ proprietary stacks, not
|
||||
_integration of_ proprietary code in the library.
|
||||
|
||||
As such, no code to integrate proprietary drivers will be accepted in libfprint
|
||||
upstream. Proprietary drivers would make it impossible to debug problems in
|
||||
libfprint, as we wouldn't know what the proprietary driver does behind the
|
||||
library's back. The closed source nature of drivers is usually used to hide
|
||||
parts of the hardware setup, such as encryption keys, or protocols, in order
|
||||
to protect the hardware's integrity. Unfortunately, this is only [security through
|
||||
obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity).
|
||||
|
||||
We however encourage potential contributors to take advantage of libfprint's
|
||||
source availability to create such shims to make it easier to reverse-engineer
|
||||
proprietary drivers in order to create new free software drivers, to the extent
|
||||
permitted by local laws.
|
||||
|
||||
## 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.
|
6
INSTALL
6
INSTALL
|
@ -1,6 +0,0 @@
|
|||
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.
|
207
NEWS
207
NEWS
|
@ -1,207 +0,0 @@
|
|||
This file lists notable changes in each release. For the full history of all
|
||||
changes, see ChangeLog.
|
||||
|
||||
2019-08-08: v1.0 release
|
||||
* Library:
|
||||
- Add guards to the public API and require GLib 2.50
|
||||
- Deprecate print storage API
|
||||
- Better documentation for fp_discover_devs()
|
||||
- Remove unused internal fpi_timeout_cancel_for_dev()
|
||||
- Remove state from fp_img_driver activate handler
|
||||
- Bug fixes related to restarting a failed verification immediately
|
||||
|
||||
* Drivers:
|
||||
- The Elan driver received a lot of bug fixes including a fix for a
|
||||
hang when verifying prints with fprintd, quirks for some devices,
|
||||
a memory leak fix and support for 04f3:0c42
|
||||
- Fix a probable crash in all the AES drivers
|
||||
- Add support for Lenovo Preferred Pro Keyboard (KUF1256) to vfs5011
|
||||
- Prevent hang during enroll process in etes603 driver
|
||||
- Fix possible integer overflow in uru4000
|
||||
- Work-around SELinux AVC warnings when uru4000 driver starts
|
||||
- Remove long-unmaintained and broken fdu2000 driver
|
||||
|
||||
* Tools/Examples:
|
||||
- Fix examples not working due to an overly strict check
|
||||
- Fix crash in GTK demo when there's no supported devices
|
||||
- Disable GTK demo until we have a USB Flatpak portal
|
||||
- Remove sleep() in enroll example which caused a crash in some drivers
|
||||
- Add a simple storage implementation example
|
||||
|
||||
2018-12-14: v0.99.0 release
|
||||
* Library:
|
||||
- All the internal API for device driver writers is now covered by the
|
||||
documentation and has been enhanced to make it easier to write drivers
|
||||
- Update internal NBIS fingerprint data processing library to one that's
|
||||
nearly 10 years newer
|
||||
- Re-add accessor for minutia coordinates which was used in the very
|
||||
old fprint_demo program, but also by our new GTK+ test program (see below)
|
||||
- Fix a crash when too many minutiae were detected in a capture
|
||||
|
||||
* Drivers:
|
||||
- Support more devices in the Elan driver, stability improvements
|
||||
|
||||
* Tools:
|
||||
- Add a test GTK+ application that will eventually be used for testing
|
||||
drivers without modifying the OS installed version. Note that this
|
||||
application currently requires manually changing permissions of USB
|
||||
devices, this will be fixed when the infrastructure exists to access
|
||||
those devices without additional permissions, as a normal user.
|
||||
|
||||
2018-07-15: v0.8.2 release
|
||||
* Drivers:
|
||||
- Add USB ID for TNP Nano USB Fingerprint Reader
|
||||
- Fix UPEKTS enrollment never finishing on some devices
|
||||
|
||||
* Library:
|
||||
- Fix fp_get_pollfds() retval type, a small ABI change
|
||||
- Downgrade fatal errors to run-time warnings, as a number of drivers
|
||||
used to throw silent errors and we made them fatal. Those will now
|
||||
be visible warnings, hopefully helping with fixing them.
|
||||
|
||||
2018-06-12: v0.8.1 release
|
||||
- Brown paperbag release to install the udev rules file in the correct
|
||||
directory if the udev pkg-config file doesn't have a trailing slash
|
||||
|
||||
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
|
||||
- Add support for 147e:2020 to upeke2 driver
|
||||
- Fix possible race condition, and cancellation in uru4000 driver
|
||||
|
||||
* Udev rules:
|
||||
- Add Microsoft keyboard to the suspend blacklist
|
||||
|
||||
* Plenty of build fixes
|
||||
|
||||
2012-12-03: v0.5.0 release
|
||||
|
||||
* Drivers:
|
||||
- New VFS300/VFS301 driver
|
||||
- New AES2550/AES2810 drivers
|
||||
- New AES1660 driver
|
||||
- New AES2660 driver
|
||||
- New DigitalPersona URU4500 driver
|
||||
- Avoid empty capture and improve image contrast in the
|
||||
AES2501 and AES2550 drivers
|
||||
- Update upektc driver, add support for Eikon Touch 300
|
||||
- Fix UrU4000 image capture on ARM
|
||||
|
||||
* Library:
|
||||
- Fix global variable collisions with libusb and other system headers
|
||||
- Fix possible crash in NBIS image processing with some fingerprints
|
||||
|
||||
* Udev rules:
|
||||
- Fix power control path for newer kernels
|
||||
- Move udev rules to the correct directory
|
||||
- Don't print duplicated udev rules
|
||||
- Include udev rules in the tarball
|
||||
- Allow disabling of udev rules for cross-compiling
|
||||
- Add driver names in the generated rules
|
||||
|
||||
2011-04-18: v0.4.0 release
|
||||
* Add support for Validity VFS101 (USB ID 138a:0001)
|
||||
* Fix crasher when resizing a fingerprint image
|
||||
* Fix wrong timeout being returned when either of
|
||||
libusb or libfprint doesn't have a timeout
|
||||
|
||||
2010-09-08: v0.3.0 release
|
||||
* Add support for UPEK TCS4C (USB ID 147e:1000)
|
||||
* Use NSS instead of OpenSSL for GPL compliance
|
||||
* upeksonly driver bug fixes
|
||||
* Fix a crash if a scan was shorter than 8 lines
|
||||
* Fix compilation with C++ compiler
|
||||
|
||||
2010-08-19: v0.2.0 release (since 0.1.0-pre2)
|
||||
* Add gdk-pixbuf support for image manipulation
|
||||
* Add udev rules to allow devices to autosuspend
|
||||
* Finish port of AES1610 driver
|
||||
* Add UPEK EikonII (TCRD4C) driver
|
||||
|
||||
2007-12-07: v0.0.5 release
|
||||
* AES1610 imaging improvements
|
||||
* Internal cleanups for Authentec drivers
|
||||
* Add support for latest Microsoft Fingerprint Scanner hardware revision
|
||||
|
||||
2007-11-22: v0.0.4 release
|
||||
* Enable AES1610 driver thanks to Michele B
|
||||
* Implement identification: one-to-many fingerprint matching (Daniel Drake)
|
||||
|
||||
2007-11-19: v0.0.3 release
|
||||
* Add API to access minutiae (Daniel Drake)
|
||||
* Add API to delete enroll data (Daniel Drake)
|
||||
* Add Authentec AES1610 driver (Anthony Bretaudeau)
|
||||
|
||||
2007-11-17: v0.0.2 release
|
||||
* Detect reversed scans on AES2501 (Vasily Khoruzhick)
|
||||
* Improved AES2501 scanning
|
||||
* Compatibility with older ImageMagick versions
|
||||
* Add UPEK TouchChip driver (Jan-Michael Brummer)
|
||||
* Add binarization API
|
||||
|
||||
2007-11-15: v0.0.1 release
|
||||
* Initial release
|
||||
|
51
README
51
README
|
@ -1,51 +0,0 @@
|
|||
libfprint
|
||||
=========
|
||||
|
||||
libfprint is part of the fprint project:
|
||||
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
|
||||
consumer fingerprint scanners and providing a single uniform API to application
|
||||
developers. The ultimate goal of the fprint project is to make fingerprint
|
||||
scanners widely and easily usable under common Linux environments.
|
||||
|
||||
The academic university project runs off a codebase maintained separately
|
||||
from this one, although I try to keep them as similar as possible (I'm not
|
||||
hiding anything in the academic branch, it's just the open source release
|
||||
contains some commits excluded from the academic project).
|
||||
|
||||
THE UNIVERSITY OF MANCHESTER DOES NOT ENDORSE THIS THIS SOFTWARE RELEASE AND
|
||||
IS IN NO WAY RESPONSIBLE FOR THE CODE CONTAINED WITHIN, OR ANY DAMAGES CAUSED
|
||||
BY USING OR DISTRIBUTING THE SOFTWARE. Development does not happen on
|
||||
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:
|
||||
https://fprint.freedesktop.org/
|
||||
|
||||
libfprint is licensed under the GNU LGPL version 2.1. See the COPYING file
|
||||
for the license text.
|
||||
|
||||
Section 6 of the license states that for compiled works that use this
|
||||
library, such works must include libfprint copyright notices alongside the
|
||||
copyright notices for the other parts of the work. We have attempted to
|
||||
make this process slightly easier for you by grouping these all in one place:
|
||||
the AUTHORS file.
|
||||
|
||||
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 https://fprint.freedesktop.org/us-export-control.html
|
||||
|
||||
## Historical links
|
||||
|
||||
Older versions of libfprint are available at:
|
||||
https://sourceforge.net/projects/fprint/files/
|
||||
|
||||
Historical mailing-list archives:
|
||||
http://www.reactivated.net/fprint_list_archives/
|
||||
|
||||
Historical website:
|
||||
http://web.archive.org/web/*/https://www.freedesktop.org/wiki/Software/fprint/
|
11
THANKS
11
THANKS
|
@ -1,11 +0,0 @@
|
|||
Tony Vroon - hardware donations
|
||||
Gerrie Mansur from Security Database BV (http://www.securitydatabase.net/) - hardware donations
|
||||
Joaquin Custodio - hardware donations
|
||||
TimeTrex (http://www.timetrex.com/) - hardware donations
|
||||
Craig Watson (NIST)
|
||||
James Vasile (SFLC)
|
||||
Toby Howard (University of Manchester)
|
||||
Seemant Kulleen
|
||||
Pavel Herrman
|
||||
Bastien Nocera
|
||||
Greg Kerr and Martin Konecny from AuthenTec Inc - hardware donations (AES2550 device), datasheets for AES2550 and AES2810
|
30
TODO
30
TODO
|
@ -1,30 +0,0 @@
|
|||
LIBRARY
|
||||
=======
|
||||
test suite against NFIQ compliance set
|
||||
make library optionally asynchronous and maybe thread-safe
|
||||
nbis cleanups
|
||||
API function to determine if img device supports uncond. capture
|
||||
race-free way of saying "save this print but don't overwrite"
|
||||
|
||||
NEW DRIVERS
|
||||
===========
|
||||
Sunplus 895 driver
|
||||
AES3400/3500 driver
|
||||
ID Mouse driver
|
||||
Support for 2nd generation MS devices
|
||||
Support for 2nd generation UPEK devices
|
||||
|
||||
IMAGING
|
||||
=======
|
||||
ignore first frame or two with aes2501
|
||||
aes2501: increase threshold "sum" for end-of-image detection
|
||||
aes2501 gain calibration
|
||||
aes4000 gain calibration
|
||||
aes4000 resampling
|
||||
PPMM parameter to get_minutiae seems to have no effect
|
||||
nbis minutiae should be stored in endian-independent format
|
||||
|
||||
PORTABILITY
|
||||
===========
|
||||
OpenBSD can't do -Wshadow or visibility
|
||||
OpenBSD: add compat codes for ENOTSUP ENODATA and EPROTO
|
|
@ -1,3 +0,0 @@
|
|||
This project and its community follow the [Freedesktop.org code of conduct]
|
||||
|
||||
[Freedesktop.org code of conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/
|
|
@ -1,19 +0,0 @@
|
|||
The libfprint0 postinst script calls udevadmin trigger for all the supported
|
||||
USB readers. The list of readers used in that file is generated by calling the
|
||||
following awk script:
|
||||
|
||||
/plugdev/ {
|
||||
printf ("\tudevadm trigger --action=add " )
|
||||
for (i=1;i<=NF; i++){
|
||||
if (match($i,/idVendor/)>0) {
|
||||
match($i, /"[^"]*"/); printf ("--attr-match=idVendor=%s ",substr($i,RSTART+1,RLENGTH-2))
|
||||
}
|
||||
if (match($i,/idProduct/)>0) {
|
||||
match($i, /"[^"]*"/); printf ("--attr-match=idProduct=%s",substr($i,RSTART+1,RLENGTH-2))
|
||||
}
|
||||
};
|
||||
printf("\n")
|
||||
}
|
||||
|
||||
When preparing a new upstream release, this script should be called against the
|
||||
udev .rules file generated during the build of the package
|
|
@ -1,5 +0,0 @@
|
|||
libfprint (1:1.0-ok1) yangtze; urgency=medium
|
||||
|
||||
* build for openKylin.
|
||||
|
||||
-- openKylinBot <openKylinBot@openkylin.top> Tue, 07 Jun 2022 14:13:26 +0800
|
|
@ -1 +0,0 @@
|
|||
10
|
|
@ -1,83 +0,0 @@
|
|||
Source: libfprint
|
||||
Priority: optional
|
||||
Section: libs
|
||||
Maintainer: FingerForce Team <fingerforce-devel@lists.alioth.debian.org>
|
||||
Uploaders: Ulises Vitulli <dererk@debian.org>, Didier Raboud <odyx@debian.org>
|
||||
Build-Depends: debhelper (>= 10.3),
|
||||
gtk-doc-tools,
|
||||
libglib2.0-dev (>= 2.50),
|
||||
libnss3-dev,
|
||||
libpixman-1-dev,
|
||||
libusb-1.0-0-dev,
|
||||
libxv-dev,
|
||||
meson
|
||||
Build-Depends-Indep: libglib2.0-doc
|
||||
Standards-Version: 4.4.0
|
||||
Homepage: https://www.freedesktop.org/wiki/Software/fprint/libfprint
|
||||
Vcs-Browser: https://salsa.debian.org/debian/libfprint
|
||||
Vcs-Git: https://salsa.debian.org/debian/libfprint.git
|
||||
|
||||
Package: libfprint-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Depends: libfprint0 (= ${binary:Version}), ${misc:Depends}
|
||||
Suggests: libfprint-doc
|
||||
Description: async fingerprint library of fprint project, development headers
|
||||
The fprint project aims to support for consumer fingerprint reader devices.
|
||||
.
|
||||
Previously, Linux support for such devices has been scattered amongst different
|
||||
projects (many incomplete) and inconsistent in that application developers
|
||||
would have to implement support for each type of fingerprint reader separately.
|
||||
The idea is to change that by providing a central system to support all the
|
||||
fingerprint readers as it's possible to get hands on.
|
||||
.
|
||||
libfprint is the centre of efforts, this component does the dirty job of
|
||||
talking to fingerprint reading devices, and processing fingerprint data.
|
||||
.
|
||||
This library depends on the new libusb, which performs asynchronous callbacks,
|
||||
allowing fprint to perform non-blocking device tasks.
|
||||
.
|
||||
This package provides development headers.
|
||||
|
||||
Package: libfprint0
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: async fingerprint library of fprint project, shared libraries
|
||||
The fprint project aims to support for consumer fingerprint reader devices.
|
||||
.
|
||||
Previously, Linux support for such devices has been scattered amongst different
|
||||
projects (many incomplete) and inconsistent in that application developers
|
||||
would have to implement support for each type of fingerprint reader separately.
|
||||
The idea is to change that by providing a central system to support all the
|
||||
fingerprint readers as it's possible to get hands on.
|
||||
.
|
||||
libfprint is the centre of efforts, this component does the dirty job of
|
||||
talking to fingerprint reading devices, and processing fingerprint data.
|
||||
.
|
||||
This library depends on the new libusb, which performs asynchronous callbacks,
|
||||
allowing fprint to perform non-blocking device tasks.
|
||||
.
|
||||
This package provides shared libraries.
|
||||
|
||||
Package: libfprint-doc
|
||||
Section: doc
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}
|
||||
Description: async fingerprint library of fprint project, API documentation
|
||||
The fprint project aims to support for consumer fingerprint reader devices.
|
||||
.
|
||||
Previously, Linux support for such devices has been scattered amongst different
|
||||
projects (many incomplete) and inconsistent in that application developers
|
||||
would have to implement support for each type of fingerprint reader separately.
|
||||
The idea is to change that by providing a central system to support all the
|
||||
fingerprint readers as it's possible to get hands on.
|
||||
.
|
||||
libfprint is the centre of efforts, this component does the dirty job of
|
||||
talking to fingerprint reading devices, and processing fingerprint data.
|
||||
.
|
||||
This library depends on the new libusb, which performs asynchronous callbacks,
|
||||
allowing fprint to perform non-blocking device tasks.
|
||||
.
|
||||
This package provides the API documentation.
|
|
@ -1,45 +0,0 @@
|
|||
This package was debianized by Ulises Vitulli <uvitulli@fi.uba.ar> on
|
||||
Fri, 19 Sep 2008 05:14:30 -0300.
|
||||
|
||||
It was downloaded from <http://reactivated.net/fprint/wiki/Libfprint>
|
||||
|
||||
Upstream Authors, and Copyright:
|
||||
|
||||
Copyright © 2007 Daniel Drake <dsd@gentoo.org>
|
||||
Copyright © 2006-2007 Timo Hoenig <thoenig@suse.de>
|
||||
Copyright © 2006 Pavel Machek <pavel@suse.cz>
|
||||
Copyright © 1999 Erik Walthinsen <omega@cse.ogi.edu>
|
||||
Copyright © 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
Copyright © 2007 Cyrille Bagard
|
||||
Copyright © 2007 Vasily Khoruzhick
|
||||
Copyright © 2007 Jan-Michael Brummer <buzz2@gmx.de>
|
||||
Copyright © 2007 Anthony Bretaudeau <wxcover@users.sourceforge.net>
|
||||
|
||||
|
||||
License:
|
||||
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
|
||||
|
||||
The files under libfprint/nbis directory has this license:
|
||||
|
||||
This software was developed at the National Institute of Standards and
|
||||
Technology (NIST) by employees of the Federal Government in the course
|
||||
of their official duties. Pursuant to title 17 Section 105 of the
|
||||
United States Code, this software is not subject to copyright protection
|
||||
and is in the public domain. NIST assumes no responsibility whatsoever for
|
||||
its use by other parties, and makes no guarantees, expressed or implied,
|
||||
about its quality, reliability, or any other characteristic.
|
||||
|
||||
The Debian packaging is © 2008, Ulises Vitulli <uvitulli@fi.uba.ar> and is
|
||||
licensed under the LGPL v2.1, see `/usr/share/common-licenses/LGPL-2.1'.
|
|
@ -1,3 +0,0 @@
|
|||
NEWS
|
||||
README
|
||||
TODO
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
pristine-tar = True
|
||||
debian-branch = debian
|
||||
upstream-vcs-tag = V_%(version%.%_)s
|
|
@ -1,3 +0,0 @@
|
|||
usr/include/*
|
||||
usr/lib/*/lib*.so
|
||||
usr/lib/*/pkgconfig/*
|
|
@ -1 +0,0 @@
|
|||
usr/share/gtk-doc/
|
|
@ -1,2 +0,0 @@
|
|||
lib/udev/rules.d/
|
||||
usr/lib/*/lib*.so.*
|
|
@ -1,3 +0,0 @@
|
|||
# For security reasons, we don't want the fingerprint readers to be accessible
|
||||
# by unprivileged users
|
||||
udev-rule-missing-uaccess
|
|
@ -1,139 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
if [ "$1" = "configure" -o "$1" = "upgrade" ] && command -V udevadm >/dev/null 2>&1; then
|
||||
# apply udev rules at package installation, see
|
||||
# <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683863#27>
|
||||
# NOTA BENE: after the DEBHELPER section since dh_installudev
|
||||
# adds stuff there
|
||||
udevadm trigger --action=add --attr-match=idVendor=0483 --attr-match=idProduct=2016
|
||||
udevadm trigger --action=add --attr-match=idVendor=0483 --attr-match=idProduct=2015
|
||||
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=3001
|
||||
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=2016
|
||||
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=1000
|
||||
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=1001
|
||||
udevadm trigger --action=add --attr-match=idVendor=061a --attr-match=idProduct=0110
|
||||
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bb
|
||||
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bc
|
||||
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00bd
|
||||
udevadm trigger --action=add --attr-match=idVendor=045e --attr-match=idProduct=00ca
|
||||
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=0007
|
||||
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=0008
|
||||
udevadm trigger --action=add --attr-match=idVendor=05ba --attr-match=idProduct=000a
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1600
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1660
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1680
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1681
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1682
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1683
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1684
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1685
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1686
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1687
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1688
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=1689
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168a
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168b
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168c
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168d
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168e
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=168f
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2500
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2580
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2550
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2810
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2660
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2680
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2681
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2682
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2683
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2684
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2685
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2686
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2687
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2688
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2689
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268a
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268b
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268c
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268d
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268e
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=268f
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=2691
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=5731
|
||||
udevadm trigger --action=add --attr-match=idVendor=08ff --attr-match=idProduct=5501
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0001
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0005
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0008
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0010
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0011
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0015
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0017
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0018
|
||||
udevadm trigger --action=add --attr-match=idVendor=147e --attr-match=idProduct=2020
|
||||
udevadm trigger --action=add --attr-match=idVendor=1c7a --attr-match=idProduct=0603
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0050
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0903
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0907
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c01
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c02
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c03
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c04
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c05
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c06
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c07
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c08
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c09
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0a
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0b
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0c
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0d
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0e
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c0f
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c10
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c11
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c12
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c13
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c14
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c15
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c16
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c17
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c18
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c19
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1a
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1b
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1c
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1d
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1e
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c1f
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c20
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c21
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c22
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c23
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c24
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c25
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c26
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c27
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c28
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c29
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2a
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2b
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2c
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2d
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2e
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c2f
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c30
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c31
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c32
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c33
|
||||
udevadm trigger --action=add --attr-match=idVendor=04f3 --attr-match=idProduct=0c42
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0090
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0091
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0094
|
||||
udevadm trigger --action=add --attr-match=idVendor=138a --attr-match=idProduct=0097
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -1,77 +0,0 @@
|
|||
libfprint.so.0 libfprint0 #MINVER#
|
||||
* Build-Depends-Package: libfprint-dev
|
||||
fp_async_capture_start@Base 1:0.6.0
|
||||
fp_async_capture_stop@Base 1:0.6.0
|
||||
fp_async_dev_close@Base 1:0.4.0
|
||||
fp_async_dev_open@Base 1:0.4.0
|
||||
fp_async_enroll_start@Base 1:0.4.0
|
||||
fp_async_enroll_stop@Base 1:0.4.0
|
||||
fp_async_identify_start@Base 1:0.4.0
|
||||
fp_async_identify_stop@Base 1:0.4.0
|
||||
fp_async_verify_start@Base 1:0.4.0
|
||||
fp_async_verify_stop@Base 1:0.4.0
|
||||
fp_dev_close@Base 1:0.4.0
|
||||
fp_dev_get_devtype@Base 1:0.4.0
|
||||
fp_dev_get_driver@Base 1:0.4.0
|
||||
fp_dev_get_img_height@Base 1:0.4.0
|
||||
fp_dev_get_img_width@Base 1:0.4.0
|
||||
fp_dev_get_nr_enroll_stages@Base 1:0.4.0
|
||||
fp_dev_img_capture@Base 1:0.4.0
|
||||
fp_dev_open@Base 1:0.4.0
|
||||
fp_dev_supports_dscv_print@Base 1:0.4.0
|
||||
fp_dev_supports_identification@Base 1:0.4.0
|
||||
fp_dev_supports_imaging@Base 1:0.4.0
|
||||
fp_dev_supports_print_data@Base 1:0.4.0
|
||||
fp_discover_devs@Base 1:0.4.0
|
||||
fp_discover_prints@Base 1:0.4.0
|
||||
fp_driver_get_driver_id@Base 1:0.4.0
|
||||
fp_driver_get_full_name@Base 1:0.4.0
|
||||
fp_driver_get_name@Base 1:0.4.0
|
||||
fp_driver_get_scan_type@Base 1:0.4.0
|
||||
fp_driver_supports_imaging@Base 1:0.99.0
|
||||
fp_dscv_dev_for_dscv_print@Base 1:0.4.0
|
||||
fp_dscv_dev_for_print_data@Base 1:0.4.0
|
||||
fp_dscv_dev_get_devtype@Base 1:0.4.0
|
||||
fp_dscv_dev_get_driver@Base 1:0.4.0
|
||||
fp_dscv_dev_get_driver_id@Base 1:0.8.1
|
||||
fp_dscv_dev_supports_dscv_print@Base 1:0.4.0
|
||||
fp_dscv_dev_supports_print_data@Base 1:0.4.0
|
||||
fp_dscv_devs_free@Base 1:0.4.0
|
||||
fp_dscv_print_delete@Base 1:0.4.0
|
||||
fp_dscv_print_get_devtype@Base 1:0.4.0
|
||||
fp_dscv_print_get_driver_id@Base 1:0.4.0
|
||||
fp_dscv_print_get_finger@Base 1:0.4.0
|
||||
fp_dscv_prints_free@Base 1:0.4.0
|
||||
fp_enroll_finger@Base 1:0.8.1
|
||||
fp_enroll_finger_img@Base 1:0.4.0
|
||||
fp_exit@Base 1:0.4.0
|
||||
fp_get_next_timeout@Base 1:0.4.0
|
||||
fp_get_pollfds@Base 1:0.4.0
|
||||
fp_handle_events@Base 1:0.4.0
|
||||
fp_handle_events_timeout@Base 1:0.4.0
|
||||
fp_identify_finger@Base 1:0.8.1
|
||||
fp_identify_finger_img@Base 1:0.4.0
|
||||
fp_img_binarize@Base 1:0.4.0
|
||||
fp_img_free@Base 1:0.4.0
|
||||
fp_img_get_data@Base 1:0.4.0
|
||||
fp_img_get_height@Base 1:0.4.0
|
||||
fp_img_get_minutiae@Base 1:0.4.0
|
||||
fp_img_get_width@Base 1:0.4.0
|
||||
fp_img_save_to_file@Base 1:0.4.0
|
||||
fp_img_standardize@Base 1:0.4.0
|
||||
fp_init@Base 1:0.4.0
|
||||
fp_minutia_get_coords@Base 1:0.8.2-3~
|
||||
fp_print_data_delete@Base 1:0.4.0
|
||||
fp_print_data_free@Base 1:0.4.0
|
||||
fp_print_data_from_data@Base 1:0.4.0
|
||||
fp_print_data_from_dscv_print@Base 1:0.4.0
|
||||
fp_print_data_get_data@Base 1:0.4.0
|
||||
fp_print_data_get_devtype@Base 1:0.4.0
|
||||
fp_print_data_get_driver_id@Base 1:0.4.0
|
||||
fp_print_data_load@Base 1:0.4.0
|
||||
fp_print_data_save@Base 1:0.4.0
|
||||
fp_set_debug@Base 1:0.4.0
|
||||
fp_set_pollfd_notifiers@Base 1:0.4.0
|
||||
fp_verify_finger@Base 1:0.8.1
|
||||
fp_verify_finger_img@Base 1:0.4.0
|
||||
fprint_get_drivers@Base 1:0.4.0
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
# Configuration arguments
|
||||
CONFIG_ARGS = -Dudev_rules_dir=/lib/udev/rules.d -Dgtk-examples=false
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- $(CONFIG_ARGS)
|
||||
|
||||
override_dh_install:
|
||||
mv debian/tmp/lib/udev/rules.d/60-fprint-autosuspend.rules \
|
||||
debian/tmp/lib/udev/rules.d/60-libfprint0.rules
|
||||
sed -i '/^Requires.private\|^Libs.private/ d' \
|
||||
debian/tmp/usr/lib/*/pkgconfig/libfprint.pc
|
||||
dh_install
|
|
@ -1 +0,0 @@
|
|||
3.0 (native)
|
|
@ -1,4 +0,0 @@
|
|||
version=4
|
||||
|
||||
https://gitlab.freedesktop.org/libfprint/libfprint/tags?sort=updated_desc \
|
||||
/libfprint/libfprint/uploads/(?:[^/]*)/libfprint-(.*).tar.xz
|
|
@ -1,523 +0,0 @@
|
|||
/*
|
||||
* Example libfprint GTK+ image capture program
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
#include "loop.h"
|
||||
|
||||
typedef GtkApplication LibfprintDemo;
|
||||
typedef GtkApplicationClass LibfprintDemoClass;
|
||||
|
||||
G_DEFINE_TYPE (LibfprintDemo, libfprint_demo, GTK_TYPE_APPLICATION)
|
||||
|
||||
typedef enum {
|
||||
IMAGE_DISPLAY_NONE = 0,
|
||||
IMAGE_DISPLAY_MINUTIAE = 1 << 0,
|
||||
IMAGE_DISPLAY_BINARY = 1 << 1
|
||||
} ImageDisplayFlags;
|
||||
|
||||
typedef struct {
|
||||
GtkApplicationWindow parent_instance;
|
||||
|
||||
GtkWidget *header_bar;
|
||||
GtkWidget *mode_stack;
|
||||
GtkWidget *capture_button;
|
||||
GtkWidget *capture_image;
|
||||
GtkWidget *spinner;
|
||||
GtkWidget *instructions;
|
||||
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dev *dev;
|
||||
|
||||
struct fp_img *img;
|
||||
ImageDisplayFlags img_flags;
|
||||
} LibfprintDemoWindow;
|
||||
|
||||
typedef GtkApplicationWindowClass LibfprintDemoWindowClass;
|
||||
|
||||
G_DEFINE_TYPE (LibfprintDemoWindow, libfprint_demo_window, GTK_TYPE_APPLICATION_WINDOW)
|
||||
|
||||
typedef enum {
|
||||
EMPTY_MODE,
|
||||
NOIMAGING_MODE,
|
||||
CAPTURE_MODE,
|
||||
SPINNER_MODE,
|
||||
ERROR_MODE
|
||||
} LibfprintDemoMode;
|
||||
|
||||
static void libfprint_demo_set_mode (LibfprintDemoWindow *win,
|
||||
LibfprintDemoMode mode);
|
||||
|
||||
static void
|
||||
pixbuf_destroy (guchar *pixels, gpointer data)
|
||||
{
|
||||
if (pixels == NULL)
|
||||
return;
|
||||
g_free (pixels);
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
img_to_rgbdata (struct fp_img *img,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
int size = width * height;
|
||||
unsigned char *imgdata = fp_img_get_data (img);
|
||||
unsigned char *rgbdata = g_malloc (size * 3);
|
||||
size_t i;
|
||||
size_t rgb_offset = 0;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned char pixel = imgdata[i];
|
||||
|
||||
rgbdata[rgb_offset++] = pixel;
|
||||
rgbdata[rgb_offset++] = pixel;
|
||||
rgbdata[rgb_offset++] = pixel;
|
||||
}
|
||||
|
||||
return rgbdata;
|
||||
}
|
||||
|
||||
static void
|
||||
plot_minutiae (unsigned char *rgbdata,
|
||||
int width,
|
||||
int height,
|
||||
struct fp_minutia **minlist,
|
||||
int nr_minutiae)
|
||||
{
|
||||
int i;
|
||||
#define write_pixel(num) do { \
|
||||
rgbdata[((num) * 3)] = 0xff; \
|
||||
rgbdata[((num) * 3) + 1] = 0; \
|
||||
rgbdata[((num) * 3) + 2] = 0; \
|
||||
} while(0)
|
||||
|
||||
for (i = 0; i < nr_minutiae; i++) {
|
||||
struct fp_minutia *min = minlist[i];
|
||||
int x, y;
|
||||
size_t pixel_offset;
|
||||
|
||||
fp_minutia_get_coords(min, &x, &y);
|
||||
pixel_offset = (y * width) + x;
|
||||
write_pixel(pixel_offset - 2);
|
||||
write_pixel(pixel_offset - 1);
|
||||
write_pixel(pixel_offset);
|
||||
write_pixel(pixel_offset + 1);
|
||||
write_pixel(pixel_offset + 2);
|
||||
|
||||
write_pixel(pixel_offset - (width * 2));
|
||||
write_pixel(pixel_offset - (width * 1) - 1);
|
||||
write_pixel(pixel_offset - (width * 1));
|
||||
write_pixel(pixel_offset - (width * 1) + 1);
|
||||
write_pixel(pixel_offset + (width * 1) - 1);
|
||||
write_pixel(pixel_offset + (width * 1));
|
||||
write_pixel(pixel_offset + (width * 1) + 1);
|
||||
write_pixel(pixel_offset + (width * 2));
|
||||
}
|
||||
}
|
||||
|
||||
static GdkPixbuf *
|
||||
img_to_pixbuf (struct fp_img *img,
|
||||
ImageDisplayFlags flags)
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
unsigned char *rgbdata;
|
||||
|
||||
width = fp_img_get_width (img);
|
||||
height = fp_img_get_height (img);
|
||||
|
||||
if (flags & IMAGE_DISPLAY_BINARY) {
|
||||
struct fp_img *binary;
|
||||
binary = fp_img_binarize (img);
|
||||
rgbdata = img_to_rgbdata (binary, width, height);
|
||||
fp_img_free (binary);
|
||||
} else {
|
||||
rgbdata = img_to_rgbdata (img, width, height);
|
||||
}
|
||||
|
||||
if (flags & IMAGE_DISPLAY_MINUTIAE) {
|
||||
struct fp_minutia **minlist;
|
||||
int nr_minutiae;
|
||||
|
||||
minlist = fp_img_get_minutiae (img, &nr_minutiae);
|
||||
plot_minutiae (rgbdata, width, height, minlist, nr_minutiae);
|
||||
}
|
||||
|
||||
return gdk_pixbuf_new_from_data (rgbdata, GDK_COLORSPACE_RGB,
|
||||
FALSE, 8, width, height,
|
||||
width * 3, pixbuf_destroy,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
update_image (LibfprintDemoWindow *win)
|
||||
{
|
||||
GdkPixbuf *pixbuf;
|
||||
|
||||
if (win->img == NULL) {
|
||||
gtk_image_clear (GTK_IMAGE (win->capture_image));
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("Updating image, minutiae %s, binary mode %s",
|
||||
win->img_flags & IMAGE_DISPLAY_MINUTIAE ? "shown" : "hidden",
|
||||
win->img_flags & IMAGE_DISPLAY_BINARY ? "on" : "off");
|
||||
pixbuf = img_to_pixbuf (win->img, win->img_flags);
|
||||
gtk_image_set_from_pixbuf (GTK_IMAGE (win->capture_image), pixbuf);
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_set_spinner_label (LibfprintDemoWindow *win,
|
||||
const char *message)
|
||||
{
|
||||
char *label;
|
||||
|
||||
label = g_strdup_printf ("<b><span size=\"large\">%s</span></b>", message);
|
||||
gtk_label_set_markup (GTK_LABEL (win->instructions), label);
|
||||
g_free (label);
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_set_capture_label (LibfprintDemoWindow *win)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
enum fp_scan_type scan_type;
|
||||
const char *message;
|
||||
|
||||
drv = fp_dscv_dev_get_driver (win->ddev);
|
||||
scan_type = fp_driver_get_scan_type(drv);
|
||||
|
||||
switch (scan_type) {
|
||||
case FP_SCAN_TYPE_PRESS:
|
||||
message = "Place your finger on the fingerprint reader";
|
||||
break;
|
||||
case FP_SCAN_TYPE_SWIPE:
|
||||
message = "Swipe your finger across the fingerprint reader";
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
libfprint_demo_set_spinner_label (win, message);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_capture_start_cb (struct fp_dev *dev,
|
||||
int result,
|
||||
struct fp_img *img,
|
||||
void *user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
|
||||
if (result < 0) {
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
fp_async_capture_stop (dev, NULL, NULL);
|
||||
|
||||
win->img = img;
|
||||
update_image (win);
|
||||
|
||||
libfprint_demo_set_mode (win, CAPTURE_MODE);
|
||||
}
|
||||
|
||||
static void
|
||||
dev_open_cb (struct fp_dev *dev, int status, void *user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
int r;
|
||||
|
||||
if (status < 0) {
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
libfprint_demo_set_capture_label (win);
|
||||
|
||||
win->dev = dev;
|
||||
r = fp_async_capture_start (win->dev, FALSE, dev_capture_start_cb, user_data);
|
||||
if (r < 0) {
|
||||
g_warning ("fp_async_capture_start failed: %d", r);
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
activate_capture (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
int r;
|
||||
|
||||
libfprint_demo_set_mode (win, SPINNER_MODE);
|
||||
g_clear_pointer (&win->img, fp_img_free);
|
||||
|
||||
if (win->dev != NULL) {
|
||||
dev_open_cb (win->dev, 0, user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
libfprint_demo_set_spinner_label (win, "Opening fingerprint reader");
|
||||
|
||||
r = fp_async_dev_open (win->ddev, dev_open_cb, user_data);
|
||||
if (r < 0) {
|
||||
g_warning ("fp_async_dev_open failed: %d", r);
|
||||
libfprint_demo_set_mode (win, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
activate_quit (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkApplication *app = user_data;
|
||||
GtkWidget *win;
|
||||
GList *list, *next;
|
||||
|
||||
list = gtk_application_get_windows (app);
|
||||
while (list)
|
||||
{
|
||||
win = list->data;
|
||||
next = list->next;
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (win));
|
||||
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
activate_show_minutiae (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
GVariant *state;
|
||||
gboolean new_state;
|
||||
|
||||
state = g_action_get_state (G_ACTION (action));
|
||||
new_state = !g_variant_get_boolean (state);
|
||||
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
|
||||
g_variant_unref (state);
|
||||
|
||||
if (new_state)
|
||||
win->img_flags |= IMAGE_DISPLAY_MINUTIAE;
|
||||
else
|
||||
win->img_flags &= ~IMAGE_DISPLAY_MINUTIAE;
|
||||
|
||||
update_image (win);
|
||||
}
|
||||
|
||||
static void
|
||||
activate_show_binary (GSimpleAction *action,
|
||||
GVariant *parameter,
|
||||
gpointer user_data)
|
||||
{
|
||||
LibfprintDemoWindow *win = user_data;
|
||||
GVariant *state;
|
||||
gboolean new_state;
|
||||
|
||||
state = g_action_get_state (G_ACTION (action));
|
||||
new_state = !g_variant_get_boolean (state);
|
||||
g_action_change_state (G_ACTION (action), g_variant_new_boolean (new_state));
|
||||
g_variant_unref (state);
|
||||
|
||||
if (new_state)
|
||||
win->img_flags |= IMAGE_DISPLAY_BINARY;
|
||||
else
|
||||
win->img_flags &= ~IMAGE_DISPLAY_BINARY;
|
||||
|
||||
update_image (win);
|
||||
}
|
||||
|
||||
static void
|
||||
change_show_minutiae_state (GSimpleAction *action,
|
||||
GVariant *state,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_simple_action_set_state (action, state);
|
||||
}
|
||||
|
||||
static void
|
||||
change_show_binary_state (GSimpleAction *action,
|
||||
GVariant *state,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_simple_action_set_state (action, state);
|
||||
}
|
||||
|
||||
static GActionEntry app_entries[] = {
|
||||
{ "quit", activate_quit, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static GActionEntry win_entries[] = {
|
||||
{ "show-minutiae", activate_show_minutiae, NULL, "false", change_show_minutiae_state },
|
||||
{ "show-binary", activate_show_binary, NULL, "false", change_show_binary_state },
|
||||
{ "capture", activate_capture, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
activate (GApplication *app)
|
||||
{
|
||||
LibfprintDemoWindow *window;
|
||||
|
||||
window = g_object_new (libfprint_demo_window_get_type (),
|
||||
"application", app,
|
||||
NULL);
|
||||
gtk_widget_show (GTK_WIDGET (window));
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_set_mode (LibfprintDemoWindow *win,
|
||||
LibfprintDemoMode mode)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
char *title;
|
||||
|
||||
switch (mode) {
|
||||
case EMPTY_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "empty-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, FALSE);
|
||||
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
case NOIMAGING_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "noimaging-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, FALSE);
|
||||
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
case CAPTURE_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "capture-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, TRUE);
|
||||
|
||||
drv = fp_dscv_dev_get_driver (win->ddev);
|
||||
title = g_strdup_printf ("%s Test", fp_driver_get_full_name (drv));
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (win->header_bar), title);
|
||||
g_free (title);
|
||||
|
||||
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
case SPINNER_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "spinner-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, FALSE);
|
||||
gtk_spinner_start (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
case ERROR_MODE:
|
||||
gtk_stack_set_visible_child_name (GTK_STACK (win->mode_stack), "error-mode");
|
||||
gtk_widget_set_sensitive (win->capture_button, FALSE);
|
||||
gtk_spinner_stop (GTK_SPINNER (win->spinner));
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_init (LibfprintDemo *app)
|
||||
{
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (app),
|
||||
app_entries, G_N_ELEMENTS (app_entries),
|
||||
app);
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_class_init (LibfprintDemoClass *class)
|
||||
{
|
||||
GApplicationClass *app_class = G_APPLICATION_CLASS (class);
|
||||
|
||||
app_class->activate = activate;
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_window_init (LibfprintDemoWindow *window)
|
||||
{
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (window));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 700, 500);
|
||||
|
||||
g_action_map_add_action_entries (G_ACTION_MAP (window),
|
||||
win_entries, G_N_ELEMENTS (win_entries),
|
||||
window);
|
||||
|
||||
if (fp_init () < 0) {
|
||||
libfprint_demo_set_mode (window, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
setup_pollfds ();
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
libfprint_demo_set_mode (window, ERROR_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Empty list? */
|
||||
if (discovered_devs[0] == NULL) {
|
||||
fp_dscv_devs_free (discovered_devs);
|
||||
libfprint_demo_set_mode (window, EMPTY_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fp_driver_supports_imaging(fp_dscv_dev_get_driver(discovered_devs[0]))) {
|
||||
libfprint_demo_set_mode (window, NOIMAGING_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
window->ddev = discovered_devs[0];
|
||||
libfprint_demo_set_mode (window, CAPTURE_MODE);
|
||||
}
|
||||
|
||||
static void
|
||||
libfprint_demo_window_class_init (LibfprintDemoWindowClass *class)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/libfprint_demo/gtk-libfprint-test.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, header_bar);
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, mode_stack);
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, capture_image);
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, spinner);
|
||||
gtk_widget_class_bind_template_child (widget_class, LibfprintDemoWindow, instructions);
|
||||
|
||||
//FIXME setup dispose
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
GtkApplication *app;
|
||||
|
||||
app = GTK_APPLICATION (g_object_new (libfprint_demo_get_type (),
|
||||
"application-id", "org.freedesktop.libfprint.Demo",
|
||||
"flags", G_APPLICATION_FLAGS_NONE,
|
||||
NULL));
|
||||
|
||||
return g_application_run (G_APPLICATION (app), 0, NULL);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/libfprint_demo">
|
||||
<file>gtk-libfprint-test.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
|
@ -1,351 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<template class="LibfprintDemoWindow" parent="GtkApplicationWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="default_width">500</property>
|
||||
<property name="default_height">400</property>
|
||||
<property name="show_menubar">False</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="header_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title">Fingerprint Reader Test</property>
|
||||
<property name="subtitle">Capture test</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="option-menu-button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="menu-model">options-menu</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">open-menu-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="capture_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="action_name">win.capture</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="capture_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Capture</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="text-button"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="mode_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="spinner-mode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="row_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">9</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="instructions">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">9</property>
|
||||
<property name="label" translatable="yes"><b><span size="large">Please press finger on reader</span></b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">spinner-mode</property>
|
||||
<property name="title">spinner-mode</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="empty-mode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="row_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">9</property>
|
||||
<property name="pixel_size">192</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">9</property>
|
||||
<property name="label" translatable="yes"><b><span size="large">No fingerprint readers found</span></b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Please connect a supported fingerprint reader and start the application again</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">empty-mode</property>
|
||||
<property name="title">empty-mode</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAspectFrame" id="capture-mode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<property name="ratio">1.2999999523162842</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="capture_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">capture-mode</property>
|
||||
<property name="title">capture-mode</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="error-mode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="row_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">9</property>
|
||||
<property name="pixel_size">192</property>
|
||||
<property name="icon_name">dialog-warning-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">9</property>
|
||||
<property name="label" translatable="yes"><b><span size="large">An error occurred trying to access the fingerprint reader</span></b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Please consult the debugging output before restarting the application</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">error-mode</property>
|
||||
<property name="title">error-mode</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid" id="noimaging-mode">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="row_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">9</property>
|
||||
<property name="pixel_size">192</property>
|
||||
<property name="icon_name">scanner-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">9</property>
|
||||
<property name="label" translatable="yes"><b><span size="large">Fingerprint reader does not support capturing images</span></b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Please connect a supported fingerprint reader and start the application again</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">noimaging-mode</property>
|
||||
<property name="title">noimaging-mode</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
||||
<menu id="options-menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Show Minutiae</attribute>
|
||||
<attribute name="action">win.show-minutiae</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Show Binary</attribute>
|
||||
<attribute name="action">win.show-binary</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
</interface>
|
196
demo/loop.c
196
demo/loop.c
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* fprint D-Bus daemon
|
||||
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "loop.h"
|
||||
|
||||
struct fdsource {
|
||||
GSource source;
|
||||
GSList *pollfds;
|
||||
};
|
||||
|
||||
static gboolean source_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
int r;
|
||||
struct timeval tv;
|
||||
|
||||
r = fp_get_next_timeout(&tv);
|
||||
if (r == 0) {
|
||||
*timeout = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!timerisset(&tv))
|
||||
return TRUE;
|
||||
|
||||
*timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean source_check(GSource *source)
|
||||
{
|
||||
struct fdsource *_fdsource = (struct fdsource *) source;
|
||||
GSList *l;
|
||||
struct timeval tv;
|
||||
int r;
|
||||
|
||||
if (!_fdsource->pollfds)
|
||||
return FALSE;
|
||||
|
||||
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
|
||||
GPollFD *pollfd = l->data;
|
||||
|
||||
if (pollfd->revents)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
r = fp_get_next_timeout(&tv);
|
||||
if (r == 1 && !timerisset(&tv))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean source_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer data)
|
||||
{
|
||||
struct timeval zerotimeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
|
||||
/* FIXME error handling */
|
||||
fp_handle_events_timeout(&zerotimeout);
|
||||
|
||||
/* FIXME whats the return value used for? */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void source_finalize(GSource *source)
|
||||
{
|
||||
struct fdsource *_fdsource = (struct fdsource *) source;
|
||||
GSList *l;
|
||||
|
||||
if (!_fdsource->pollfds)
|
||||
return;
|
||||
|
||||
for (l = _fdsource->pollfds; l != NULL; l = l->next) {
|
||||
GPollFD *pollfd = l->data;
|
||||
|
||||
g_source_remove_poll((GSource *) _fdsource, pollfd);
|
||||
g_slice_free(GPollFD, pollfd);
|
||||
_fdsource->pollfds = g_slist_delete_link(_fdsource->pollfds, l);
|
||||
}
|
||||
|
||||
g_slist_free(_fdsource->pollfds);
|
||||
}
|
||||
|
||||
static GSourceFuncs sourcefuncs = {
|
||||
.prepare = source_prepare,
|
||||
.check = source_check,
|
||||
.dispatch = source_dispatch,
|
||||
.finalize = source_finalize,
|
||||
};
|
||||
|
||||
static struct fdsource *fdsource = NULL;
|
||||
|
||||
static void pollfd_add(int fd, short events)
|
||||
{
|
||||
GPollFD *pollfd;
|
||||
|
||||
pollfd = g_slice_new(GPollFD);
|
||||
pollfd->fd = fd;
|
||||
pollfd->events = 0;
|
||||
pollfd->revents = 0;
|
||||
if (events & POLLIN)
|
||||
pollfd->events |= G_IO_IN;
|
||||
if (events & POLLOUT)
|
||||
pollfd->events |= G_IO_OUT;
|
||||
|
||||
fdsource->pollfds = g_slist_prepend(fdsource->pollfds, pollfd);
|
||||
g_source_add_poll((GSource *) fdsource, pollfd);
|
||||
}
|
||||
|
||||
static void pollfd_added_cb(int fd, short events)
|
||||
{
|
||||
g_debug("now monitoring fd %d", fd);
|
||||
pollfd_add(fd, events);
|
||||
}
|
||||
|
||||
static void pollfd_removed_cb(int fd)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
g_debug("no longer monitoring fd %d", fd);
|
||||
|
||||
if (!fdsource->pollfds) {
|
||||
g_debug("cannot remove from list as list is empty?");
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = fdsource->pollfds; l != NULL; l = l->next) {
|
||||
GPollFD *pollfd = l->data;
|
||||
|
||||
if (pollfd->fd != fd)
|
||||
continue;
|
||||
|
||||
g_source_remove_poll((GSource *) fdsource, pollfd);
|
||||
g_slice_free(GPollFD, pollfd);
|
||||
fdsource->pollfds = g_slist_delete_link(fdsource->pollfds, l);
|
||||
return;
|
||||
}
|
||||
|
||||
g_error("couldn't find fd %d in list\n", fd);
|
||||
}
|
||||
|
||||
int setup_pollfds(void)
|
||||
{
|
||||
ssize_t numfds;
|
||||
size_t i;
|
||||
struct fp_pollfd *fpfds;
|
||||
GSource *gsource;
|
||||
|
||||
gsource = g_source_new(&sourcefuncs, sizeof(struct fdsource));
|
||||
fdsource = (struct fdsource *) gsource;
|
||||
fdsource->pollfds = NULL;
|
||||
|
||||
numfds = fp_get_pollfds(&fpfds);
|
||||
if (numfds < 0) {
|
||||
if (fpfds)
|
||||
free(fpfds);
|
||||
return (int) numfds;
|
||||
} else if (numfds > 0) {
|
||||
for (i = 0; i < numfds; i++) {
|
||||
struct fp_pollfd *fpfd = &fpfds[i];
|
||||
pollfd_add(fpfd->fd, fpfd->events);
|
||||
}
|
||||
}
|
||||
|
||||
free(fpfds);
|
||||
fp_set_pollfd_notifiers(pollfd_added_cb, pollfd_removed_cb);
|
||||
g_source_attach(gsource, NULL);
|
||||
return 0;
|
||||
}
|
27
demo/loop.h
27
demo/loop.h
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Daniel Drake <dsd@gentoo.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef POLL_H
|
||||
|
||||
#define POLL_H
|
||||
|
||||
int setup_pollfds(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
gtk_test_resources = gnome.compile_resources('gtk-test-resources', 'gtk-libfprint-test.gresource.xml',
|
||||
source_dir : '.',
|
||||
c_name : 'gtk_test')
|
||||
|
||||
prefix = get_option('prefix')
|
||||
bindir = join_paths(prefix, get_option('bindir'))
|
||||
datadir = join_paths(prefix, get_option('datadir'))
|
||||
|
||||
executable('gtk-libfprint-test',
|
||||
[ 'gtk-libfprint-test.c', 'loop.c', 'loop.h', gtk_test_resources ],
|
||||
dependencies: [ libfprint_dep, gtk_dep ],
|
||||
include_directories: [
|
||||
root_inc,
|
||||
],
|
||||
c_args: [ common_cflags,
|
||||
'-DPACKAGE_VERSION="' + meson.project_version() + '"' ],
|
||||
install: true,
|
||||
install_dir: bindir)
|
||||
|
||||
appdata = 'org.freedesktop.libfprint.Demo.appdata.xml'
|
||||
install_data(appdata,
|
||||
install_dir: join_paths(datadir, 'metainfo'))
|
||||
|
||||
desktop = 'org.freedesktop.libfprint.Demo.desktop'
|
||||
install_data(desktop,
|
||||
install_dir: join_paths(datadir, 'applications'))
|
||||
|
||||
icon = 'org.freedesktop.libfprint.Demo.png'
|
||||
install_data(icon,
|
||||
install_dir: join_paths(datadir, 'icons'))
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>org.freedesktop.libfprint.Demo.desktop</id>
|
||||
<name>Fingerprint Reader Demo</name>
|
||||
<summary>Test fingerprint readers</summary>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>LGPL-2.1+</project_license>
|
||||
<description>
|
||||
<p>
|
||||
Fingerprint Reader Demo is a test application for the libfprint
|
||||
fingerprint reader library. Its purpose is to test drivers, new and old,
|
||||
in a sandbox, to make sure that the drivers and associated functions
|
||||
work correctly.
|
||||
</p>
|
||||
<p>
|
||||
Fingerprint Reader Demo does not modify the system, or replace integration
|
||||
in desktop environments.
|
||||
</p>
|
||||
</description>
|
||||
<url type="homepage">https://fprint.freedesktop.org</url>
|
||||
<screenshots>
|
||||
<screenshot type="default" width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-main.png</screenshot>
|
||||
<screenshot width="1024" height="576">https://git.gnome.org/browse/totem/plain/data/appdata/ss-music-playlist.png</screenshot>
|
||||
</screenshots>
|
||||
<updatecontact>hadess@hadess.net</updatecontact>
|
||||
<!-- Incorrect, but appstream-util won't validate without it -->
|
||||
<translation type="gettext">libfprint</translation>
|
||||
</component>
|
|
@ -1,10 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Name=Fingerprint Reader Demo
|
||||
Comment=Test fingerprint readers
|
||||
Keywords=finger;print;fingerprint;fprint;demo;driver;reader;
|
||||
Exec=gtk-libfprint-test
|
||||
Icon=org.freedesktop.libfprint.Demo
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=GTK;GNOME;Development;System;
|
||||
StartupNotify=true
|
|
@ -1,51 +0,0 @@
|
|||
{
|
||||
"app-id": "org.freedesktop.libfprint.Demo",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "master",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"command": "gtk-libfprint-test",
|
||||
"finish-args": [
|
||||
/* X11 + XShm access */
|
||||
"--share=ipc", "--socket=x11",
|
||||
/* Wayland access */
|
||||
"--socket=wayland",
|
||||
/* OpenGL access */
|
||||
"--device=dri",
|
||||
/* USB access */
|
||||
"--device=all"
|
||||
],
|
||||
"cleanup": [ "/include", "/lib/pkgconfig/" ],
|
||||
"modules": [
|
||||
{
|
||||
"name": "libusb",
|
||||
"config-opts": [ "--disable-static", "--disable-udev" ],
|
||||
"cleanup": [
|
||||
"/lib/*.la",
|
||||
"/lib/pkgconfig",
|
||||
"/include"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/libusb/libusb/archive/v1.0.22.tar.gz",
|
||||
"sha256": "3500f7b182750cd9ccf9be8b1df998f83df56a39ab264976bdb3307773e16f48"
|
||||
}
|
||||
],
|
||||
"post-install": [
|
||||
"install -Dm644 COPYING /app/share/licenses/libusb/COPYING"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libfprint",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [ "-Dudev_rules=false", "-Dx11-examples=false", "-Dgtk-examples=true" ],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/libfprint/libfprint.git",
|
||||
"branch": "wip/hadess/gtk-example"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
|
@ -1,115 +0,0 @@
|
|||
<?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>
|
|
@ -1,30 +0,0 @@
|
|||
<?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
106
doc/intro.xml
|
@ -1,106 +0,0 @@
|
|||
<?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>
|
|
@ -1,73 +0,0 @@
|
|||
<?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://fprint.freedesktop.org/libfprint-dev/">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>Library 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>
|
||||
<chapter id="driver-helpers">
|
||||
<title>Logging, and async machinery</title>
|
||||
<xi:include href="xml/fpi-log.xml"/>
|
||||
<xi:include href="xml/fpi-ssm.xml"/>
|
||||
<xi:include href="xml/fpi-poll.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-dev">
|
||||
<title>Device and driver structures</title>
|
||||
<xi:include href="xml/fpi-dev.xml"/>
|
||||
<xi:include href="xml/fpi-dev-img.xml"/>
|
||||
<xi:include href="xml/fpi-core.xml"/>
|
||||
<xi:include href="xml/fpi-core-img.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="driver-img">
|
||||
<title>Image manipulation</title>
|
||||
<xi:include href="xml/fpi-img.xml"/>
|
||||
<xi:include href="xml/fpi-assembling.xml"/>
|
||||
</chapter>
|
||||
|
||||
<xi:include href="xml/fpi-usb.xml"/>
|
||||
</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>
|
|
@ -1,271 +0,0 @@
|
|||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>events</FILE>
|
||||
<TITLE>Initialisation and events handling</TITLE>
|
||||
LIBFPRINT_DEPRECATED
|
||||
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>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<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>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>drv</FILE>
|
||||
fp_driver
|
||||
fp_driver_get_name
|
||||
fp_driver_get_full_name
|
||||
fp_driver_get_driver_id
|
||||
fp_driver_get_scan_type
|
||||
fp_driver_supports_imaging
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<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>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<FILE>print_data</FILE>
|
||||
fp_finger
|
||||
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>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<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>
|
||||
<INCLUDE>fprint.h</INCLUDE>
|
||||
<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
|
||||
fp_minutia_get_coords
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-log.h</INCLUDE>
|
||||
<FILE>fpi-log</FILE>
|
||||
fp_dbg
|
||||
fp_info
|
||||
fp_warn
|
||||
fp_err
|
||||
BUG_ON
|
||||
BUG
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-ssm.h</INCLUDE>
|
||||
<FILE>fpi-ssm</FILE>
|
||||
fpi_ssm
|
||||
ssm_completed_fn
|
||||
ssm_handler_fn
|
||||
|
||||
fpi_ssm_new
|
||||
fpi_ssm_free
|
||||
fpi_ssm_start
|
||||
fpi_ssm_start_subsm
|
||||
|
||||
fpi_ssm_next_state
|
||||
fpi_ssm_next_state_timeout_cb
|
||||
fpi_ssm_jump_to_state
|
||||
fpi_ssm_mark_completed
|
||||
fpi_ssm_mark_failed
|
||||
fpi_ssm_get_user_data
|
||||
fpi_ssm_get_error
|
||||
fpi_ssm_get_cur_state
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-poll.h</INCLUDE>
|
||||
<FILE>fpi-poll</FILE>
|
||||
fpi_timeout
|
||||
fpi_timeout_fn
|
||||
fpi_timeout_add
|
||||
fpi_timeout_set_name
|
||||
fpi_timeout_cancel
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-dev.h</INCLUDE>
|
||||
<FILE>fpi-dev</FILE>
|
||||
fp_img_dev
|
||||
|
||||
FP_DEV
|
||||
FP_IMG_DEV
|
||||
fp_dev_set_instance_data
|
||||
FP_INSTANCE_DATA
|
||||
|
||||
fpi_dev_get_usb_dev
|
||||
fpi_dev_get_verify_data
|
||||
fpi_dev_set_nr_enroll_stages
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-dev-img.h</INCLUDE>
|
||||
<FILE>fpi-dev-img</FILE>
|
||||
fp_imgdev_action
|
||||
fp_imgdev_state
|
||||
fp_imgdev_enroll_state
|
||||
|
||||
fpi_imgdev_abort_scan
|
||||
fpi_imgdev_activate_complete
|
||||
fpi_imgdev_close_complete
|
||||
fpi_imgdev_deactivate_complete
|
||||
fpi_imgdev_get_action
|
||||
fpi_imgdev_get_action_result
|
||||
fpi_imgdev_get_action_state
|
||||
fpi_imgdev_image_captured
|
||||
fpi_imgdev_open_complete
|
||||
fpi_imgdev_report_finger_status
|
||||
fpi_imgdev_session_error
|
||||
fpi_imgdev_set_action_result
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-core.h</INCLUDE>
|
||||
<FILE>fpi-core</FILE>
|
||||
usb_id
|
||||
fp_driver_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-core.h</INCLUDE>
|
||||
<FILE>fpi-core-img</FILE>
|
||||
FpiImgDriverFlags
|
||||
fp_img_driver
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-img.h</INCLUDE>
|
||||
<FILE>fpi-img</FILE>
|
||||
FpiImgFlags
|
||||
|
||||
fpi_img_new
|
||||
fpi_img_new_for_imgdev
|
||||
fpi_img_realloc
|
||||
fpi_img_resize
|
||||
|
||||
fpi_std_sq_dev
|
||||
fpi_mean_sq_diff_norm
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-assembling.h</INCLUDE>
|
||||
<FILE>fpi-assembling</FILE>
|
||||
fpi_frame
|
||||
fpi_frame_asmbl_ctx
|
||||
fpi_line_asmbl_ctx
|
||||
|
||||
fpi_do_movement_estimation
|
||||
fpi_assemble_frames
|
||||
fpi_assemble_lines
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<INCLUDE>fpi-usb.h</INCLUDE>
|
||||
<FILE>fpi-usb</FILE>
|
||||
fpi_usb_transfer
|
||||
|
||||
fpi_usb_transfer_cb_fn
|
||||
fpi_usb_alloc
|
||||
fpi_usb_fill_bulk_transfer
|
||||
fpi_usb_submit_transfer
|
||||
fpi_usb_cancel_transfer
|
||||
</SECTION>
|
|
@ -1,71 +0,0 @@
|
|||
subdir('xml')
|
||||
|
||||
private_headers = [
|
||||
'config.h',
|
||||
|
||||
'aeslib.h',
|
||||
'assembling.h',
|
||||
'fp_internal.h',
|
||||
'nbis-helpers.h',
|
||||
'fpi-async.h',
|
||||
'fpi-data.h',
|
||||
|
||||
# Drivers
|
||||
'aes1660.h',
|
||||
'aes2501.h',
|
||||
'aes2550.h',
|
||||
'aes2660.h',
|
||||
'aes3k.h',
|
||||
'aesx660.h',
|
||||
'driver_ids.h',
|
||||
'elan.h',
|
||||
'upek_proto.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',
|
||||
'mytime.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: [
|
||||
'--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)
|
|
@ -1,8 +0,0 @@
|
|||
<!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@">
|
|
@ -1,10 +0,0 @@
|
|||
ent_conf = configuration_data()
|
||||
ent_conf.set('PACKAGE', 'libfprint')
|
||||
ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/libfprint/libfprint/issues')
|
||||
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://fprint.freedesktop.org/')
|
||||
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,11 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
fp_init ();
|
||||
return 0;
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* Example fingerprint enrollment program
|
||||
* Enrolls your right index finger and saves the print to disk
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
{
|
||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||
struct fp_driver *drv;
|
||||
if (!ddev)
|
||||
return NULL;
|
||||
|
||||
drv = fp_dscv_dev_get_driver(ddev);
|
||||
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
|
||||
return ddev;
|
||||
}
|
||||
|
||||
struct fp_print_data *enroll(struct fp_dev *dev) {
|
||||
struct fp_print_data *enrolled_print = NULL;
|
||||
int r;
|
||||
|
||||
printf("You will need to successfully scan your finger %d times to "
|
||||
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
|
||||
|
||||
do {
|
||||
struct fp_img *img = NULL;
|
||||
|
||||
printf("\nScan your finger now.\n");
|
||||
|
||||
r = fp_enroll_finger_img(dev, &enrolled_print, &img);
|
||||
if (img) {
|
||||
fp_img_save_to_file(img, "enrolled.pgm");
|
||||
printf("Wrote scanned image to enrolled.pgm\n");
|
||||
fp_img_free(img);
|
||||
}
|
||||
if (r < 0) {
|
||||
printf("Enroll failed with error %d\n", r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case FP_ENROLL_COMPLETE:
|
||||
printf("Enroll complete!\n");
|
||||
break;
|
||||
case FP_ENROLL_FAIL:
|
||||
printf("Enroll failed, something wen't wrong :(\n");
|
||||
return NULL;
|
||||
case FP_ENROLL_PASS:
|
||||
printf("Enroll stage passed. Yay!\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY:
|
||||
printf("Didn't quite catch that. Please try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_TOO_SHORT:
|
||||
printf("Your swipe was too short, please try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_CENTER_FINGER:
|
||||
printf("Didn't catch that, please center your finger on the "
|
||||
"sensor and try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_REMOVE_FINGER:
|
||||
printf("Scan failed, please remove your finger and then try "
|
||||
"again.\n");
|
||||
break;
|
||||
}
|
||||
} while (r != FP_ENROLL_COMPLETE);
|
||||
|
||||
if (!enrolled_print) {
|
||||
fprintf(stderr, "Enroll complete but no print?\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("Enrollment completed!\n\n");
|
||||
return enrolled_print;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
struct fp_dev *dev;
|
||||
struct fp_print_data *data;
|
||||
|
||||
printf("This program will enroll your right index finger, "
|
||||
"unconditionally overwriting any right-index print that was enrolled "
|
||||
"previously. If you want to continue, press enter, otherwise hit "
|
||||
"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);
|
||||
}
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Opened device. It's now time to enroll your finger.\n\n");
|
||||
data = enroll(dev);
|
||||
if (!data)
|
||||
goto out_close;
|
||||
|
||||
r = print_data_save(data, RIGHT_INDEX);
|
||||
if (r < 0)
|
||||
fprintf(stderr, "Data save failed, code %d\n", r);
|
||||
|
||||
fp_print_data_free(data);
|
||||
out_close:
|
||||
fp_dev_close(dev);
|
||||
out:
|
||||
fp_exit();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Example libfprint image capture program
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
{
|
||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||
struct fp_driver *drv;
|
||||
if (!ddev)
|
||||
return NULL;
|
||||
|
||||
drv = fp_dscv_dev_get_driver(ddev);
|
||||
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
|
||||
return ddev;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
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);
|
||||
}
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fp_dev_supports_imaging(dev)) {
|
||||
fprintf(stderr, "this device does not have imaging capabilities.\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
printf("Opened device. It's now time to scan your finger.\n\n");
|
||||
|
||||
r = fp_dev_img_capture(dev, 0, &img);
|
||||
if (r) {
|
||||
fprintf(stderr, "image capture failed, code %d\n", r);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
r = fp_img_save_to_file(img, "finger.pgm");
|
||||
if (r) {
|
||||
fprintf(stderr, "img save failed, code %d\n", r);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
fp_img_standardize(img);
|
||||
r = fp_img_save_to_file(img, "finger_standardized.pgm");
|
||||
fp_img_free(img);
|
||||
if (r) {
|
||||
fprintf(stderr, "standardized img save failed, code %d\n", r);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
out_close:
|
||||
fp_dev_close(dev);
|
||||
out:
|
||||
fp_exit();
|
||||
return r;
|
||||
}
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
/*
|
||||
* Example libfprint continuous image capture program
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
|
||||
#define FORMAT 0x32595559
|
||||
|
||||
static int adaptor = -1;
|
||||
static char *framebuffer = NULL;
|
||||
|
||||
static Display *display = NULL;
|
||||
static Window window=(Window)NULL;
|
||||
static XvImage *xv_image = NULL;
|
||||
static XvAdaptorInfo *info;
|
||||
static GC gc;
|
||||
static int connection = -1;
|
||||
|
||||
/* based on macro by Bart Nabbe */
|
||||
#define GREY2YUV(grey, y, u, v)\
|
||||
y = (9798*grey + 19235*grey + 3736*grey) / 32768;\
|
||||
u = (-4784*grey - 9437*grey + 14221*grey) / 32768 + 128;\
|
||||
v = (20218*grey - 16941*grey - 3277*grey) / 32768 + 128;\
|
||||
y = y < 0 ? 0 : y;\
|
||||
u = u < 0 ? 0 : u;\
|
||||
v = v < 0 ? 0 : v;\
|
||||
y = y > 255 ? 255 : y;\
|
||||
u = u > 255 ? 255 : u;\
|
||||
v = v > 255 ? 255 : v
|
||||
|
||||
static void grey2yuy2 (unsigned char *grey, char *YUV, int num) {
|
||||
int i, j;
|
||||
int y0, y1, u0, u1, v0, v1;
|
||||
uint64_t gval;
|
||||
|
||||
for (i = 0, j = 0; i < num; i += 2, j += 4)
|
||||
{
|
||||
gval = grey[i];
|
||||
GREY2YUV (gval, y0, u0 , v0);
|
||||
gval = grey[i + 1];
|
||||
GREY2YUV (gval, y1, u1 , v1);
|
||||
YUV[j + 0] = y0;
|
||||
YUV[j + 1] = (u0+u1)/2;
|
||||
YUV[j + 2] = y1;
|
||||
YUV[j + 3] = (v0+v1)/2;
|
||||
}
|
||||
}
|
||||
|
||||
static void display_frame(struct fp_img *img)
|
||||
{
|
||||
int width = fp_img_get_width(img);
|
||||
int height = fp_img_get_height(img);
|
||||
unsigned char *data = fp_img_get_data(img);
|
||||
|
||||
if (adaptor < 0)
|
||||
return;
|
||||
|
||||
grey2yuy2(data, framebuffer, width * height);
|
||||
xv_image = XvCreateImage(display, info[adaptor].base_id, FORMAT,
|
||||
framebuffer, width, height);
|
||||
XvPutImage(display, info[adaptor].base_id, window, gc, xv_image,
|
||||
0, 0, width, height, 0, 0, width, height);
|
||||
}
|
||||
|
||||
static void QueryXv(void)
|
||||
{
|
||||
unsigned int num_adaptors;
|
||||
int num_formats;
|
||||
XvImageFormatValues *formats = NULL;
|
||||
int i,j;
|
||||
char xv_name[5];
|
||||
|
||||
XvQueryAdaptors(display, DefaultRootWindow(display), &num_adaptors,
|
||||
&info);
|
||||
|
||||
for(i = 0; i < num_adaptors; i++) {
|
||||
formats = XvListImageFormats(display, info[i].base_id,
|
||||
&num_formats);
|
||||
for(j = 0; j < num_formats; j++) {
|
||||
xv_name[4] = 0;
|
||||
memcpy(xv_name, &formats[j].id, 4);
|
||||
if(formats[j].id == FORMAT) {
|
||||
printf("using Xv format 0x%x %s %s\n",
|
||||
formats[j].id, xv_name,
|
||||
(formats[j].format==XvPacked)
|
||||
? "packed" : "planar");
|
||||
if (adaptor < 0)
|
||||
adaptor = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
XFree(formats);
|
||||
if (adaptor < 0)
|
||||
printf("No suitable Xv adaptor found\n");
|
||||
}
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
{
|
||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||
struct fp_driver *drv;
|
||||
if (!ddev)
|
||||
return NULL;
|
||||
|
||||
drv = fp_dscv_dev_get_driver(ddev);
|
||||
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
|
||||
return ddev;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1;
|
||||
XEvent xev;
|
||||
XGCValues xgcv;
|
||||
long background=0x010203;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
struct fp_dev *dev;
|
||||
int img_width;
|
||||
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);
|
||||
}
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!fp_dev_supports_imaging(dev)) {
|
||||
fprintf(stderr, "this device does not have imaging capabilities.\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
img_width = fp_dev_get_img_width(dev);
|
||||
img_height = fp_dev_get_img_height(dev);
|
||||
if (img_width <= 0 || img_height <= 0) {
|
||||
fprintf(stderr, "this device returns images with variable dimensions,"
|
||||
" this example does not support that.\n");
|
||||
goto out_close;
|
||||
}
|
||||
framebuffer = malloc(img_width * img_height * 2);
|
||||
if (!framebuffer)
|
||||
goto out_close;
|
||||
|
||||
/* make the window */
|
||||
display = XOpenDisplay(getenv("DISPLAY"));
|
||||
if(display == NULL) {
|
||||
fprintf(stderr,"Could not open display \"%s\"\n",
|
||||
getenv("DISPLAY"));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
QueryXv();
|
||||
|
||||
if (adaptor < 0)
|
||||
goto out_close;
|
||||
|
||||
window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0,
|
||||
img_width, img_height, 0,
|
||||
WhitePixel(display, DefaultScreen(display)), background);
|
||||
|
||||
XSelectInput(display, window, StructureNotifyMask | KeyPressMask);
|
||||
XMapWindow(display, window);
|
||||
connection = ConnectionNumber(display);
|
||||
|
||||
gc = XCreateGC(display, window, 0, &xgcv);
|
||||
|
||||
printf("Press S to toggle standardized mode, Q to quit\n");
|
||||
|
||||
while (1) { /* event loop */
|
||||
struct fp_img *img;
|
||||
|
||||
r = fp_dev_img_capture(dev, 1, &img);
|
||||
if (r) {
|
||||
fprintf(stderr, "image capture failed, code %d\n", r);
|
||||
goto out_close;
|
||||
}
|
||||
if (standardize)
|
||||
fp_img_standardize(img);
|
||||
|
||||
display_frame(img);
|
||||
fp_img_free(img);
|
||||
XFlush(display);
|
||||
|
||||
while (XPending(display) > 0) {
|
||||
XNextEvent(display, &xev);
|
||||
if (xev.type != KeyPress)
|
||||
continue;
|
||||
|
||||
switch (XKeycodeToKeysym(display, xev.xkey.keycode, 0)) {
|
||||
case XK_q:
|
||||
case XK_Q:
|
||||
r = 0;
|
||||
goto out_close;
|
||||
break;
|
||||
case XK_s:
|
||||
case XK_S:
|
||||
standardize = !standardize;
|
||||
break;
|
||||
}
|
||||
} /* XPending */
|
||||
}
|
||||
|
||||
r = 0;
|
||||
out_close:
|
||||
if (framebuffer)
|
||||
free(framebuffer);
|
||||
fp_dev_close(dev);
|
||||
if ((void *) window != NULL)
|
||||
XUnmapWindow(display, window);
|
||||
if (display != NULL)
|
||||
XFlush(display);
|
||||
out:
|
||||
fp_exit();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
examples = [ 'verify_live', 'enroll', 'verify', 'img_capture' ]
|
||||
foreach example: examples
|
||||
executable(example,
|
||||
[example + '.c', 'storage.c'],
|
||||
dependencies: [libfprint_dep, glib_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
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* Trivial storage driver for example programs
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.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 <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
#define STORAGE_FILE "test-storage.variant"
|
||||
|
||||
static char *
|
||||
get_print_data_descriptor (struct fp_print_data *data, struct fp_dev *dev, enum fp_finger finger)
|
||||
{
|
||||
gint drv_id;
|
||||
gint devtype;
|
||||
|
||||
if (data) {
|
||||
drv_id = fp_print_data_get_driver_id (data);
|
||||
devtype = fp_print_data_get_devtype (data);
|
||||
} else {
|
||||
drv_id = fp_driver_get_driver_id(fp_dev_get_driver (dev));
|
||||
devtype = fp_dev_get_devtype (dev);
|
||||
}
|
||||
|
||||
return g_strdup_printf("%x/%08x/%x",
|
||||
drv_id,
|
||||
devtype,
|
||||
finger);
|
||||
}
|
||||
|
||||
static GVariantDict*
|
||||
load_data(void)
|
||||
{
|
||||
GVariantDict *res;
|
||||
GVariant *var;
|
||||
gchar *contents = NULL;
|
||||
gssize length = 0;
|
||||
|
||||
if (!g_file_get_contents (STORAGE_FILE, &contents, &length, NULL)) {
|
||||
g_warning ("Error loading storage, assuming it is empty");
|
||||
return g_variant_dict_new(NULL);
|
||||
}
|
||||
|
||||
var = g_variant_new_from_data (G_VARIANT_TYPE_VARDICT, contents, length, FALSE, NULL, NULL);
|
||||
|
||||
res = g_variant_dict_new(var);
|
||||
g_variant_unref(var);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
save_data(GVariant *data)
|
||||
{
|
||||
const gchar *contents = NULL;
|
||||
gsize length;
|
||||
|
||||
length = g_variant_get_size(data);
|
||||
contents = (gchar*) g_variant_get_data (data);
|
||||
|
||||
if (!g_file_set_contents (STORAGE_FILE, contents, length, NULL)) {
|
||||
g_warning ("Error saving storage,!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_variant_ref_sink(data);
|
||||
g_variant_unref(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
print_data_save(struct fp_print_data *fp_data, enum fp_finger finger)
|
||||
{
|
||||
gchar *descr = get_print_data_descriptor (fp_data, NULL, finger);
|
||||
GVariantDict *dict;
|
||||
GVariant *val;
|
||||
guchar *data;
|
||||
gsize size;
|
||||
int res;
|
||||
|
||||
dict = load_data();
|
||||
|
||||
size = fp_print_data_get_data(fp_data, &data);
|
||||
val = g_variant_new_fixed_array (G_VARIANT_TYPE("y"), data, size, 1);
|
||||
g_variant_dict_insert_value (dict, descr, val);
|
||||
|
||||
res = save_data(g_variant_dict_end(dict));
|
||||
g_variant_dict_unref(dict);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct fp_print_data*
|
||||
print_data_load(struct fp_dev *dev, enum fp_finger finger)
|
||||
{
|
||||
gchar *descr = get_print_data_descriptor (NULL, dev, finger);
|
||||
GVariantDict *dict;
|
||||
guchar *stored_data;
|
||||
gsize stored_len;
|
||||
GVariant *val;
|
||||
struct fp_print_data *res = NULL;
|
||||
|
||||
dict = load_data();
|
||||
val = g_variant_dict_lookup_value (dict, descr, G_VARIANT_TYPE ("ay"));
|
||||
|
||||
if (val) {
|
||||
stored_data = (guchar*) g_variant_get_fixed_array (val, &stored_len, 1);
|
||||
res = fp_print_data_from_data(stored_data, stored_len);
|
||||
|
||||
g_variant_unref(val);
|
||||
}
|
||||
|
||||
g_variant_dict_unref(dict);
|
||||
g_free(descr);
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Trivial storage driver for example programs
|
||||
*
|
||||
* Copyright (C) 2019 Benjamin Berg <bberg@redhat.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 __STORAGE_H
|
||||
#define __STORAGE_H
|
||||
|
||||
int print_data_save(struct fp_print_data *fp_data, enum fp_finger finger);
|
||||
struct fp_print_data* print_data_load(struct fp_dev *dev, enum fp_finger finger);
|
||||
|
||||
#endif /* __STORAGE_H */
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* Example fingerprint verification program, which verifies the right index
|
||||
* finger which has been previously enrolled to disk.
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
{
|
||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||
struct fp_driver *drv;
|
||||
if (!ddev)
|
||||
return NULL;
|
||||
|
||||
drv = fp_dscv_dev_get_driver(ddev);
|
||||
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
|
||||
return ddev;
|
||||
}
|
||||
|
||||
int verify(struct fp_dev *dev, struct fp_print_data *data)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
struct fp_img *img = NULL;
|
||||
|
||||
sleep(1);
|
||||
printf("\nScan your finger now.\n");
|
||||
r = fp_verify_finger_img(dev, data, &img);
|
||||
if (img) {
|
||||
fp_img_save_to_file(img, "verify.pgm");
|
||||
printf("Wrote scanned image to verify.pgm\n");
|
||||
fp_img_free(img);
|
||||
}
|
||||
if (r < 0) {
|
||||
printf("verification failed with error %d :(\n", r);
|
||||
return r;
|
||||
}
|
||||
switch (r) {
|
||||
case FP_VERIFY_NO_MATCH:
|
||||
printf("NO MATCH!\n");
|
||||
return 0;
|
||||
case FP_VERIFY_MATCH:
|
||||
printf("MATCH!\n");
|
||||
return 0;
|
||||
case FP_VERIFY_RETRY:
|
||||
printf("Scan didn't quite work. Please try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
printf("Swipe was too short, please try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
printf("Please center your finger on the sensor and try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
printf("Please remove finger from the sensor and try again.\n");
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
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);
|
||||
}
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Opened device. Loading previously enrolled right index finger "
|
||||
"data...\n");
|
||||
|
||||
data = print_data_load(dev, RIGHT_INDEX);
|
||||
if (!data) {
|
||||
fprintf(stderr, "Failed to load fingerprint, error %d\n", r);
|
||||
fprintf(stderr, "Did you remember to enroll your right index finger "
|
||||
"first?\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
printf("Print loaded. Time to verify!\n");
|
||||
do {
|
||||
char buffer[20];
|
||||
|
||||
verify(dev, data);
|
||||
printf("Verify again? [Y/n]? ");
|
||||
fgets(buffer, sizeof(buffer), stdin);
|
||||
if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y')
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
fp_print_data_free(data);
|
||||
out_close:
|
||||
fp_dev_close(dev);
|
||||
out:
|
||||
fp_exit();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* Example fingerprint verification program
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libfprint/fprint.h>
|
||||
|
||||
struct fp_dscv_dev *discover_device(struct fp_dscv_dev **discovered_devs)
|
||||
{
|
||||
struct fp_dscv_dev *ddev = discovered_devs[0];
|
||||
struct fp_driver *drv;
|
||||
if (!ddev)
|
||||
return NULL;
|
||||
|
||||
drv = fp_dscv_dev_get_driver(ddev);
|
||||
printf("Found device claimed by %s driver\n", fp_driver_get_full_name(drv));
|
||||
return ddev;
|
||||
}
|
||||
|
||||
struct fp_print_data *enroll(struct fp_dev *dev) {
|
||||
struct fp_print_data *enrolled_print = NULL;
|
||||
int r;
|
||||
|
||||
printf("You will need to successfully scan your finger %d times to "
|
||||
"complete the process.\n", fp_dev_get_nr_enroll_stages(dev));
|
||||
|
||||
do {
|
||||
printf("\nScan your finger now.\n");
|
||||
r = fp_enroll_finger(dev, &enrolled_print);
|
||||
if (r < 0) {
|
||||
printf("Enroll failed with error %d\n", r);
|
||||
return NULL;
|
||||
}
|
||||
switch (r) {
|
||||
case FP_ENROLL_COMPLETE:
|
||||
printf("Enroll complete!\n");
|
||||
break;
|
||||
case FP_ENROLL_FAIL:
|
||||
printf("Enroll failed, something wen't wrong :(\n");
|
||||
return NULL;
|
||||
case FP_ENROLL_PASS:
|
||||
printf("Enroll stage passed. Yay!\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY:
|
||||
printf("Didn't quite catch that. Please try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_TOO_SHORT:
|
||||
printf("Your swipe was too short, please try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_CENTER_FINGER:
|
||||
printf("Didn't catch that, please center your finger on the "
|
||||
"sensor and try again.\n");
|
||||
break;
|
||||
case FP_ENROLL_RETRY_REMOVE_FINGER:
|
||||
printf("Scan failed, please remove your finger and then try "
|
||||
"again.\n");
|
||||
break;
|
||||
}
|
||||
} while (r != FP_ENROLL_COMPLETE);
|
||||
|
||||
if (!enrolled_print) {
|
||||
fprintf(stderr, "Enroll complete but no print?\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("Enrollment completed!\n\n");
|
||||
return enrolled_print;
|
||||
}
|
||||
|
||||
int verify(struct fp_dev *dev, struct fp_print_data *data)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
sleep(1);
|
||||
printf("\nScan your finger now.\n");
|
||||
r = fp_verify_finger(dev, data);
|
||||
if (r < 0) {
|
||||
printf("verification failed with error %d :(\n", r);
|
||||
return r;
|
||||
}
|
||||
switch (r) {
|
||||
case FP_VERIFY_NO_MATCH:
|
||||
printf("NO MATCH!\n");
|
||||
return 0;
|
||||
case FP_VERIFY_MATCH:
|
||||
printf("MATCH!\n");
|
||||
return 0;
|
||||
case FP_VERIFY_RETRY:
|
||||
printf("Scan didn't quite work. Please try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_TOO_SHORT:
|
||||
printf("Swipe was too short, please try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_CENTER_FINGER:
|
||||
printf("Please center your finger on the sensor and try again.\n");
|
||||
break;
|
||||
case FP_VERIFY_RETRY_REMOVE_FINGER:
|
||||
printf("Please remove finger from the sensor and try again.\n");
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1;
|
||||
struct fp_dscv_dev *ddev;
|
||||
struct fp_dscv_dev **discovered_devs;
|
||||
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);
|
||||
}
|
||||
|
||||
discovered_devs = fp_discover_devs();
|
||||
if (!discovered_devs) {
|
||||
fprintf(stderr, "Could not discover devices\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ddev = discover_device(discovered_devs);
|
||||
if (!ddev) {
|
||||
fprintf(stderr, "No devices detected.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = fp_dev_open(ddev);
|
||||
fp_dscv_devs_free(discovered_devs);
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Could not open device.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Opened device. It's now time to enroll your finger.\n\n");
|
||||
data = enroll(dev);
|
||||
if (!data)
|
||||
goto out_close;
|
||||
|
||||
|
||||
printf("Normally we'd save that print to disk, and recall it at some "
|
||||
"point later when we want to authenticate the user who just "
|
||||
"enrolled. In the interests of demonstration, we'll authenticate "
|
||||
"that user immediately.\n");
|
||||
|
||||
do {
|
||||
char buffer[20];
|
||||
|
||||
verify(dev, data);
|
||||
printf("Verify again? [Y/n]? ");
|
||||
fgets(buffer, sizeof(buffer), stdin);
|
||||
if (buffer[0] != '\n' && buffer[0] != 'y' && buffer[0] != 'Y')
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
fp_print_data_free(data);
|
||||
out_close:
|
||||
fp_dev_close(dev);
|
||||
out:
|
||||
fp_exit();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -1,837 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES1610 driver for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2007 Cyrille Bagard
|
||||
* Copyright (C) 2007 Vasily Khoruzhick
|
||||
* Copyright (C) 2009 Guido Grazioli <guido.grazioli@gmail.com>
|
||||
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* Based on code from libfprint aes2501 driver.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes1610"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void complete_deactivation(struct fp_img_dev *dev);
|
||||
static int adjust_gain(unsigned char *buffer, int status);
|
||||
|
||||
#define FIRST_AES1610_REG 0x1B
|
||||
#define LAST_AES1610_REG 0xFF
|
||||
|
||||
#define GAIN_STATUS_FIRST 1
|
||||
#define GAIN_STATUS_NORMAL 2
|
||||
|
||||
/* FIXME these need checking */
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
|
||||
#define BULK_TIMEOUT 4000
|
||||
|
||||
#define FINGER_DETECTION_LEN 19
|
||||
#define STRIP_CAPTURE_LEN 665
|
||||
|
||||
/*
|
||||
* The AES1610 is an imaging device using a swipe-type sensor. It samples
|
||||
* the finger at preprogrammed intervals, sending a 128x8 frame to the
|
||||
* computer.
|
||||
* Unless the user is scanning their finger unreasonably fast, the frames
|
||||
* *will* overlap. The implementation below detects this overlap and produces
|
||||
* a contiguous image as the end result.
|
||||
* The fact that the user determines the length of the swipe (and hence the
|
||||
* number of useful frames) and also the fact that overlap varies means that
|
||||
* images returned from this driver vary in height.
|
||||
*/
|
||||
|
||||
#define FRAME_WIDTH 128
|
||||
#define FRAME_HEIGHT 8
|
||||
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
/* maximum number of frames to read during a scan */
|
||||
/* FIXME reduce substantially */
|
||||
#define MAX_FRAMES 350
|
||||
|
||||
/****** GENERAL FUNCTIONS ******/
|
||||
|
||||
struct aes1610_dev {
|
||||
uint8_t read_regs_retry_count;
|
||||
GSList *strips;
|
||||
size_t strips_len;
|
||||
gboolean deactivating;
|
||||
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);
|
||||
|
||||
struct aes1610_read_regs {
|
||||
struct fp_img_dev *dev;
|
||||
aes1610_read_regs_cb callback;
|
||||
struct aes_regwrite *regwrite;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
/* FIXME: what to do here? */
|
||||
static void stub_capture_stop_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* check that read succeeded but ignore all data */
|
||||
static void generic_ignore_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
else if (transfer->length != transfer->actual_length)
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_ssm *ssm = user_data;
|
||||
if (result == 0)
|
||||
fpi_ssm_next_state(ssm);
|
||||
else
|
||||
fpi_ssm_mark_failed(ssm, result);
|
||||
}
|
||||
|
||||
/* read the specified number of bytes from the IN endpoint but throw them
|
||||
* away, then increment the SSM */
|
||||
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
data = g_malloc(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);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
/****** FINGER PRESENCE DETECTION ******/
|
||||
|
||||
|
||||
static const struct aes_regwrite finger_det_reqs[] = {
|
||||
{ 0x80, 0x01 },
|
||||
{ 0x80, 0x12 },
|
||||
{ 0x85, 0x00 },
|
||||
{ 0x8A, 0x00 },
|
||||
{ 0x8B, 0x0E },
|
||||
{ 0x8C, 0x90 },
|
||||
{ 0x8D, 0x83 },
|
||||
{ 0x8E, 0x07 },
|
||||
{ 0x8F, 0x07 },
|
||||
{ 0x96, 0x00 },
|
||||
{ 0x97, 0x48 },
|
||||
{ 0xA1, 0x00 },
|
||||
{ 0xA2, 0x50 },
|
||||
{ 0xA6, 0xE4 },
|
||||
{ 0xAD, 0x08 },
|
||||
{ 0xAE, 0x5B },
|
||||
{ 0xAF, 0x54 },
|
||||
{ 0xB1, 0x28 },
|
||||
{ 0xB5, 0xAB },
|
||||
{ 0xB6, 0x0E },
|
||||
{ 0x1B, 0x2D },
|
||||
{ 0x81, 0x04 }
|
||||
};
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev);
|
||||
|
||||
static void finger_det_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
unsigned char *data = transfer->buffer;
|
||||
int i;
|
||||
int sum = 0;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto out;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* examine histogram to determine finger presence */
|
||||
for (i = 3; i < 17; i++)
|
||||
sum += (data[i] & 0xf) + (data[i] >> 4);
|
||||
if (sum > 20) {
|
||||
/* reset default gain */
|
||||
adjust_gain(data,GAIN_STATUS_FIRST);
|
||||
/* finger present, start capturing */
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
start_capture(dev);
|
||||
} else {
|
||||
/* no finger, poll for a new histogram */
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
if (result) {
|
||||
fpi_imgdev_session_error(dev, result);
|
||||
return;
|
||||
}
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
data = g_malloc(FINGER_DETECTION_LEN);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, FINGER_DETECTION_LEN,
|
||||
finger_det_data_cb, dev, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs), finger_det_reqs_cb, NULL);
|
||||
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
static struct aes_regwrite capture_reqs[] = {
|
||||
{ 0x80, 0x01 },
|
||||
{ 0x80, 0x12 },
|
||||
{ 0x84, 0x01 },
|
||||
{ 0x85, 0x00 },
|
||||
{ 0x89, 0x64 },
|
||||
{ 0x8A, 0x00 },
|
||||
{ 0x8B, 0x0E },
|
||||
{ 0x8C, 0x90 },
|
||||
{ 0xBE, 0x23 },
|
||||
{ 0x29, 0x04 },
|
||||
{ 0x2A, 0xFF },
|
||||
{ 0x96, 0x00 },
|
||||
{ 0x98, 0x03 },
|
||||
{ 0x99, 0x00 },
|
||||
{ 0x9C, 0xA5 },
|
||||
{ 0x9D, 0x40 },
|
||||
{ 0x9E, 0xC6 },
|
||||
{ 0x9F, 0x8E },
|
||||
{ 0xA2, 0x50 },
|
||||
{ 0xA3, 0xF0 },
|
||||
{ 0xAD, 0x08 },
|
||||
{ 0xBD, 0x4F },
|
||||
{ 0xAF, 0x54 },
|
||||
{ 0xB1, 0x08 },
|
||||
{ 0xB5, 0xAB },
|
||||
{ 0x1B, 0x2D },
|
||||
{ 0xB6, 0x4E },
|
||||
{ 0xB8, 0x70 },
|
||||
{ 0x2B, 0xB3 },
|
||||
{ 0x2C, 0x5D },
|
||||
{ 0x2D, 0x98 },
|
||||
{ 0x2E, 0xB0 },
|
||||
{ 0x2F, 0x20 },
|
||||
{ 0xA2, 0xD0 },
|
||||
{ 0x1D, 0x21 },
|
||||
{ 0x1E, 0xBE },
|
||||
{ 0x1C, 0x00 },
|
||||
{ 0x1D, 0x30 },
|
||||
{ 0x1E, 0x29 },
|
||||
{ 0x1C, 0x01 },
|
||||
{ 0x1D, 0x00 },
|
||||
{ 0x1E, 0x9E },
|
||||
{ 0x1C, 0x02 },
|
||||
{ 0x1D, 0x30 },
|
||||
{ 0x1E, 0xBB },
|
||||
{ 0x1C, 0x03 },
|
||||
{ 0x1D, 0x00 },
|
||||
{ 0x1E, 0x9D },
|
||||
{ 0x1C, 0x04 },
|
||||
{ 0x1D, 0x22 },
|
||||
{ 0x1E, 0xFF },
|
||||
{ 0x1C, 0x05 },
|
||||
{ 0x1D, 0x1B },
|
||||
{ 0x1E, 0x4E },
|
||||
{ 0x1C, 0x06 },
|
||||
{ 0x1D, 0x16 },
|
||||
{ 0x1E, 0x28 },
|
||||
{ 0x1C, 0x07 },
|
||||
{ 0x1D, 0x22 },
|
||||
{ 0x1E, 0xFF },
|
||||
{ 0x1C, 0x08 },
|
||||
{ 0x1D, 0x15 },
|
||||
{ 0x1E, 0xF1 },
|
||||
{ 0x1C, 0x09 },
|
||||
{ 0x1D, 0x30 },
|
||||
{ 0x1E, 0xD5 },
|
||||
{ 0x1C, 0x0A },
|
||||
{ 0x1D, 0x00 },
|
||||
{ 0x1E, 0x9E },
|
||||
{ 0x1C, 0x0B },
|
||||
{ 0x1D, 0x17 },
|
||||
{ 0x1E, 0x9D },
|
||||
{ 0x1C, 0x0C },
|
||||
{ 0x1D, 0x28 },
|
||||
{ 0x1E, 0xD7 },
|
||||
{ 0x1C, 0x0D },
|
||||
{ 0x1D, 0x17 },
|
||||
{ 0x1E, 0xD7 },
|
||||
{ 0x1C, 0x0E },
|
||||
{ 0x1D, 0x0A },
|
||||
{ 0x1E, 0xCB },
|
||||
{ 0x1C, 0x0F },
|
||||
{ 0x1D, 0x24 },
|
||||
{ 0x1E, 0x14 },
|
||||
{ 0x1C, 0x10 },
|
||||
{ 0x1D, 0x17 },
|
||||
{ 0x1E, 0x85 },
|
||||
{ 0x1C, 0x11 },
|
||||
{ 0x1D, 0x15 },
|
||||
{ 0x1E, 0x71 },
|
||||
{ 0x1C, 0x12 },
|
||||
{ 0x1D, 0x2B },
|
||||
{ 0x1E, 0x36 },
|
||||
{ 0x1C, 0x13 },
|
||||
{ 0x1D, 0x12 },
|
||||
{ 0x1E, 0x06 },
|
||||
{ 0x1C, 0x14 },
|
||||
{ 0x1D, 0x30 },
|
||||
{ 0x1E, 0x97 },
|
||||
{ 0x1C, 0x15 },
|
||||
{ 0x1D, 0x21 },
|
||||
{ 0x1E, 0x32 },
|
||||
{ 0x1C, 0x16 },
|
||||
{ 0x1D, 0x06 },
|
||||
{ 0x1E, 0xE6 },
|
||||
{ 0x1C, 0x17 },
|
||||
{ 0x1D, 0x16 },
|
||||
{ 0x1E, 0x06 },
|
||||
{ 0x1C, 0x18 },
|
||||
{ 0x1D, 0x30 },
|
||||
{ 0x1E, 0x01 },
|
||||
{ 0x1C, 0x19 },
|
||||
{ 0x1D, 0x21 },
|
||||
{ 0x1E, 0x37 },
|
||||
{ 0x1C, 0x1A },
|
||||
{ 0x1D, 0x00 },
|
||||
{ 0x1E, 0x08 },
|
||||
{ 0x1C, 0x1B },
|
||||
{ 0x1D, 0x80 },
|
||||
{ 0x1E, 0xD5 },
|
||||
{ 0xA2, 0x50 },
|
||||
{ 0xA2, 0x50 },
|
||||
{ 0x81, 0x01 }
|
||||
};
|
||||
|
||||
static struct aes_regwrite strip_scan_reqs[] = {
|
||||
{ 0xBE, 0x23 },
|
||||
{ 0x29, 0x04 },
|
||||
{ 0x2A, 0xFF },
|
||||
{ 0xBD, 0x4F },
|
||||
{ 0xFF, 0x00 }
|
||||
};
|
||||
|
||||
static const struct aes_regwrite capture_stop[] = {
|
||||
{ 0x81,0x00 }
|
||||
};
|
||||
|
||||
/*
|
||||
* The different possible values for 0xBE register */
|
||||
static unsigned char list_BE_values[10] = {
|
||||
0x23, 0x43, 0x63, 0x64, 0x65, 0x67, 0x6A, 0x6B
|
||||
};
|
||||
|
||||
/*
|
||||
* The different possible values for 0xBD register */
|
||||
static unsigned char list_BD_values[10] = {
|
||||
0x28, 0x2b, 0x30, 0x3b, 0x45, 0x49, 0x4B
|
||||
};
|
||||
|
||||
/*
|
||||
* Adjust the gain according to the histogram data
|
||||
* 0xbd, 0xbe, 0x29 and 0x2A registers are affected
|
||||
* Returns 0 if no problem occurred
|
||||
* TODO: This is a basic support for gain. It needs testing/tweaking. */
|
||||
static int adjust_gain(unsigned char *buffer, int status)
|
||||
{
|
||||
// The position in the array of possible values for 0xBE and 0xBD registers
|
||||
static int pos_list_BE = 0;
|
||||
static int pos_list_BD = 0;
|
||||
|
||||
// This is the first adjustment (we begin acquisition)
|
||||
// We adjust strip_scan_reqs for future strips and capture_reqs that is sent just after this step
|
||||
if (status == GAIN_STATUS_FIRST) {
|
||||
if (buffer[1] > 0x78) { // maximum gain needed
|
||||
strip_scan_reqs[0].value = 0x6B;
|
||||
strip_scan_reqs[1].value = 0x06;
|
||||
strip_scan_reqs[2].value = 0x35;
|
||||
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 = 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 = 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 = 0x28;
|
||||
}
|
||||
|
||||
// Now copy this values in capture_reqs
|
||||
capture_reqs[8].value = strip_scan_reqs[0].value;
|
||||
capture_reqs[9].value = strip_scan_reqs[1].value;
|
||||
capture_reqs[10].value = strip_scan_reqs[2].value;
|
||||
capture_reqs[21].value = strip_scan_reqs[3].value;
|
||||
|
||||
fp_dbg("first gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
|
||||
}
|
||||
|
||||
// Every 2/3 strips
|
||||
// We try to soften big changes of the gain (at least for 0xBE and 0xBD
|
||||
// FIXME: This softenning will need testing and tweaking too
|
||||
else if (status == GAIN_STATUS_NORMAL) {
|
||||
if (buffer[514] > 0x78) { // maximum gain needed
|
||||
if (pos_list_BE < 7)
|
||||
pos_list_BE++;
|
||||
|
||||
if (pos_list_BD < 6)
|
||||
pos_list_BD++;
|
||||
|
||||
strip_scan_reqs[1].value = 0x04;
|
||||
strip_scan_reqs[2].value = 0x35;
|
||||
}
|
||||
else if (buffer[514] > 0x55) {
|
||||
if (pos_list_BE < 2)
|
||||
pos_list_BE++;
|
||||
else if (pos_list_BE > 2)
|
||||
pos_list_BE--;
|
||||
|
||||
if (pos_list_BD < 2)
|
||||
pos_list_BD++;
|
||||
else if (pos_list_BD > 2)
|
||||
pos_list_BD--;
|
||||
|
||||
strip_scan_reqs[1].value = 0x15;
|
||||
strip_scan_reqs[2].value = 0x35;
|
||||
}
|
||||
else if (buffer[514] > 0x40 || buffer[529] > 0x19) {
|
||||
if (pos_list_BE < 1)
|
||||
pos_list_BE++;
|
||||
else if (pos_list_BE > 1)
|
||||
pos_list_BE--;
|
||||
|
||||
if (pos_list_BD < 1)
|
||||
pos_list_BD++;
|
||||
else if (pos_list_BD > 1)
|
||||
pos_list_BD--;
|
||||
|
||||
strip_scan_reqs[1].value = 0x13;
|
||||
strip_scan_reqs[2].value = 0x35;
|
||||
}
|
||||
else { // minimum gain needed
|
||||
if (pos_list_BE > 0)
|
||||
pos_list_BE--;
|
||||
|
||||
if (pos_list_BD > 0)
|
||||
pos_list_BD--;
|
||||
|
||||
strip_scan_reqs[1].value = 0x07;
|
||||
strip_scan_reqs[2].value = 0x35;
|
||||
}
|
||||
|
||||
strip_scan_reqs[0].value = list_BE_values[pos_list_BE];
|
||||
strip_scan_reqs[3].value = list_BD_values[pos_list_BD];
|
||||
|
||||
fp_dbg("gain: %x %x %x %x %x %x %x %x", strip_scan_reqs[0].reg, strip_scan_reqs[0].value, strip_scan_reqs[1].reg, strip_scan_reqs[1].value, strip_scan_reqs[2].reg, strip_scan_reqs[2].value, strip_scan_reqs[3].reg, strip_scan_reqs[3].value);
|
||||
}
|
||||
// Unknown status
|
||||
else {
|
||||
fp_err("Unexpected gain status.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the default gain values */
|
||||
static void restore_gain(void)
|
||||
{
|
||||
strip_scan_reqs[0].value = list_BE_values[0];
|
||||
strip_scan_reqs[1].value = 0x04;
|
||||
strip_scan_reqs[2].value = 0xFF;
|
||||
strip_scan_reqs[3].value = list_BD_values[0];
|
||||
|
||||
capture_reqs[8].value = list_BE_values[0];
|
||||
capture_reqs[9].value = 0x04;
|
||||
capture_reqs[10].value = 0xFF;
|
||||
capture_reqs[21].value = list_BD_values[0];
|
||||
}
|
||||
|
||||
|
||||
/* capture SM movement:
|
||||
* request and read strip,
|
||||
* jump back to request UNLESS there's no finger, in which case exit SM,
|
||||
* report lack of finger presence, and move to finger detection */
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_WRITE_REQS,
|
||||
CAPTURE_READ_DATA,
|
||||
CAPTURE_REQUEST_STRIP,
|
||||
CAPTURE_READ_STRIP,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void capture_read_strip_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
unsigned char *stripdata;
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
int sum, i;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME: would preallocating strip buffers be a decent optimization? */
|
||||
|
||||
sum = 0;
|
||||
for (i = 516; i < 530; i++)
|
||||
{
|
||||
/* histogram[i] = number of pixels of value i
|
||||
Only the pixel values from 10 to 15 are used to detect finger. */
|
||||
sum += data[i];
|
||||
}
|
||||
|
||||
if (sum > 0) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (sum < 0) {
|
||||
fpi_ssm_mark_failed(ssm, sum);
|
||||
goto out;
|
||||
}
|
||||
fp_dbg("sum=%d", sum);
|
||||
|
||||
/* FIXME: 0 might be too low as a threshold */
|
||||
/* FIXME: sometimes we get 0 in the middle of a scan, should we wait for
|
||||
* a few consecutive zeroes? */
|
||||
|
||||
/* If sum is 0 for a reasonable # of frames, finger has been removed */
|
||||
if (sum == 0) {
|
||||
aesdev->blanks_count++;
|
||||
fp_dbg("got blank frame");
|
||||
}
|
||||
|
||||
/* use histogram data above for gain calibration (0xbd, 0xbe, 0x29 and 0x2A ) */
|
||||
adjust_gain(data, GAIN_STATUS_NORMAL);
|
||||
|
||||
/* stop capturing if MAX_FRAMES is reached */
|
||||
if (aesdev->blanks_count > 10 || g_slist_length(aesdev->strips) >= MAX_FRAMES) {
|
||||
struct fp_img *img;
|
||||
|
||||
fp_dbg("sending stop capture.... blanks=%d frames=%d", aesdev->blanks_count, g_slist_length(aesdev->strips));
|
||||
/* send stop capture bits */
|
||||
aes_write_regv(dev, capture_stop, G_N_ELEMENTS(capture_stop), stub_capture_stop_cb, NULL);
|
||||
aesdev->strips = g_slist_reverse(aesdev->strips);
|
||||
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;
|
||||
aesdev->blanks_count = 0;
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
/* marking machine complete will re-trigger finger detection loop */
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
/* Acquisition finished: restore default gain values */
|
||||
restore_gain();
|
||||
} else {
|
||||
/* obtain next strip */
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
int r;
|
||||
|
||||
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),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case CAPTURE_READ_DATA:
|
||||
fp_dbg("read data");
|
||||
generic_read_ignore_data(ssm, _dev, STRIP_CAPTURE_LEN);
|
||||
break;
|
||||
case CAPTURE_REQUEST_STRIP:
|
||||
fp_dbg("request strip");
|
||||
if (aesdev->deactivating)
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
else
|
||||
aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case CAPTURE_READ_STRIP: ;
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(STRIP_CAPTURE_LEN);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, STRIP_CAPTURE_LEN,
|
||||
capture_read_strip_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (aesdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
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);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
static const struct aes_regwrite init[] = {
|
||||
{ 0x82, 0x00 }
|
||||
};
|
||||
|
||||
/*
|
||||
static const struct aes_regwrite stop_reader[] = {
|
||||
{ 0xFF, 0x00 }
|
||||
};
|
||||
*/
|
||||
|
||||
enum activate_states {
|
||||
WRITE_INIT,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
|
||||
/* activation on aes1610 seems much more straightforward compared to aes2501 */
|
||||
/* verify there's anything missing here */
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* jump to finger detection */
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
fp_dbg("status %d", fpi_ssm_get_error(ssm));
|
||||
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
|
||||
|
||||
if (!fpi_ssm_get_error(ssm))
|
||||
start_finger_detection(dev);
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, dev);
|
||||
aesdev->read_regs_retry_count = 0;
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
/* FIXME: audit cancellation points, probably need more, specifically
|
||||
* in error handling paths? */
|
||||
aesdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static void complete_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(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? */
|
||||
|
||||
aesdev->deactivating = FALSE;
|
||||
g_slist_free(aesdev->strips);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
aesdev->blanks_count = 0;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
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(fpi_dev_get_usb_dev(FP_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 aes1610_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes1610_dev *aesdev;
|
||||
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x1600 }, /* AES1600 */
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes1610_driver = {
|
||||
.driver = {
|
||||
.id = AES1610_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES1610",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = IMAGE_WIDTH,
|
||||
|
||||
.bz3_threshold = 20,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES1660 driver for libfprint
|
||||
* Copyright (C) 2012 Vasily Khoruzhick
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes1660"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aesx660.h"
|
||||
#include "aes1660.h"
|
||||
|
||||
#define FRAME_WIDTH 128
|
||||
#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)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct aesX660_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_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 aesX660_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
|
||||
aesdev->buffer = g_malloc0(AES1660_FRAME_SIZE + AESX660_HEADER_SIZE);
|
||||
aesdev->init_seqs[0] = 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] = 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->assembling_ctx = &assembling_ctx;
|
||||
aesdev->extra_img_flags = FP_IMG_PARTIAL;
|
||||
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev->buffer);
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x1660 },
|
||||
{ .vendor = 0x08ff, .product = 0x1680 },
|
||||
{ .vendor = 0x08ff, .product = 0x1681 },
|
||||
{ .vendor = 0x08ff, .product = 0x1682 },
|
||||
{ .vendor = 0x08ff, .product = 0x1683 },
|
||||
{ .vendor = 0x08ff, .product = 0x1684 },
|
||||
{ .vendor = 0x08ff, .product = 0x1685 },
|
||||
{ .vendor = 0x08ff, .product = 0x1686 },
|
||||
{ .vendor = 0x08ff, .product = 0x1687 },
|
||||
{ .vendor = 0x08ff, .product = 0x1688 },
|
||||
{ .vendor = 0x08ff, .product = 0x1689 },
|
||||
{ .vendor = 0x08ff, .product = 0x168a },
|
||||
{ .vendor = 0x08ff, .product = 0x168b },
|
||||
{ .vendor = 0x08ff, .product = 0x168c },
|
||||
{ .vendor = 0x08ff, .product = 0x168d },
|
||||
{ .vendor = 0x08ff, .product = 0x168e },
|
||||
{ .vendor = 0x08ff, .product = 0x168f },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes1660_driver = {
|
||||
.driver = {
|
||||
.id = AES1660_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES1660",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
.bz3_threshold = 20,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = aesX660_dev_activate,
|
||||
.deactivate = aesX660_dev_deactivate,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,877 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES2501 driver for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2007 Cyrille Bagard
|
||||
* Copyright (C) 2007-2008, 2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* Based on code from http://home.gna.org/aes2501, relicensed with permission
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes2501"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aes2501.h"
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void complete_deactivation(struct fp_img_dev *dev);
|
||||
|
||||
/* FIXME these need checking */
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
|
||||
#define BULK_TIMEOUT 4000
|
||||
|
||||
#define FINGER_DETECTION_LEN 20
|
||||
#define READ_REGS_LEN 126
|
||||
#define READ_REGS_RESP_LEN 159
|
||||
#define STRIP_CAPTURE_LEN 1705
|
||||
|
||||
/*
|
||||
* The AES2501 is an imaging device using a swipe-type sensor. It samples
|
||||
* the finger at preprogrammed intervals, sending a 192x16 frame to the
|
||||
* computer.
|
||||
* Unless the user is scanning their finger unreasonably fast, the frames
|
||||
* *will* overlap. The implementation below detects this overlap and produces
|
||||
* a contiguous image as the end result.
|
||||
* The fact that the user determines the length of the swipe (and hence the
|
||||
* number of useful frames) and also the fact that overlap varies means that
|
||||
* images returned from this driver vary in height.
|
||||
*/
|
||||
|
||||
#define FRAME_WIDTH 192
|
||||
#define FRAME_HEIGHT 16
|
||||
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
/* maximum number of frames to read during a scan */
|
||||
/* FIXME reduce substantially */
|
||||
#define MAX_FRAMES 150
|
||||
|
||||
/****** GENERAL FUNCTIONS ******/
|
||||
|
||||
struct aes2501_dev {
|
||||
uint8_t read_regs_retry_count;
|
||||
GSList *strips;
|
||||
size_t strips_len;
|
||||
gboolean deactivating;
|
||||
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);
|
||||
|
||||
struct aes2501_read_regs {
|
||||
struct fp_img_dev *dev;
|
||||
aes2501_read_regs_cb callback;
|
||||
struct aes_regwrite *regwrite;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static void read_regs_data_cb(struct libusb_transfer *transfer,
|
||||
struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data)
|
||||
{
|
||||
struct aes2501_read_regs *rdata = user_data;
|
||||
unsigned char *retdata = NULL;
|
||||
int r;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
r = -EIO;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
r = -EPROTO;
|
||||
} else {
|
||||
r = 0;
|
||||
retdata = transfer->buffer;
|
||||
}
|
||||
|
||||
rdata->callback(rdata->dev, r, retdata, rdata->user_data);
|
||||
g_free(rdata);
|
||||
}
|
||||
|
||||
static void read_regs_rq_cb(struct fp_img_dev *dev, int result, void *user_data)
|
||||
{
|
||||
struct aes2501_read_regs *rdata = user_data;
|
||||
fpi_usb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
g_free(rdata->regwrite);
|
||||
if (result != 0)
|
||||
goto err;
|
||||
|
||||
data = g_malloc(READ_REGS_LEN);
|
||||
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
|
||||
NULL,
|
||||
EP_IN,
|
||||
data,
|
||||
READ_REGS_LEN,
|
||||
read_regs_data_cb,
|
||||
rdata,
|
||||
BULK_TIMEOUT);
|
||||
|
||||
r = fpi_usb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
result = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
err:
|
||||
rdata->callback(dev, result, NULL, rdata->user_data);
|
||||
g_free(rdata);
|
||||
}
|
||||
|
||||
static void read_regs(struct fp_img_dev *dev, aes2501_read_regs_cb callback,
|
||||
void *user_data)
|
||||
{
|
||||
/* FIXME: regwrite is dynamic because of asynchronity. is this really
|
||||
* required? */
|
||||
struct aes_regwrite *regwrite = g_malloc(sizeof(*regwrite));
|
||||
struct aes2501_read_regs *rdata = g_malloc(sizeof(*rdata));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
regwrite->reg = AES2501_REG_CTRL2;
|
||||
regwrite->value = AES2501_CTRL2_READ_REGS;
|
||||
rdata->dev = dev;
|
||||
rdata->callback = callback;
|
||||
rdata->user_data = user_data;
|
||||
rdata->regwrite = regwrite;
|
||||
|
||||
aes_write_regv(dev, (const struct aes_regwrite *) regwrite, 1,
|
||||
read_regs_rq_cb, rdata);
|
||||
}
|
||||
|
||||
/* Read the value of a specific register from a register dump */
|
||||
static int regval_from_dump(unsigned char *data, uint8_t target)
|
||||
{
|
||||
if (*data != FIRST_AES2501_REG) {
|
||||
fp_err("not a register dump");
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
if (!(FIRST_AES2501_REG <= target && target <= LAST_AES2501_REG)) {
|
||||
fp_err("out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
target -= FIRST_AES2501_REG;
|
||||
target *= 2;
|
||||
return data[target + 1];
|
||||
}
|
||||
|
||||
static void generic_write_regv_cb(struct fp_img_dev *dev, int result,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_ssm *ssm = user_data;
|
||||
if (result == 0)
|
||||
fpi_ssm_next_state(ssm);
|
||||
else
|
||||
fpi_ssm_mark_failed(ssm, result);
|
||||
}
|
||||
|
||||
/* check that read succeeded but ignore all data */
|
||||
static void generic_ignore_data_cb(struct libusb_transfer *transfer,
|
||||
struct fp_dev *dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data)
|
||||
{
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
else if (transfer->length != transfer->actual_length)
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
|
||||
/* read the specified number of bytes from the IN endpoint but throw them
|
||||
* away, then increment the SSM */
|
||||
static void generic_read_ignore_data(fpi_ssm *ssm, struct fp_dev *dev, size_t bytes)
|
||||
{
|
||||
fpi_usb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
data = g_malloc(bytes);
|
||||
transfer = fpi_usb_fill_bulk_transfer(dev,
|
||||
ssm,
|
||||
EP_IN,
|
||||
data,
|
||||
bytes,
|
||||
generic_ignore_data_cb,
|
||||
NULL,
|
||||
BULK_TIMEOUT);
|
||||
|
||||
r = fpi_usb_submit_transfer(transfer);
|
||||
if (r < 0)
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
|
||||
/****** IMAGE PROCESSING ******/
|
||||
|
||||
static int sum_histogram_values(unsigned char *data, uint8_t threshold)
|
||||
{
|
||||
int r = 0;
|
||||
int i;
|
||||
uint16_t *histogram = (uint16_t *)(data + 1);
|
||||
|
||||
if (*data != 0xde)
|
||||
return -EILSEQ;
|
||||
|
||||
if (threshold > 0x0f)
|
||||
return -EINVAL;
|
||||
|
||||
/* FIXME endianness */
|
||||
for (i = threshold; i < 16; i++)
|
||||
r += histogram[i];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/****** FINGER PRESENCE DETECTION ******/
|
||||
|
||||
static const struct aes_regwrite finger_det_reqs[] = {
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_DETCTRL,
|
||||
AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS },
|
||||
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
|
||||
{ AES2501_REG_MEASDRV, AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE },
|
||||
{ AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M },
|
||||
{ AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE },
|
||||
{ AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE },
|
||||
{ AES2501_REG_CHANGAIN,
|
||||
AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X },
|
||||
{ AES2501_REG_ADREFHI, 0x44 },
|
||||
{ AES2501_REG_ADREFLO, 0x34 },
|
||||
{ AES2501_REG_STRTCOL, 0x16 },
|
||||
{ AES2501_REG_ENDCOL, 0x16 },
|
||||
{ AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 },
|
||||
{ AES2501_REG_TREG1, 0x70 },
|
||||
{ 0xa2, 0x02 },
|
||||
{ 0xa7, 0x00 },
|
||||
{ AES2501_REG_TREGC, AES2501_TREGC_ENABLE },
|
||||
{ AES2501_REG_TREGD, 0x1a },
|
||||
{ 0, 0 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
|
||||
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
|
||||
};
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev);
|
||||
|
||||
static void finger_det_data_cb(struct libusb_transfer *transfer,
|
||||
struct fp_dev *_dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
|
||||
unsigned char *data = transfer->buffer;
|
||||
int i;
|
||||
int sum = 0;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
return;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
return;
|
||||
}
|
||||
|
||||
/* examine histogram to determine finger presence */
|
||||
for (i = 1; i < 9; i++)
|
||||
sum += (data[i] & 0xf) + (data[i] >> 4);
|
||||
if (sum > 20) {
|
||||
/* finger present, start capturing */
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
start_capture(dev);
|
||||
} else {
|
||||
/* no finger, poll for a new histogram */
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void finger_det_reqs_cb(struct fp_img_dev *dev, int result,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_usb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
if (result) {
|
||||
fpi_imgdev_session_error(dev, result);
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_malloc(FINGER_DETECTION_LEN);
|
||||
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
|
||||
NULL,
|
||||
EP_IN,
|
||||
data,
|
||||
FINGER_DETECTION_LEN,
|
||||
finger_det_data_cb,
|
||||
NULL,
|
||||
BULK_TIMEOUT);
|
||||
|
||||
r = fpi_usb_submit_transfer(transfer);
|
||||
if (r < 0)
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
aes_write_regv(dev, finger_det_reqs, G_N_ELEMENTS(finger_det_reqs),
|
||||
finger_det_reqs_cb, NULL);
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
static const struct aes_regwrite capture_reqs_1[] = {
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ 0, 0 },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_DETCTRL,
|
||||
AES2501_DETCTRL_SDELAY_31_MS | AES2501_DETCTRL_DRATE_CONTINUOUS },
|
||||
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
|
||||
{ AES2501_REG_DEMODPHASE2, 0x7c },
|
||||
{ AES2501_REG_MEASDRV,
|
||||
AES2501_MEASDRV_MEASURE_SQUARE | AES2501_MEASDRV_MDRIVE_0_325 },
|
||||
{ AES2501_REG_DEMODPHASE1, 0x24 },
|
||||
{ AES2501_REG_CHWORD1, 0x00 },
|
||||
{ AES2501_REG_CHWORD2, 0x6c },
|
||||
{ AES2501_REG_CHWORD3, 0x09 },
|
||||
{ AES2501_REG_CHWORD4, 0x54 },
|
||||
{ AES2501_REG_CHWORD5, 0x78 },
|
||||
{ 0xa2, 0x02 },
|
||||
{ 0xa7, 0x00 },
|
||||
{ 0xb6, 0x26 },
|
||||
{ 0xb7, 0x1a },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
{ AES2501_REG_IMAGCTRL,
|
||||
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE |
|
||||
AES2501_IMAGCTRL_IMG_DATA_DISABLE },
|
||||
{ AES2501_REG_STRTCOL, 0x10 },
|
||||
{ AES2501_REG_ENDCOL, 0x1f },
|
||||
{ AES2501_REG_CHANGAIN,
|
||||
AES2501_CHANGAIN_STAGE1_2X | AES2501_CHANGAIN_STAGE2_2X },
|
||||
{ AES2501_REG_ADREFHI, 0x70 },
|
||||
{ AES2501_REG_ADREFLO, 0x20 },
|
||||
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
|
||||
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
|
||||
};
|
||||
|
||||
static const struct aes_regwrite capture_reqs_2[] = {
|
||||
{ AES2501_REG_IMAGCTRL,
|
||||
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE |
|
||||
AES2501_IMAGCTRL_IMG_DATA_DISABLE },
|
||||
{ AES2501_REG_STRTCOL, 0x10 },
|
||||
{ AES2501_REG_ENDCOL, 0x1f },
|
||||
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
|
||||
{ AES2501_REG_ADREFHI, 0x70 },
|
||||
{ AES2501_REG_ADREFLO, 0x20 },
|
||||
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
|
||||
};
|
||||
|
||||
static struct aes_regwrite strip_scan_reqs[] = {
|
||||
{ AES2501_REG_IMAGCTRL,
|
||||
AES2501_IMAGCTRL_TST_REG_ENABLE | AES2501_IMAGCTRL_HISTO_DATA_ENABLE },
|
||||
{ AES2501_REG_STRTCOL, 0x00 },
|
||||
{ AES2501_REG_ENDCOL, 0x2f },
|
||||
{ AES2501_REG_CHANGAIN, AES2501_CHANGAIN_STAGE1_16X },
|
||||
{ AES2501_REG_ADREFHI, AES2501_ADREFHI_MAX_VALUE },
|
||||
{ AES2501_REG_ADREFLO, 0x20 },
|
||||
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
|
||||
};
|
||||
|
||||
/* capture SM movement:
|
||||
* write reqs and read data 1 + 2,
|
||||
* request and read strip,
|
||||
* jump back to request UNLESS there's no finger, in which case exit SM,
|
||||
* report lack of finger presence, and move to finger detection */
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_WRITE_REQS_1,
|
||||
CAPTURE_READ_DATA_1,
|
||||
CAPTURE_WRITE_REQS_2,
|
||||
CAPTURE_READ_DATA_2,
|
||||
CAPTURE_REQUEST_STRIP,
|
||||
CAPTURE_READ_STRIP,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void capture_read_strip_cb(struct libusb_transfer *transfer,
|
||||
struct fp_dev *_dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data)
|
||||
{
|
||||
unsigned char *stripdata;
|
||||
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
unsigned char *data = transfer->buffer;
|
||||
int sum;
|
||||
int threshold;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
return;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
return;
|
||||
}
|
||||
|
||||
threshold = regval_from_dump(data + 1 + 192*8 + 1 + 16*2 + 1 + 8,
|
||||
AES2501_REG_DATFMT);
|
||||
if (threshold < 0) {
|
||||
fpi_ssm_mark_failed(ssm, threshold);
|
||||
return;
|
||||
}
|
||||
|
||||
sum = sum_histogram_values(data + 1 + 192*8, threshold & 0x0f);
|
||||
if (sum < 0) {
|
||||
fpi_ssm_mark_failed(ssm, sum);
|
||||
return;
|
||||
}
|
||||
fp_dbg("sum=%d", sum);
|
||||
|
||||
if (sum < AES2501_SUM_LOW_THRESH) {
|
||||
strip_scan_reqs[4].value -= 0x8;
|
||||
if (strip_scan_reqs[4].value < AES2501_ADREFHI_MIN_VALUE)
|
||||
strip_scan_reqs[4].value = AES2501_ADREFHI_MIN_VALUE;
|
||||
} else if (sum > AES2501_SUM_HIGH_THRESH) {
|
||||
strip_scan_reqs[4].value += 0x8;
|
||||
if (strip_scan_reqs[4].value > AES2501_ADREFHI_MAX_VALUE)
|
||||
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
|
||||
}
|
||||
fp_dbg("ADREFHI is %.2x", strip_scan_reqs[4].value);
|
||||
|
||||
/* Sum is 0, maybe finger was removed? Wait for 3 empty frames
|
||||
* to ensure
|
||||
*/
|
||||
if (sum == 0) {
|
||||
aesdev->no_finger_cnt++;
|
||||
if (aesdev->no_finger_cnt == 3) {
|
||||
struct fp_img *img;
|
||||
|
||||
aesdev->strips = g_slist_reverse(aesdev->strips);
|
||||
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;
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
/* marking machine complete will re-trigger finger detection loop */
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
|
||||
}
|
||||
} else {
|
||||
/* obtain next strip */
|
||||
/* FIXME: would preallocating strip buffers be a decent optimization? */
|
||||
struct 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, stripe);
|
||||
aesdev->strips_len++;
|
||||
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_REQUEST_STRIP);
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
int r;
|
||||
|
||||
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);
|
||||
break;
|
||||
case CAPTURE_READ_DATA_1:
|
||||
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
|
||||
break;
|
||||
case CAPTURE_WRITE_REQS_2:
|
||||
aes_write_regv(dev, capture_reqs_2, G_N_ELEMENTS(capture_reqs_2),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case CAPTURE_READ_DATA_2:
|
||||
generic_read_ignore_data(ssm, _dev, READ_REGS_RESP_LEN);
|
||||
break;
|
||||
case CAPTURE_REQUEST_STRIP:
|
||||
if (aesdev->deactivating)
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
else
|
||||
aes_write_regv(dev, strip_scan_reqs, G_N_ELEMENTS(strip_scan_reqs),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case CAPTURE_READ_STRIP: ;
|
||||
fpi_usb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(STRIP_CAPTURE_LEN);
|
||||
transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
|
||||
ssm,
|
||||
EP_IN,
|
||||
data,
|
||||
STRIP_CAPTURE_LEN,
|
||||
capture_read_strip_cb,
|
||||
NULL,
|
||||
BULK_TIMEOUT);
|
||||
|
||||
r = fpi_usb_submit_transfer(transfer);
|
||||
if (r < 0)
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (aesdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
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);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
aesdev->no_finger_cnt = 0;
|
||||
/* Reset gain */
|
||||
strip_scan_reqs[4].value = AES2501_ADREFHI_MAX_VALUE;
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
static const struct aes_regwrite init_1[] = {
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ 0, 0 },
|
||||
{ 0xb0, 0x27 }, /* Reserved? */
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ 0xff, 0x00 }, /* Reserved? */
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_DETCTRL,
|
||||
AES2501_DETCTRL_DRATE_CONTINUOUS | AES2501_DETCTRL_SDELAY_31_MS },
|
||||
{ AES2501_REG_COLSCAN, AES2501_COLSCAN_SRATE_128_US },
|
||||
{ AES2501_REG_MEASDRV,
|
||||
AES2501_MEASDRV_MDRIVE_0_325 | AES2501_MEASDRV_MEASURE_SQUARE },
|
||||
{ AES2501_REG_MEASFREQ, AES2501_MEASFREQ_2M },
|
||||
{ AES2501_REG_DEMODPHASE1, DEMODPHASE_NONE },
|
||||
{ AES2501_REG_DEMODPHASE2, DEMODPHASE_NONE },
|
||||
{ AES2501_REG_CHANGAIN,
|
||||
AES2501_CHANGAIN_STAGE2_4X | AES2501_CHANGAIN_STAGE1_16X },
|
||||
{ AES2501_REG_ADREFHI, 0x44 },
|
||||
{ AES2501_REG_ADREFLO, 0x34 },
|
||||
{ AES2501_REG_STRTCOL, 0x16 },
|
||||
{ AES2501_REG_ENDCOL, 0x16 },
|
||||
{ AES2501_REG_DATFMT, AES2501_DATFMT_BIN_IMG | 0x08 },
|
||||
{ AES2501_REG_TREG1, 0x70 },
|
||||
{ 0xa2, 0x02 },
|
||||
{ 0xa7, 0x00 },
|
||||
{ AES2501_REG_TREGC, AES2501_TREGC_ENABLE },
|
||||
{ AES2501_REG_TREGD, 0x1a },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
{ AES2501_REG_CTRL2, AES2501_CTRL2_SET_ONE_SHOT },
|
||||
{ AES2501_REG_LPONT, AES2501_LPONT_MIN_VALUE },
|
||||
};
|
||||
|
||||
static const struct aes_regwrite init_2[] = {
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
|
||||
{ AES2501_REG_EXCITCTRL, 0x42 },
|
||||
{ AES2501_REG_DETCTRL, 0x53 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
};
|
||||
|
||||
static const struct aes_regwrite init_3[] = {
|
||||
{ 0xff, 0x00 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
|
||||
{ AES2501_REG_EXCITCTRL, 0x42 },
|
||||
{ AES2501_REG_DETCTRL, 0x53 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
};
|
||||
|
||||
static const struct aes_regwrite init_4[] = {
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ 0xb0, 0x27 },
|
||||
{ AES2501_REG_ENDROW, 0x0a },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_REG_UPDATE },
|
||||
{ AES2501_REG_DETCTRL, 0x45 },
|
||||
{ AES2501_REG_AUTOCALOFFSET, 0x41 },
|
||||
};
|
||||
|
||||
static const struct aes_regwrite init_5[] = {
|
||||
{ 0xb0, 0x27 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ 0xff, 0x00 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_MASTER_RESET },
|
||||
{ AES2501_REG_EXCITCTRL, 0x40 },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET },
|
||||
{ AES2501_REG_CTRL1, AES2501_CTRL1_SCAN_RESET },
|
||||
};
|
||||
|
||||
enum activate_states {
|
||||
WRITE_INIT_1,
|
||||
READ_DATA_1,
|
||||
WRITE_INIT_2,
|
||||
READ_REGS,
|
||||
WRITE_INIT_3,
|
||||
WRITE_INIT_4,
|
||||
WRITE_INIT_5,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
void activate_read_regs_cb(struct fp_img_dev *dev, int status,
|
||||
unsigned char *regs, void *user_data)
|
||||
{
|
||||
fpi_ssm *ssm = user_data;
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (status != 0) {
|
||||
fpi_ssm_mark_failed(ssm, status);
|
||||
} else {
|
||||
fp_dbg("reg 0xaf = %x", regs[0x5f]);
|
||||
if (regs[0x5f] != 0x6b || ++aesdev->read_regs_retry_count == 13)
|
||||
fpi_ssm_jump_to_state(ssm, WRITE_INIT_4);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_init3_cb(struct fp_img_dev *dev, int result,
|
||||
void *user_data)
|
||||
{
|
||||
fpi_ssm *ssm = user_data;
|
||||
if (result == 0)
|
||||
fpi_ssm_jump_to_state(ssm, READ_REGS);
|
||||
else
|
||||
fpi_ssm_mark_failed(ssm, result);
|
||||
}
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
|
||||
/* 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
|
||||
* waiting for a specific value. READ_REGS checks the register value, and
|
||||
* if we're ready to move on, we jump to init4. Otherwise, we write init3
|
||||
* and then jump back to READ_REGS. In a synchronous model:
|
||||
|
||||
[...]
|
||||
aes_write_regv(init_2);
|
||||
read_regs(into buffer);
|
||||
i = 0;
|
||||
while (buffer[0x5f] == 0x6b) {
|
||||
aes_write_regv(init_3);
|
||||
read_regs(into buffer);
|
||||
if (++i == 13)
|
||||
break;
|
||||
}
|
||||
aes_write_regv(init_4);
|
||||
*/
|
||||
|
||||
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);
|
||||
break;
|
||||
case READ_DATA_1:
|
||||
fp_dbg("read data 1");
|
||||
generic_read_ignore_data(ssm, _dev, FINGER_DETECTION_LEN);
|
||||
break;
|
||||
case WRITE_INIT_2:
|
||||
aes_write_regv(dev, init_2, G_N_ELEMENTS(init_2),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case READ_REGS:
|
||||
read_regs(dev, activate_read_regs_cb, ssm);
|
||||
break;
|
||||
case WRITE_INIT_3:
|
||||
aes_write_regv(dev, init_3, G_N_ELEMENTS(init_3),
|
||||
activate_init3_cb, ssm);
|
||||
break;
|
||||
case WRITE_INIT_4:
|
||||
aes_write_regv(dev, init_4, G_N_ELEMENTS(init_4),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
case WRITE_INIT_5:
|
||||
aes_write_regv(dev, init_5, G_N_ELEMENTS(init_5),
|
||||
generic_write_regv_cb, ssm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
fp_dbg("status %d", fpi_ssm_get_error(ssm));
|
||||
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
|
||||
|
||||
if (!fpi_ssm_get_error(ssm))
|
||||
start_finger_detection(dev);
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, dev);
|
||||
aesdev->read_regs_retry_count = 0;
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
/* FIXME: audit cancellation points, probably need more, specifically
|
||||
* in error handling paths? */
|
||||
aesdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static void complete_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(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? */
|
||||
|
||||
aesdev->deactivating = FALSE;
|
||||
g_slist_free(aesdev->strips);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
/* FIXME check endpoints */
|
||||
int r;
|
||||
struct aes2501_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_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 aes2501_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2501_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x2500 }, /* AES2500 */
|
||||
{ .vendor = 0x08ff, .product = 0x2580 }, /* AES2501 */
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes2501_driver = {
|
||||
.driver = {
|
||||
.id = AES2501_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES2501",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = IMAGE_WIDTH,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES2501 driver for libfprint
|
||||
* Copyright (C) 2007 Cyrille Bagard
|
||||
*
|
||||
* Based on code from http://home.gna.org/aes2501, relicensed with permission
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AES2501_H
|
||||
#define __AES2501_H
|
||||
|
||||
enum aes2501_regs {
|
||||
AES2501_REG_CTRL1 = 0x80,
|
||||
AES2501_REG_CTRL2 = 0x81,
|
||||
AES2501_REG_EXCITCTRL = 0x82, /* excitation control */
|
||||
AES2501_REG_DETCTRL = 0x83, /* detect control */
|
||||
AES2501_REG_COLSCAN = 0x88, /* column scan rate register */
|
||||
AES2501_REG_MEASDRV = 0x89, /* measure drive */
|
||||
AES2501_REG_MEASFREQ = 0x8a, /* measure frequency */
|
||||
AES2501_REG_DEMODPHASE1 = 0x8d,
|
||||
AES2501_REG_DEMODPHASE2 = 0x8c,
|
||||
AES2501_REG_CHANGAIN = 0x8e, /* channel gain */
|
||||
AES2501_REG_ADREFHI = 0x91, /* A/D reference high */
|
||||
AES2501_REG_ADREFLO = 0x92, /* A/D reference low */
|
||||
AES2501_REG_STRTROW = 0x93, /* start row */
|
||||
AES2501_REG_ENDROW = 0x94, /* end row */
|
||||
AES2501_REG_STRTCOL = 0x95, /* start column */
|
||||
AES2501_REG_ENDCOL = 0x96, /* end column */
|
||||
AES2501_REG_DATFMT = 0x97, /* data format */
|
||||
AES2501_REG_IMAGCTRL = 0x98, /* image data */
|
||||
AES2501_REG_STAT = 0x9a,
|
||||
AES2501_REG_CHWORD1 = 0x9b, /* challenge word 1 */
|
||||
AES2501_REG_CHWORD2 = 0x9c,
|
||||
AES2501_REG_CHWORD3 = 0x9d,
|
||||
AES2501_REG_CHWORD4 = 0x9e,
|
||||
AES2501_REG_CHWORD5 = 0x9f,
|
||||
AES2501_REG_TREG1 = 0xa1, /* test register 1 */
|
||||
AES2501_REG_AUTOCALOFFSET = 0xa8,
|
||||
AES2501_REG_TREGC = 0xac,
|
||||
AES2501_REG_TREGD = 0xad,
|
||||
AES2501_REG_LPONT = 0xb4, /* low power oscillator on time */
|
||||
};
|
||||
|
||||
#define FIRST_AES2501_REG AES2501_REG_CTRL1
|
||||
#define LAST_AES2501_REG AES2501_REG_CHWORD5
|
||||
|
||||
#define AES2501_CTRL1_MASTER_RESET (1<<0)
|
||||
#define AES2501_CTRL1_SCAN_RESET (1<<1) /* stop + restart scan sequencer */
|
||||
/* 1 = continuously updated, 0 = updated prior to starting a scan */
|
||||
#define AES2501_CTRL1_REG_UPDATE (1<<2)
|
||||
|
||||
/* 1 = continuous scans, 0 = single scans */
|
||||
#define AES2501_CTRL2_CONTINUOUS 0x01
|
||||
#define AES2501_CTRL2_READ_REGS 0x02 /* dump registers */
|
||||
#define AES2501_CTRL2_SET_ONE_SHOT 0x04
|
||||
#define AES2501_CTRL2_CLR_ONE_SHOT 0x08
|
||||
#define AES2501_CTRL2_READ_ID 0x10
|
||||
|
||||
enum aes2501_detection_rate {
|
||||
/* rate of detection cycles: */
|
||||
AES2501_DETCTRL_DRATE_CONTINUOUS = 0x00, /* continuously */
|
||||
AES2501_DETCTRL_DRATE_16_MS = 0x01, /* every 16.62ms */
|
||||
AES2501_DETCTRL_DRATE_31_MS = 0x02, /* every 31.24ms */
|
||||
AES2501_DETCTRL_DRATE_62_MS = 0x03, /* every 62.50ms */
|
||||
AES2501_DETCTRL_DRATE_125_MS = 0x04, /* every 125.0ms */
|
||||
AES2501_DETCTRL_DRATE_250_MS = 0x05, /* every 250.0ms */
|
||||
AES2501_DETCTRL_DRATE_500_MS = 0x06, /* every 500.0ms */
|
||||
AES2501_DETCTRL_DRATE_1_S = 0x07, /* every 1s */
|
||||
};
|
||||
|
||||
enum aes2501_settling_delay {
|
||||
AES2501_DETCTRL_SDELAY_31_MS = 0x00, /* 31.25ms */
|
||||
AES2501_DETCTRL_SSDELAY_62_MS = 0x10, /* 62.5ms */
|
||||
AES2501_DETCTRL_SSDELAY_125_MS = 0x20, /* 125ms */
|
||||
AES2501_DETCTRL_SSDELAY_250_MS = 0x30 /* 250ms */
|
||||
};
|
||||
|
||||
enum aes2501_col_scan_rate {
|
||||
AES2501_COLSCAN_SRATE_32_US = 0x00, /* 32us */
|
||||
AES2501_COLSCAN_SRATE_64_US = 0x01, /* 64us */
|
||||
AES2501_COLSCAN_SRATE_128_US = 0x02, /* 128us */
|
||||
AES2501_COLSCAN_SRATE_256_US = 0x03, /* 256us */
|
||||
AES2501_COLSCAN_SRATE_512_US = 0x04, /* 512us */
|
||||
AES2501_COLSCAN_SRATE_1024_US = 0x05, /* 1024us */
|
||||
AES2501_COLSCAN_SRATE_2048_US = 0x06, /* 2048us */
|
||||
|
||||
};
|
||||
|
||||
enum aes2501_mesure_drive {
|
||||
AES2501_MEASDRV_MDRIVE_0_325 = 0x00, /* 0.325 Vpp */
|
||||
AES2501_MEASDRV_MDRIVE_0_65 = 0x01, /* 0.65 Vpp */
|
||||
AES2501_MEASDRV_MDRIVE_1_3 = 0x02, /* 1.3 Vpp */
|
||||
AES2501_MEASDRV_MDRIVE_2_6 = 0x03 /* 2.6 Vpp */
|
||||
|
||||
};
|
||||
|
||||
/* Select (1=square | 0=sine) wave drive during measure */
|
||||
#define AES2501_MEASDRV_SQUARE 0x20
|
||||
/* 0 = use measure drive setting, 1 = when sine wave is selected */
|
||||
#define AES2501_MEASDRV_MEASURE_SQUARE 0x10
|
||||
|
||||
enum aes2501_measure_freq {
|
||||
AES2501_MEASFREQ_125K = 0x01, /* 125 kHz */
|
||||
AES2501_MEASFREQ_250K = 0x02, /* 250 kHz */
|
||||
AES2501_MEASFREQ_500K = 0x03, /* 500 kHz */
|
||||
AES2501_MEASFREQ_1M = 0x04, /* 1 MHz */
|
||||
AES2501_MEASFREQ_2M = 0x05 /* 2 MHz */
|
||||
};
|
||||
|
||||
#define DEMODPHASE_NONE 0x00
|
||||
#define DEMODPHASE_180_00 0x40 /* 180 degrees */
|
||||
#define DEMODPHASE_2_81 0x01 /* 2.8125 degrees */
|
||||
|
||||
#define AES2501_REG_DEMODPHASE1 0x8d
|
||||
#define DEMODPHASE_1_40 0x40 /* 1.40625 degrees */
|
||||
#define DEMODPHASE_0_02 0x01 /* 0.02197256 degrees */
|
||||
|
||||
enum aes2501_sensor_gain1 {
|
||||
AES2501_CHANGAIN_STAGE1_2X = 0x00, /* 2x */
|
||||
AES2501_CHANGAIN_STAGE1_4X = 0x01, /* 4x */
|
||||
AES2501_CHANGAIN_STAGE1_8X = 0x02, /* 8x */
|
||||
AES2501_CHANGAIN_STAGE1_16X = 0x03 /* 16x */
|
||||
};
|
||||
|
||||
enum aes2501_sensor_gain2 {
|
||||
AES2501_CHANGAIN_STAGE2_2X = 0x00, /* 2x */
|
||||
AES2501_CHANGAIN_STAGE2_4X = 0x10, /* 4x */
|
||||
AES2501_CHANGAIN_STAGE2_8X = 0x20, /* 8x */
|
||||
AES2501_CHANGAIN_STAGE2_16X = 0x30 /* 16x */
|
||||
};
|
||||
|
||||
#define AES2501_DATFMT_EIGHT 0x40 /* 1 = 8-bit data, 0 = 4-bit data */
|
||||
#define AES2501_DATFMT_LOW_RES 0x20
|
||||
#define AES2501_DATFMT_BIN_IMG 0x10
|
||||
|
||||
/* don't send image or authentication messages when imaging */
|
||||
#define AES2501_IMAGCTRL_IMG_DATA_DISABLE 0x01
|
||||
/* send histogram when imaging */
|
||||
#define AES2501_IMAGCTRL_HISTO_DATA_ENABLE 0x02
|
||||
/* send histogram at end of each row rather than each scan */
|
||||
#define AES2501_IMAGCTRL_HISTO_EACH_ROW 0x04
|
||||
/* send full image array rather than 64x64 center */
|
||||
#define AES2501_IMAGCTRL_HISTO_FULL_ARRAY 0x08
|
||||
/* return registers before data (rather than after) */
|
||||
#define AES2501_IMAGCTRL_REG_FIRST 0x10
|
||||
/* return test registers with register dump */
|
||||
#define AES2501_IMAGCTRL_TST_REG_ENABLE 0x20
|
||||
|
||||
#define AES2501_CHWORD1_IS_FINGER 0x01 /* If set, finger is present */
|
||||
|
||||
/* Enable the reading of the register in TREGD */
|
||||
#define AES2501_TREGC_ENABLE 0x01
|
||||
|
||||
#define AES2501_LPONT_MIN_VALUE 0x00 /* 0 ms */
|
||||
#define AES2501_LPONT_MAX_VALUE 0x1f /* About 16 ms */
|
||||
|
||||
#define AES2501_ADREFHI_MIN_VALUE 0x28
|
||||
#define AES2501_ADREFHI_MAX_VALUE 0x58
|
||||
|
||||
#define AES2501_SUM_HIGH_THRESH 1000
|
||||
#define AES2501_SUM_LOW_THRESH 700
|
||||
|
||||
#endif /* __AES2501_H */
|
|
@ -1,620 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES2550/AES2810 driver for libfprint
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2007 Cyrille Bagard
|
||||
* Copyright (C) 2007-2012 Vasily Khoruzhick
|
||||
*
|
||||
* Based on AES2501 driver
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes2550"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aes2550.h"
|
||||
#include "aeslib.h"
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void complete_deactivation(struct fp_img_dev *dev);
|
||||
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
#define BULK_TIMEOUT 4000
|
||||
|
||||
/*
|
||||
* The AES2550 is an imaging device using a swipe-type sensor. It samples
|
||||
* the finger at preprogrammed intervals, sending a 192x16 frame to the
|
||||
* computer.
|
||||
* Unless the user is scanning their finger unreasonably fast, the frames
|
||||
* *will* overlap. The implementation below detects this overlap and produces
|
||||
* a contiguous image as the end result.
|
||||
* The fact that the user determines the length of the swipe (and hence the
|
||||
* number of useful frames) and also the fact that overlap varies means that
|
||||
* images returned from this driver vary in height.
|
||||
*/
|
||||
|
||||
#define FRAME_WIDTH 192
|
||||
#define FRAME_HEIGHT 8
|
||||
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT)
|
||||
#define IMAGE_WIDTH (FRAME_WIDTH + (FRAME_WIDTH / 2))
|
||||
|
||||
struct aes2550_dev {
|
||||
GSList *strips;
|
||||
size_t strips_len;
|
||||
gboolean deactivating;
|
||||
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[] = {
|
||||
0x80, AES2550_REG80_MASTER_RESET,
|
||||
0x95, (8 << AES2550_REG95_COL_SCANNED_OFS) | (1 << AES2550_REG95_EPIX_AVG_OFS),
|
||||
0xad, 0x00,
|
||||
0xbd, (0 << AES2550_REGBD_LPO_IN_15_8_OFS),
|
||||
0xbe, (0 << AES2550_REGBE_LPO_IN_7_0_OFS),
|
||||
0xcf, AES2550_REGCF_INTERFERENCE_CHK_EN,
|
||||
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
|
||||
AES2550_CMD_RUN_FD,
|
||||
};
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev);
|
||||
|
||||
static void finger_det_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("data transfer status %d\n", transfer->status);
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fp_dbg("transfer completed, len: %.4x, data: %.2x %.2x",
|
||||
transfer->actual_length, (int)data[0], (int)data[1]);
|
||||
|
||||
/* Check if we got 2 bytes, reg address 0x83 and its value */
|
||||
if ((transfer->actual_length >= 2) && (data[0] == 0x83) && (data[1] & AES2550_REG83_FINGER_PRESENT)) {
|
||||
/* finger present, start capturing */
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
start_capture(dev);
|
||||
} else {
|
||||
/* no finger, poll for a new histogram */
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
out:
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void finger_det_reqs_cb(struct libusb_transfer *t)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
struct fp_img_dev *dev = t->user_data;
|
||||
|
||||
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("req transfer status %d\n", t->status);
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto exit_free_transfer;
|
||||
} else if (t->length != t->actual_length) {
|
||||
fp_dbg("expected %d, got %d bytes", t->length, t->actual_length);
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
goto exit_free_transfer;
|
||||
}
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
/* 2 bytes of result */
|
||||
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
|
||||
finger_det_data_cb, dev, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
exit_free_transfer:
|
||||
libusb_free_transfer(t);
|
||||
}
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev)
|
||||
{
|
||||
int r;
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
struct libusb_transfer *transfer;
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
static unsigned char capture_reqs[] = {
|
||||
0x80, AES2550_REG80_MASTER_RESET,
|
||||
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_HGC_ENABLE),
|
||||
0x85, AES2550_REG85_FLUSH_PER_FRAME,
|
||||
0x8f, AES2550_REG8F_AUTH_DISABLE | AES2550_REG8F_EHISTO_DISABLE,
|
||||
0xbf, AES2550_REGBF_RSR_DIR_UPDOWN_MOTION | AES2550_REGBF_RSR_LEVEL_SUPER_RSR,
|
||||
0xcf, (3 << AES2550_REGCF_INTERFERENCE_AVG_OFFS) | AES2550_REGCF_INTERFERENCE_AVG_EN,
|
||||
0xdc, (1 << AES2550_REGDC_BP_NUM_REF_SWEEP_OFS),
|
||||
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x03, /* Heart beat cmd, 3 * 16 cycles without sending image */
|
||||
AES2550_CMD_GET_ENROLL_IMG,
|
||||
};
|
||||
|
||||
static unsigned char capture_set_idle_reqs[] = {
|
||||
0x80, AES2550_REG80_MASTER_RESET,
|
||||
AES2550_CMD_HEARTBEAT, 0x00, 0x01, 0x00, /* Heart beat off */
|
||||
AES2550_CMD_SET_IDLE_MODE,
|
||||
};
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_WRITE_REQS,
|
||||
CAPTURE_READ_DATA,
|
||||
CAPTURE_SET_IDLE,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
/* Returns number of processed bytes */
|
||||
static int process_strip_data(fpi_ssm *ssm, struct fp_img_dev *dev, unsigned char *data)
|
||||
{
|
||||
unsigned char *stripdata;
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
struct fpi_frame *stripe;
|
||||
int len;
|
||||
|
||||
if (data[0] != AES2550_EDATA_MAGIC) {
|
||||
fp_dbg("Bogus magic: %.2x\n", (int)(data[0]));
|
||||
return -EPROTO;
|
||||
}
|
||||
len = data[1] * 256 + data[2];
|
||||
if (len != (AES2550_STRIP_SIZE - 3)) {
|
||||
fp_dbg("Bogus frame len: %.4x\n", len);
|
||||
}
|
||||
stripe = g_malloc(FRAME_WIDTH * FRAME_HEIGHT / 2 + sizeof(struct 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, stripe);
|
||||
aesdev->strips_len++;
|
||||
|
||||
fp_dbg("deltas: %dx%d", stripe->delta_x, stripe->delta_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void capture_reqs_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
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_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_set_idle_reqs_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length) &&
|
||||
aesdev->strips_len) {
|
||||
struct fp_img *img;
|
||||
|
||||
aesdev->strips = g_slist_reverse(aesdev->strips);
|
||||
img = 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;
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
/* marking machine complete will re-trigger finger detection loop */
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
int r;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("request is not completed, %d", transfer->status);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fp_dbg("request completed, len: %.4x", transfer->actual_length);
|
||||
if (transfer->actual_length >= 2)
|
||||
fp_dbg("data: %.2x %.2x", (int)data[0], (int)data[1]);
|
||||
|
||||
switch (transfer->actual_length) {
|
||||
case AES2550_STRIP_SIZE:
|
||||
r = process_strip_data(ssm, dev, data);
|
||||
if (r < 0) {
|
||||
fp_dbg("Processing strip data failed: %d", r);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
aesdev->heartbeat_cnt = 0;
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
|
||||
break;
|
||||
case AES2550_HEARTBEAT_SIZE:
|
||||
if (data[0] == AES2550_HEARTBEAT_MAGIC) {
|
||||
/* No data for a long time => finger was removed or there's no movement */
|
||||
aesdev->heartbeat_cnt++;
|
||||
if (aesdev->heartbeat_cnt == 3) {
|
||||
/* Got 3 heartbeat message, that's enough to consider that finger was removed,
|
||||
* assemble image and submit it to the library */
|
||||
fp_dbg("Got 3 heartbeats => finger removed");
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fp_dbg("Short frame %d, skip", transfer->actual_length);
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_DATA);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CAPTURE_WRITE_REQS:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, capture_reqs,
|
||||
sizeof(capture_reqs), capture_reqs_cb, ssm, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAPTURE_READ_DATA:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
|
||||
capture_read_data_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAPTURE_SET_IDLE:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
fp_dbg("Capture completed");
|
||||
if (aesdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
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);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
aesdev->heartbeat_cnt = 0;
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
static unsigned char init_reqs[] = {
|
||||
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
|
||||
0x80, (1 << AES2550_REG80_SENSOR_MODE_OFS) | (AES2550_REG80_FORCE_FINGER_PRESENT),
|
||||
0x85, AES2550_REG85_FLUSH_PER_FRAME,
|
||||
0xa8, AES2550_REGA8_DIG_BIT_EN,
|
||||
0x81, AES2550_REG81_NSHOT,
|
||||
};
|
||||
|
||||
static unsigned char calibrate_reqs[] = {
|
||||
0x80, AES2550_REG80_MASTER_RESET, /* Master reset */
|
||||
AES2550_CMD_CALIBRATE,
|
||||
AES2550_CMD_READ_CALIBRATION_DATA,
|
||||
};
|
||||
|
||||
enum activate_states {
|
||||
WRITE_INIT,
|
||||
READ_DATA,
|
||||
CALIBRATE,
|
||||
READ_CALIB_TABLE,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void init_reqs_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
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_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void init_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
/* TODO: use calibration table, datasheet is rather terse on that
|
||||
* need more info for implementation */
|
||||
static void calibrate_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case WRITE_INIT:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, init_reqs,
|
||||
sizeof(init_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case READ_DATA:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
|
||||
init_read_data_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CALIBRATE:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT, calibrate_reqs,
|
||||
sizeof(calibrate_reqs), init_reqs_cb, ssm, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case READ_CALIB_TABLE:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(AES2550_EP_IN_BUF_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN, data, AES2550_EP_IN_BUF_SIZE,
|
||||
calibrate_read_data_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
fp_dbg("status %d", fpi_ssm_get_error(ssm));
|
||||
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
|
||||
|
||||
if (!fpi_ssm_get_error(ssm))
|
||||
start_finger_detection(dev);
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
aesdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static void complete_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2550_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
G_DEBUG_HERE();
|
||||
|
||||
aesdev->deactivating = FALSE;
|
||||
g_slist_free(aesdev->strips);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct aes2550_dev *aes2550_dev;
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
if (r < 0) {
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
aes2550_dev = g_malloc0(sizeof(struct aes2550_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), aes2550_dev);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aes2550_dev *aesdev;
|
||||
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x2550 }, /* AES2550 */
|
||||
{ .vendor = 0x08ff, .product = 0x2810 }, /* AES2810 */
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes2550_driver = {
|
||||
.driver = {
|
||||
.id = AES2550_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES2550/AES2810",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES2550/AES2810 driver for libfprint
|
||||
* Copyright (C) 2012 Vasily Khoruzhick
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AES2550_H
|
||||
#define __AES2550_H
|
||||
|
||||
/* Registers bits */
|
||||
|
||||
#define AES2550_REG80_MASTER_RESET (1 << 0)
|
||||
#define AES2550_REG80_FORCE_FINGER_PRESENT (1 << 1)
|
||||
#define AES2550_REG80_LPO_START (1 << 2)
|
||||
#define AES2550_REG80_HGC_ENABLE (1 << 3)
|
||||
#define AES2550_REG80_SENSOR_MODE_OFS (4)
|
||||
#define AES2550_REG80_AUTO_RESTART_FD (1 << 6)
|
||||
#define AES2550_REG80_EXT_REG_ENABLE (1 << 7)
|
||||
|
||||
#define AES2550_REG81_CONT_SCAN (1 << 0)
|
||||
#define AES2550_REG81_READ_REG (1 << 1)
|
||||
#define AES2550_REG81_NSHOT (1 << 2)
|
||||
#define AES2550_REG81_RUN_FD (1 << 3)
|
||||
#define AES2550_REG81_READ_ID (1 << 4)
|
||||
#define AES2550_REG81_RUN_CAL (1 << 5)
|
||||
#define AES2550_REG81_RUN_TIMER (1 << 6)
|
||||
#define AES2550_REG81_RUN_BIST (1 << 7)
|
||||
|
||||
#define AES2550_REG83_FINGER_PRESENT (1 << 7)
|
||||
|
||||
#define AES2550_REG85_FLUSH_PER_FRAME (1 << 7)
|
||||
|
||||
#define AES2550_REG8F_EDATA_DISABLE (1 << 1)
|
||||
#define AES2550_REG8F_AUTH_DISABLE (1 << 2)
|
||||
#define AES2550_REG8F_EHISTO_DISABLE (1 << 3)
|
||||
#define AES2550_REG8F_HISTO64 (1 << 4)
|
||||
#define AES2550_REG8F_SINGLE_REG_ENABLE (1 << 6)
|
||||
|
||||
#define AES2550_REG95_COL_SCANNED_OFS (0)
|
||||
#define AES2550_REG95_EPIX_AVG_OFS (4)
|
||||
|
||||
#define AES2550_REGA8_DIG_BIT_DATA_OFS (0)
|
||||
#define AES2550_REGA8_DIG_BIT_EN (1 << 4)
|
||||
#define AES2550_REGA8_FIXED_BIT_DATA (1 << 5)
|
||||
#define AES2550_REGA8_INVERT_BIT_DATA (1 << 6)
|
||||
|
||||
#define AES2550_REGAD_LPFD_AVG_OFS (0)
|
||||
#define AES2550_REGAD_DETECT_FGROFF (1 << 4)
|
||||
#define AES2550_REGAD_ADVRANGE_2V (1 << 6)
|
||||
|
||||
#define AES2550_REGB1_ATE_CONT_IMAGE (1 << 1)
|
||||
#define AES2550_REGB1_ANALOG_RESET (1 << 2)
|
||||
#define AES2550_REGB1_ANALOG_PD (1 << 3)
|
||||
#define AES2550_REGB1_TEST_EMBD_WORD (1 << 4)
|
||||
#define AES2550_REGB1_ORIG_EMBD_WORD (1 << 5)
|
||||
#define AES2550_REGB1_RESET_UHSM (1 << 6)
|
||||
#define AES2550_REGB1_RESET_SENSOR (1 << 7)
|
||||
|
||||
#define AES2550_REGBD_LPO_IN_15_8_OFS (0)
|
||||
#define AES2550_REGBE_LPO_IN_7_0_OFS (0)
|
||||
|
||||
#define AES2550_REGBF_RSR_LEVEL_DISABLED (0 << 0)
|
||||
#define AES2550_REGBF_RSR_LEVEL_LEADING_RSR (1 << 0)
|
||||
#define AES2550_REGBF_RSR_LEVEL_SIMPLE_RSR (2 << 0)
|
||||
#define AES2550_REGBF_RSR_LEVEL_SUPER_RSR (3 << 0)
|
||||
#define AES2550_REGBF_RSR_DIR_DOWN_MOTION (0 << 2)
|
||||
#define AES2550_REGBF_RSR_DIR_UP_MOTION (1 << 2)
|
||||
#define AES2550_REGBF_RSR_DIR_UPDOWN_MOTION (2 << 2)
|
||||
#define AES2550_REGBF_NOISE_FLOOR_MODE (1 << 4)
|
||||
#define AES2550_REGBF_QUADRATURE_MODE (1 << 5)
|
||||
|
||||
#define AES2550_REGCF_INTERFERENCE_CHK_EN (1 << 0)
|
||||
#define AES2550_REGCF_INTERFERENCE_AVG_EN (1 << 1)
|
||||
#define AES2550_REGCF_INTERFERENCE_AVG_OFFS (4)
|
||||
|
||||
#define AES2550_REGDC_BP_NUM_REF_SWEEP_OFS (0)
|
||||
#define AES2550_REGDC_DEBUG_CTRL2_OFS (3)
|
||||
|
||||
#define AES2550_REGDD_DEBUG_CTRL1_OFS (0)
|
||||
|
||||
/* Commands */
|
||||
|
||||
enum aes2550_cmds {
|
||||
AES2550_CMD_SET_IDLE_MODE = 0x00,
|
||||
AES2550_CMD_RUN_FD = 0x01,
|
||||
AES2550_CMD_GET_ENROLL_IMG = 0x02,
|
||||
AES2550_CMD_CALIBRATE = 0x06,
|
||||
AES2550_CMD_READ_CALIBRATION_DATA = 0x10,
|
||||
AES2550_CMD_HEARTBEAT = 0x70,
|
||||
};
|
||||
|
||||
/* Messages */
|
||||
|
||||
#define AES2550_STRIP_SIZE (0x31e + 3)
|
||||
#define AES2550_HEARTBEAT_SIZE (4 + 3)
|
||||
#define AES2550_EDATA_MAGIC 0xe0
|
||||
#define AES2550_HEARTBEAT_MAGIC 0xdb
|
||||
|
||||
#define AES2550_EP_IN_BUF_SIZE 8192
|
||||
|
||||
#endif
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES2660 driver for libfprint
|
||||
* Copyright (C) 2012 Vasily Khoruzhick
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aes2660"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aesx660.h"
|
||||
#include "aes2660.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)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct aesX660_dev *aesdev;
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_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 aesX660_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), aesdev);
|
||||
aesdev->buffer = g_malloc0(AES2660_FRAME_SIZE + AESX660_HEADER_SIZE);
|
||||
/* No scaling for AES2660 */
|
||||
aesdev->init_seqs[0] = aes2660_init_1;
|
||||
aesdev->init_seqs_len[0] = G_N_ELEMENTS(aes2660_init_1);
|
||||
aesdev->init_seqs[1] = 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->assembling_ctx = &assembling_ctx;
|
||||
aesdev->extra_img_flags = FP_IMG_PARTIAL;
|
||||
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aesX660_dev *aesdev;
|
||||
aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev->buffer);
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x2660 },
|
||||
{ .vendor = 0x08ff, .product = 0x2680 },
|
||||
{ .vendor = 0x08ff, .product = 0x2681 },
|
||||
{ .vendor = 0x08ff, .product = 0x2682 },
|
||||
{ .vendor = 0x08ff, .product = 0x2683 },
|
||||
{ .vendor = 0x08ff, .product = 0x2684 },
|
||||
{ .vendor = 0x08ff, .product = 0x2685 },
|
||||
{ .vendor = 0x08ff, .product = 0x2686 },
|
||||
{ .vendor = 0x08ff, .product = 0x2687 },
|
||||
{ .vendor = 0x08ff, .product = 0x2688 },
|
||||
{ .vendor = 0x08ff, .product = 0x2689 },
|
||||
{ .vendor = 0x08ff, .product = 0x268a },
|
||||
{ .vendor = 0x08ff, .product = 0x268b },
|
||||
{ .vendor = 0x08ff, .product = 0x268c },
|
||||
{ .vendor = 0x08ff, .product = 0x268d },
|
||||
{ .vendor = 0x08ff, .product = 0x268e },
|
||||
{ .vendor = 0x08ff, .product = 0x268f },
|
||||
{ .vendor = 0x08ff, .product = 0x2691 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes2660_driver = {
|
||||
.driver = {
|
||||
.id = AES2660_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES2660",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = -1,
|
||||
.img_width = FRAME_WIDTH + FRAME_WIDTH / 2,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = aesX660_dev_activate,
|
||||
.deactivate = aesX660_dev_deactivate,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* 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_dev_get_usb_dev(FP_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));
|
||||
fp_dev_set_instance_data(FP_DEV(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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_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,
|
||||
};
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* 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 = FP_INSTANCE_DATA(FP_DEV(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_img_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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
aesdev->img_trf = fpi_usb_alloc();
|
||||
data = g_malloc(aesdev->data_buflen);
|
||||
libusb_fill_bulk_transfer(aesdev->img_trf, fpi_dev_get_usb_dev(FP_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)
|
||||
{
|
||||
struct aes3k_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(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 = FP_INSTANCE_DATA(FP_DEV(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);
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
void aes3k_dev_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
#endif
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 "aes4000"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aes3k.h"
|
||||
|
||||
#define DATA_BUFLEN 0x1259
|
||||
|
||||
/* image size = FRAME_WIDTH x FRAME_WIDTH */
|
||||
#define FRAME_WIDTH 96
|
||||
#define FRAME_SIZE (FRAME_WIDTH * AES3K_FRAME_HEIGHT / 2)
|
||||
#define FRAME_NUMBER (FRAME_WIDTH / AES3K_FRAME_HEIGHT)
|
||||
#define ENLARGE_FACTOR 3
|
||||
|
||||
|
||||
static 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, 0x05 }, /* set end row to 5 */
|
||||
{ 0x95, 0x00 }, /* set start column to 0 */
|
||||
{ 0x96, 0x18 }, /* set end column to 24*4=96 */
|
||||
{ 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_dev_get_usb_dev(FP_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));
|
||||
fp_dev_set_instance_data(FP_DEV(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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(aesdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x08ff, .product = 0x5501 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver aes4000_driver = {
|
||||
.driver = {
|
||||
.id = AES4000_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "AuthenTec AES4000",
|
||||
.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,
|
||||
};
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
* Shared functions between libfprint Authentec drivers
|
||||
* 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 "aeslib"
|
||||
|
||||
#include "fp_internal.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "fpi-usb.h"
|
||||
#include "fpi-assembling.h"
|
||||
#include "aeslib.h"
|
||||
|
||||
#define MAX_REGWRITES_PER_REQUEST 16
|
||||
|
||||
#define BULK_TIMEOUT 4000
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
|
||||
struct write_regv_data {
|
||||
struct fp_img_dev *imgdev;
|
||||
unsigned int num_regs;
|
||||
const struct aes_regwrite *regs;
|
||||
unsigned int offset;
|
||||
aes_write_regv_cb callback;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
static void continue_write_regv(struct write_regv_data *wdata);
|
||||
|
||||
/* libusb bulk callback for regv write completion transfer. continues the
|
||||
* transaction */
|
||||
static void write_regv_trf_complete(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct write_regv_data *wdata = transfer->user_data;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
wdata->callback(wdata->imgdev, -EIO, wdata->user_data);
|
||||
g_free(wdata);
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
wdata->callback(wdata->imgdev, -EPROTO, wdata->user_data);
|
||||
g_free(wdata);
|
||||
} else {
|
||||
continue_write_regv(wdata);
|
||||
}
|
||||
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
/* write from wdata->offset to upper_bound (inclusive) of wdata->regs */
|
||||
static int do_write_regv(struct write_regv_data *wdata, int upper_bound)
|
||||
{
|
||||
unsigned int offset = wdata->offset;
|
||||
unsigned int num = upper_bound - offset + 1;
|
||||
size_t alloc_size = num * 2;
|
||||
unsigned char *data = g_malloc(alloc_size);
|
||||
unsigned int i;
|
||||
size_t data_offset = 0;
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
int r;
|
||||
|
||||
for (i = offset; i < offset + num; i++) {
|
||||
const struct aes_regwrite *regwrite = &wdata->regs[i];
|
||||
data[data_offset++] = regwrite->reg;
|
||||
data[data_offset++] = regwrite->value;
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, FP_DEV(wdata->imgdev)->udev, EP_OUT, data,
|
||||
alloc_size, write_regv_trf_complete, wdata, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* write the next batch of registers to be written, or if there are no more,
|
||||
* indicate completion to the caller */
|
||||
static void continue_write_regv(struct write_regv_data *wdata)
|
||||
{
|
||||
unsigned int offset = wdata->offset;
|
||||
unsigned int regs_remaining;
|
||||
unsigned int limit;
|
||||
unsigned int upper_bound;
|
||||
int i;
|
||||
int r;
|
||||
|
||||
/* skip all zeros and ensure there is still work to do */
|
||||
while (TRUE) {
|
||||
if (offset >= wdata->num_regs) {
|
||||
fp_dbg("all registers written");
|
||||
wdata->callback(wdata->imgdev, 0, wdata->user_data);
|
||||
g_free(wdata);
|
||||
return;
|
||||
}
|
||||
if (wdata->regs[offset].reg)
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
|
||||
wdata->offset = offset;
|
||||
regs_remaining = wdata->num_regs - offset;
|
||||
limit = MIN(regs_remaining, MAX_REGWRITES_PER_REQUEST);
|
||||
upper_bound = offset + limit - 1;
|
||||
|
||||
/* determine if we can write the entire of the regs at once, or if there
|
||||
* is a zero dividing things up */
|
||||
for (i = offset; i <= upper_bound; i++)
|
||||
if (!wdata->regs[i].reg) {
|
||||
upper_bound = i - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
r = do_write_regv(wdata, upper_bound);
|
||||
if (r < 0) {
|
||||
wdata->callback(wdata->imgdev, r, wdata->user_data);
|
||||
g_free(wdata);
|
||||
return;
|
||||
}
|
||||
|
||||
wdata->offset = upper_bound + 1;
|
||||
}
|
||||
|
||||
/* write a load of registers to the device, combining multiple writes in a
|
||||
* single URB up to a limit. insert writes to non-existent register 0 to force
|
||||
* specific groups of writes to be separated by different URBs. */
|
||||
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)
|
||||
{
|
||||
struct write_regv_data *wdata;
|
||||
|
||||
fp_dbg("write %d regs", num_regs);
|
||||
wdata = g_malloc(sizeof(*wdata));
|
||||
wdata->imgdev = dev;
|
||||
wdata->num_regs = num_regs;
|
||||
wdata->regs = regs;
|
||||
wdata->offset = 0;
|
||||
wdata->callback = callback;
|
||||
wdata->user_data = user_data;
|
||||
continue_write_regv(wdata);
|
||||
}
|
||||
|
||||
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned int x,
|
||||
unsigned int y)
|
||||
{
|
||||
unsigned char ret;
|
||||
|
||||
ret = frame->data[x * (ctx->frame_height >> 1) + (y >> 1)];
|
||||
ret = y % 2 ? ret >> 4 : ret & 0xf;
|
||||
ret *= 17;
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Shared functions between libfprint Authentec drivers
|
||||
* Copyright (C) 2007 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 __AESLIB_H__
|
||||
#define __AESLIB_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);
|
||||
|
||||
unsigned char aes_get_pixel(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned int x,
|
||||
unsigned int y);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,620 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES1660/AES2660 common routines
|
||||
* Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
|
||||
* Copyright (C) 2007 Cyrille Bagard
|
||||
* Copyright (C) 2007-2008,2012 Vasily Khoruzhick
|
||||
*
|
||||
* Based on AES2550 driver
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "aesX660"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "aeslib.h"
|
||||
#include "aesx660.h"
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void complete_deactivation(struct fp_img_dev *dev);
|
||||
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
#define BULK_TIMEOUT 4000
|
||||
#define FRAME_HEIGHT AESX660_FRAME_HEIGHT
|
||||
|
||||
#define ID_LEN 8
|
||||
#define INIT_LEN 4
|
||||
#define CALIBRATE_DATA_LEN 4
|
||||
#define FINGER_DET_DATA_LEN 4
|
||||
|
||||
static void
|
||||
aesX660_send_cmd_timeout(fpi_ssm *ssm,
|
||||
struct fp_dev *_dev,
|
||||
const unsigned char *cmd,
|
||||
size_t cmd_len,
|
||||
libusb_transfer_cb_fn callback,
|
||||
int timeout)
|
||||
{
|
||||
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
int r;
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_OUT,
|
||||
(unsigned char *)cmd, cmd_len,
|
||||
callback, ssm, timeout);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
fp_dbg("failed to submit transfer\n");
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
aesX660_send_cmd(fpi_ssm *ssm,
|
||||
struct fp_dev *dev,
|
||||
const unsigned char *cmd,
|
||||
size_t cmd_len,
|
||||
libusb_transfer_cb_fn callback)
|
||||
{
|
||||
return aesX660_send_cmd_timeout(ssm, dev, cmd, cmd_len, callback, BULK_TIMEOUT);
|
||||
}
|
||||
|
||||
static void
|
||||
aesX660_read_response(fpi_ssm *ssm,
|
||||
struct fp_dev *_dev,
|
||||
size_t buf_len,
|
||||
libusb_transfer_cb_fn callback)
|
||||
{
|
||||
struct fp_img_dev *dev = FP_IMG_DEV(_dev);
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
data = g_malloc(buf_len);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), EP_IN,
|
||||
data, buf_len,
|
||||
callback, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
fp_dbg("Failed to submit rx transfer: %d\n", r);
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void aesX660_send_cmd_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fp_dbg("tx transfer status: %d, actual_len: %.4x\n",
|
||||
transfer->status, transfer->actual_length);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void aesX660_read_calibrate_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
|
||||
(transfer->length != transfer->actual_length)) {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
/* Calibrate response was read correctly? */
|
||||
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_CALIBRATE_RESPONSE) {
|
||||
fp_dbg("Bogus calibrate response: %.2x\n", data[0]);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fpi_ssm_next_state(ssm);
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
/****** FINGER PRESENCE DETECTION ******/
|
||||
|
||||
enum finger_det_states {
|
||||
FINGER_DET_SEND_LED_CMD,
|
||||
FINGER_DET_SEND_FD_CMD,
|
||||
FINGER_DET_READ_FD_DATA,
|
||||
FINGER_DET_SET_IDLE,
|
||||
FINGER_DET_NUM_STATES,
|
||||
};
|
||||
|
||||
static void finger_det_read_fd_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
aesdev->fd_data_transfer = NULL;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
fp_dbg("Cancelling transfer...\n");
|
||||
fpi_ssm_next_state(ssm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
|
||||
(transfer->length != transfer->actual_length)) {
|
||||
fp_dbg("Failed to read FD data\n");
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (data[AESX660_RESPONSE_TYPE_OFFSET] != AESX660_FINGER_DET_RESPONSE) {
|
||||
fp_dbg("Bogus FD response: %.2x\n", data[0]);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (data[AESX660_FINGER_PRESENT_OFFSET] == AESX660_FINGER_PRESENT || aesdev->deactivating) {
|
||||
/* Finger present or we're deactivating... */
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fp_dbg("Wait for finger returned %.2x as result\n",
|
||||
data[AESX660_FINGER_PRESENT_OFFSET]);
|
||||
fpi_ssm_jump_to_state(ssm, FINGER_DET_SEND_FD_CMD);
|
||||
}
|
||||
out:
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void finger_det_set_idle_cmd_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void finger_det_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
int err = fpi_ssm_get_error(ssm);
|
||||
|
||||
fp_dbg("Finger detection completed");
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (aesdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
else if (err)
|
||||
fpi_imgdev_session_error(dev, err);
|
||||
else {
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
start_capture(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void finger_det_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case FINGER_DET_SEND_LED_CMD:
|
||||
aesX660_send_cmd(ssm, dev, led_blink_cmd, sizeof(led_blink_cmd),
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case FINGER_DET_SEND_FD_CMD:
|
||||
aesX660_send_cmd_timeout(ssm, dev, wait_for_finger_cmd, sizeof(wait_for_finger_cmd),
|
||||
aesX660_send_cmd_cb, 0);
|
||||
break;
|
||||
case FINGER_DET_READ_FD_DATA:
|
||||
aesX660_read_response(ssm, dev, FINGER_DET_DATA_LEN, finger_det_read_fd_data_cb);
|
||||
break;
|
||||
case FINGER_DET_SET_IDLE:
|
||||
aesX660_send_cmd(ssm, dev, set_idle_cmd, sizeof(set_idle_cmd),
|
||||
finger_det_set_idle_cmd_cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev)
|
||||
{
|
||||
fpi_ssm *ssm;
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), finger_det_run_state, FINGER_DET_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, finger_det_sm_complete);
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_SEND_LED_CMD,
|
||||
CAPTURE_SEND_CAPTURE_CMD,
|
||||
CAPTURE_READ_STRIPE_DATA,
|
||||
CAPTURE_SET_IDLE,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
/* Returns number of processed bytes */
|
||||
static int process_stripe_data(fpi_ssm *ssm, struct fp_img_dev *dev, unsigned char *data)
|
||||
{
|
||||
struct fpi_frame *stripe;
|
||||
unsigned char *stripdata;
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
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]);
|
||||
|
||||
stripe->delta_x = (int8_t)data[AESX660_FRAME_DELTA_X_OFFSET];
|
||||
stripe->delta_y = -(int8_t)data[AESX660_FRAME_DELTA_Y_OFFSET];
|
||||
fp_dbg("Offset to previous frame: %d %d", stripe->delta_x, stripe->delta_y);
|
||||
|
||||
if (data[AESX660_IMAGE_OK_OFFSET] == AESX660_IMAGE_OK) {
|
||||
memcpy(stripdata, data + AESX660_IMAGE_OFFSET, aesdev->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);
|
||||
}
|
||||
|
||||
g_free(stripe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void capture_set_idle_cmd_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
struct fp_img *img;
|
||||
|
||||
aesdev->strips = g_slist_reverse(aesdev->strips);
|
||||
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;
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_read_stripe_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
int finger_missing = 0;
|
||||
size_t copied, actual_len = transfer->actual_length;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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,
|
||||
data,
|
||||
copied);
|
||||
actual_len -= copied;
|
||||
data += copied;
|
||||
aesdev->buffer_size += copied;
|
||||
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 %.4lx",
|
||||
aesdev->buffer[AESX660_RESPONSE_TYPE_OFFSET],
|
||||
aesdev->buffer_max);
|
||||
continue;
|
||||
} else {
|
||||
finger_missing |= process_stripe_data(ssm, dev, aesdev->buffer);
|
||||
aesdev->buffer_max = AESX660_HEADER_SIZE;
|
||||
aesdev->buffer_size = 0;
|
||||
}
|
||||
}
|
||||
} while (actual_len);
|
||||
|
||||
fp_dbg("finger %s\n", finger_missing ? "missing" : "present");
|
||||
|
||||
if (finger_missing) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_jump_to_state(ssm, CAPTURE_READ_STRIPE_DATA);
|
||||
}
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CAPTURE_SEND_LED_CMD:
|
||||
aesX660_send_cmd(ssm, _dev, led_solid_cmd, sizeof(led_solid_cmd),
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case CAPTURE_SEND_CAPTURE_CMD:
|
||||
aesdev->buffer_size = 0;
|
||||
aesdev->buffer_max = AESX660_HEADER_SIZE;
|
||||
aesX660_send_cmd(ssm, _dev, aesdev->start_imaging_cmd,
|
||||
aesdev->start_imaging_cmd_len,
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case CAPTURE_READ_STRIPE_DATA:
|
||||
aesX660_read_response(ssm, _dev, AESX660_BULK_TRANSFER_SIZE,
|
||||
capture_read_stripe_data_cb);
|
||||
break;
|
||||
case CAPTURE_SET_IDLE:
|
||||
fp_dbg("Got %lu frames\n", aesdev->strips_len);
|
||||
aesX660_send_cmd(ssm, _dev, set_idle_cmd, sizeof(set_idle_cmd),
|
||||
capture_set_idle_cmd_cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(_dev);
|
||||
int err = fpi_ssm_get_error(ssm);
|
||||
|
||||
fp_dbg("Capture completed");
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (aesdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
else if (err)
|
||||
fpi_imgdev_session_error(dev, err);
|
||||
else
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
if (aesdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
enum activate_states {
|
||||
ACTIVATE_SET_IDLE,
|
||||
ACTIVATE_SEND_READ_ID_CMD,
|
||||
ACTIVATE_READ_ID,
|
||||
ACTIVATE_SEND_CALIBRATE_CMD,
|
||||
ACTIVATE_READ_CALIBRATE_DATA,
|
||||
ACTIVATE_SEND_INIT_CMD,
|
||||
ACTIVATE_READ_INIT_RESPONSE,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void activate_read_id_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
|
||||
(transfer->length != transfer->actual_length)) {
|
||||
fp_dbg("read_id cmd failed\n");
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
/* ID was read correctly */
|
||||
if (data[0] == 0x07) {
|
||||
fp_dbg("Sensor device id: %.2x%2x, bcdDevice: %.2x.%.2x, init status: %.2x\n",
|
||||
data[4], data[3], data[5], data[6], data[7]);
|
||||
} else {
|
||||
fp_dbg("Bogus read ID response: %.2x\n", data[AESX660_RESPONSE_TYPE_OFFSET]);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (aesdev->init_seq_idx) {
|
||||
case 0:
|
||||
aesdev->init_seq = aesdev->init_seqs[0];
|
||||
aesdev->init_seq_len = aesdev->init_seqs_len[0];
|
||||
aesdev->init_seq_idx = 1;
|
||||
aesdev->init_cmd_idx = 0;
|
||||
/* Do calibration only after 1st init sequence */
|
||||
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
|
||||
break;
|
||||
case 1:
|
||||
aesdev->init_seq = aesdev->init_seqs[1];
|
||||
aesdev->init_seq_len = aesdev->init_seqs_len[1];
|
||||
aesdev->init_seq_idx = 2;
|
||||
aesdev->init_cmd_idx = 0;
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
default:
|
||||
fp_dbg("Failed to init device! init status: %.2x\n", data[7]);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void activate_read_init_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
fp_dbg("read_init_cb\n");
|
||||
|
||||
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
|
||||
(transfer->length != transfer->actual_length)) {
|
||||
fp_dbg("read_init transfer status: %d, actual_len: %d\n", transfer->status, transfer->actual_length);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
/* ID was read correctly */
|
||||
if (data[0] != 0x42 || data[3] != 0x01) {
|
||||
fp_dbg("Bogus read init response: %.2x %.2x\n", data[0],
|
||||
data[3]);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
aesdev->init_cmd_idx++;
|
||||
if (aesdev->init_cmd_idx == aesdev->init_seq_len) {
|
||||
if (aesdev->init_seq_idx < 2)
|
||||
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_READ_ID_CMD);
|
||||
else
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fpi_ssm_jump_to_state(ssm, ACTIVATE_SEND_INIT_CMD);
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case ACTIVATE_SET_IDLE:
|
||||
aesdev->init_seq_idx = 0;
|
||||
fp_dbg("Activate: set idle\n");
|
||||
aesX660_send_cmd(ssm, _dev, set_idle_cmd, sizeof(set_idle_cmd),
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case ACTIVATE_SEND_READ_ID_CMD:
|
||||
fp_dbg("Activate: read ID\n");
|
||||
aesX660_send_cmd(ssm, _dev, read_id_cmd, sizeof(read_id_cmd),
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case ACTIVATE_READ_ID:
|
||||
aesX660_read_response(ssm, _dev, ID_LEN, activate_read_id_cb);
|
||||
break;
|
||||
case ACTIVATE_SEND_INIT_CMD:
|
||||
fp_dbg("Activate: send init seq #%d cmd #%d\n",
|
||||
aesdev->init_seq_idx,
|
||||
aesdev->init_cmd_idx);
|
||||
aesX660_send_cmd(ssm, _dev,
|
||||
aesdev->init_seq[aesdev->init_cmd_idx].cmd,
|
||||
aesdev->init_seq[aesdev->init_cmd_idx].len,
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case ACTIVATE_READ_INIT_RESPONSE:
|
||||
fp_dbg("Activate: read init response\n");
|
||||
aesX660_read_response(ssm, _dev, INIT_LEN, activate_read_init_cb);
|
||||
break;
|
||||
case ACTIVATE_SEND_CALIBRATE_CMD:
|
||||
aesX660_send_cmd(ssm, _dev, calibrate_cmd, sizeof(calibrate_cmd),
|
||||
aesX660_send_cmd_cb);
|
||||
break;
|
||||
case ACTIVATE_READ_CALIBRATE_DATA:
|
||||
aesX660_read_response(ssm, _dev, CALIBRATE_DATA_LEN, aesX660_read_calibrate_data_cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
int err = fpi_ssm_get_error(ssm);
|
||||
fp_dbg("status %d", err);
|
||||
fpi_imgdev_activate_complete(dev, err);
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (!err)
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
|
||||
int aesX660_dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aesX660_dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (aesdev->fd_data_transfer)
|
||||
libusb_cancel_transfer(aesdev->fd_data_transfer);
|
||||
|
||||
aesdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static void complete_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct aesX660_dev *aesdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
G_DEBUG_HERE();
|
||||
|
||||
aesdev->deactivating = FALSE;
|
||||
g_slist_free(aesdev->strips);
|
||||
aesdev->strips = NULL;
|
||||
aesdev->strips_len = 0;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* AuthenTec AES1660/AES2660 common definitions
|
||||
* Copyright (c) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __AESX660_H
|
||||
#define __AESX660_H
|
||||
|
||||
#define AESX660_HEADER_SIZE 3
|
||||
#define AESX660_RESPONSE_TYPE_OFFSET 0x00
|
||||
#define AESX660_RESPONSE_SIZE_LSB_OFFSET 0x01
|
||||
#define AESX660_RESPONSE_SIZE_MSB_OFFSET 0x02
|
||||
|
||||
#define AESX660_CALIBRATE_RESPONSE 0x06
|
||||
#define AESX660_FINGER_DET_RESPONSE 0x40
|
||||
#define AESX660_FINGER_PRESENT_OFFSET 0x03
|
||||
#define AESX660_FINGER_PRESENT 0x01
|
||||
|
||||
#define AESX660_IMAGE_OK_OFFSET 0x03
|
||||
#define AESX660_IMAGE_OK 0x0d
|
||||
#define AESX660_LAST_FRAME_OFFSET 0x04
|
||||
#define AESX660_LAST_FRAME_BIT 0x01
|
||||
|
||||
#define AESX660_FRAME_DELTA_X_OFFSET 16
|
||||
#define AESX660_FRAME_DELTA_Y_OFFSET 17
|
||||
|
||||
#define AESX660_IMAGE_OFFSET 43
|
||||
#define AESX660_BULK_TRANSFER_SIZE 4096
|
||||
|
||||
#define AESX660_FRAME_HEIGHT 8
|
||||
|
||||
struct aesX660_dev {
|
||||
GSList *strips;
|
||||
size_t strips_len;
|
||||
gboolean deactivating;
|
||||
struct aesX660_cmd *init_seq;
|
||||
size_t init_seq_len;
|
||||
unsigned int init_cmd_idx;
|
||||
unsigned int init_seq_idx;
|
||||
struct libusb_transfer *fd_data_transfer;
|
||||
unsigned char *buffer;
|
||||
size_t buffer_size;
|
||||
size_t buffer_max;
|
||||
|
||||
/* Device-specific stuff */
|
||||
struct aesX660_cmd *init_seqs[2];
|
||||
size_t init_seqs_len[2];
|
||||
unsigned char *start_imaging_cmd;
|
||||
size_t start_imaging_cmd_len;
|
||||
struct fpi_frame_asmbl_ctx *assembling_ctx;
|
||||
uint16_t extra_img_flags;
|
||||
};
|
||||
|
||||
struct aesX660_cmd {
|
||||
const unsigned char *cmd;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* 0x77 cmd seems to control LED, this sequence
|
||||
* makes LED blink
|
||||
*/
|
||||
static const unsigned char led_blink_cmd[] = {
|
||||
0x77, 0x18, 0x00,
|
||||
0x00, 0x3f, 0x00, 0xff, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x00, 0xf3, 0x01, 0x00,
|
||||
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf3,
|
||||
0x01, 0x00, 0x7f
|
||||
};
|
||||
|
||||
/* This sequence makes LED light solid
|
||||
*/
|
||||
static const unsigned char led_solid_cmd[] = {
|
||||
0x77, 0x18, 0x00, 0x00, 0x3f, 0x00, 0xff, 0x00,
|
||||
0x01, 0x01, 0x00, 0x00, 0x00, 0xe7, 0x03, 0x00,
|
||||
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x7f
|
||||
};
|
||||
|
||||
static const unsigned char wait_for_finger_cmd[] = {
|
||||
0x20,
|
||||
0x40, 0x04, 0x00, 0x02, 0x1e, 0x00, 0x32
|
||||
};
|
||||
|
||||
/* 0x40 cmd response
|
||||
*
|
||||
static const unsigned char pkt1371[] = {
|
||||
0x40, 0x01, 0x00, 0x01
|
||||
};
|
||||
*/
|
||||
|
||||
static const unsigned char set_idle_cmd[] = {
|
||||
0x0d, /* Reset or "set idle"? */
|
||||
};
|
||||
|
||||
static const unsigned char read_id_cmd[] = {
|
||||
0x44, 0x02, 0x00, 0x08, 0x00, /* Max transfer size is 8 */
|
||||
0x07, /* Read ID? */
|
||||
};
|
||||
|
||||
static const unsigned char calibrate_cmd[] = {
|
||||
0x44, 0x02, 0x00, 0x04, 0x00,
|
||||
0x06,
|
||||
};
|
||||
|
||||
int aesX660_dev_activate(struct fp_img_dev *dev);
|
||||
void aesX660_dev_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
#endif
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Driver IDs
|
||||
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __DRIVER_IDS
|
||||
#define __DRIVER_IDS
|
||||
|
||||
enum {
|
||||
UPEKTS_ID = 1,
|
||||
URU4000_ID = 2,
|
||||
AES4000_ID = 3,
|
||||
AES2501_ID = 4,
|
||||
UPEKTC_ID = 5,
|
||||
AES1610_ID = 6,
|
||||
/* FDU2000_ID = 7, */
|
||||
VCOM5S_ID = 8,
|
||||
UPEKSONLY_ID = 9,
|
||||
VFS101_ID = 10,
|
||||
VFS301_ID = 11,
|
||||
AES2550_ID = 12,
|
||||
/* UPEKE2_ID = 13 */
|
||||
AES1660_ID = 14,
|
||||
AES2660_ID = 15,
|
||||
AES3500_ID = 16,
|
||||
UPEKTC_IMG_ID = 17,
|
||||
ETES603_ID = 18,
|
||||
VFS5011_ID = 19,
|
||||
VFS0050_ID = 20,
|
||||
ELAN_ID = 21,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,992 +0,0 @@
|
|||
/*
|
||||
* Elan driver for libfprint
|
||||
*
|
||||
* Copyright (C) 2017 Igor Filatov <ia.filatov@gmail.com>
|
||||
* Copyright (C) 2018 Sébastien Béchet <sebastien.bechet@osinix.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
|
||||
*/
|
||||
|
||||
/*
|
||||
* The algorithm which libfprint uses to match fingerprints doesn't like small
|
||||
* images like the ones these drivers produce. There's just not enough minutiae
|
||||
* (recognizable print-specific points) on them for a reliable match. This means
|
||||
* that unless another matching algo is found/implemented, these readers will
|
||||
* not work as good with libfprint as they do with vendor drivers.
|
||||
*
|
||||
* To get bigger images the driver expects you to swipe the finger over the
|
||||
* reader. This works quite well for readers with a rectangular 144x64 sensor.
|
||||
* Worse than real swipe readers but good enough for day-to-day use. It needs
|
||||
* a steady and relatively slow swipe. There are also square 96x96 sensors and
|
||||
* I don't know whether they are in fact usable or not because I don't have one.
|
||||
* I imagine they'd be less reliable because the resulting image is even
|
||||
* smaller. If they can't be made usable with libfprint, I might end up dropping
|
||||
* them because it's better than saying they work when they don't.
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "elan"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "elan.h"
|
||||
|
||||
#define dbg_buf(buf, len) \
|
||||
if (len == 1) \
|
||||
fp_dbg("%02x", buf[0]); \
|
||||
else if (len == 2) \
|
||||
fp_dbg("%04x", buf[0] << 8 | buf[1]); \
|
||||
else if (len > 2) \
|
||||
fp_dbg("%04x... (%d bytes)", buf[0] << 8 | buf[1], len)
|
||||
|
||||
#ifndef ETIME
|
||||
#define ETIME ETIMEDOUT /* For kFreeBSD */
|
||||
#endif
|
||||
|
||||
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 {
|
||||
/* device config */
|
||||
unsigned short dev_type;
|
||||
unsigned short fw_ver;
|
||||
void (*process_frame) (unsigned short *raw_frame, GSList ** frames);
|
||||
/* end device config */
|
||||
|
||||
/* commands */
|
||||
const struct elan_cmd *cmd;
|
||||
int cmd_timeout;
|
||||
fpi_usb_transfer *cur_transfer;
|
||||
/* end commands */
|
||||
|
||||
/* state */
|
||||
enum fp_imgdev_state dev_state;
|
||||
enum fp_imgdev_state dev_state_next;
|
||||
unsigned char *last_read;
|
||||
unsigned char calib_atts_left;
|
||||
unsigned char calib_status;
|
||||
unsigned short *background;
|
||||
unsigned char frame_width;
|
||||
unsigned char frame_height;
|
||||
unsigned char raw_frame_height;
|
||||
int num_frames;
|
||||
GSList *frames;
|
||||
/* end state */
|
||||
};
|
||||
|
||||
int cmp_short(const void *a, const void *b)
|
||||
{
|
||||
return (int)(*(short *)a - *(short *)b);
|
||||
}
|
||||
|
||||
static void elan_dev_reset(struct elan_dev *elandev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
BUG_ON(elandev->cur_transfer);
|
||||
|
||||
elandev->cmd = NULL;
|
||||
elandev->cmd_timeout = ELAN_CMD_TIMEOUT;
|
||||
|
||||
elandev->calib_status = 0;
|
||||
|
||||
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 elan_dev *elandev, unsigned short *frame)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
/* so far 3 types of readers by sensor dimensions and orientation have been
|
||||
* seen in the wild:
|
||||
* 1. 144x64. Raw images are in portrait orientation while readers themselves
|
||||
* are placed (e.g. built into a touchpad) in landscape orientation. These
|
||||
* need to be rotated before assembling.
|
||||
* 2. 96x96 rotated. Like the first type but square. Likewise, need to be
|
||||
* rotated before assembling.
|
||||
* 3. 96x96 normal. Square and need NOT be rotated. So far there's only been
|
||||
* 1 report of a 0c03 of this type. Hopefully this type can be identified
|
||||
* by device id (and manufacturers don't just install the readers as they
|
||||
* please).
|
||||
* we also discard stripes of 'frame_margin' from bottom and top because
|
||||
* assembling works bad for tall frames */
|
||||
|
||||
unsigned char frame_width = elandev->frame_width;
|
||||
unsigned char frame_height = elandev->frame_height;
|
||||
unsigned char raw_height = elandev->raw_frame_height;
|
||||
unsigned char frame_margin = (raw_height - elandev->frame_height) / 2;
|
||||
int frame_idx, raw_idx;
|
||||
|
||||
for (int y = 0; y < frame_height; y++)
|
||||
for (int x = 0; x < frame_width; x++) {
|
||||
if (elandev->dev_type & ELAN_NOT_ROTATED)
|
||||
raw_idx = x + (y + frame_margin) * frame_width;
|
||||
else
|
||||
raw_idx = frame_margin + y + x * raw_height;
|
||||
frame_idx = x + y * frame_width;
|
||||
frame[frame_idx] =
|
||||
((unsigned short *)elandev->last_read)[raw_idx];
|
||||
}
|
||||
}
|
||||
|
||||
static void elan_save_background(struct elan_dev *elandev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
g_free(elandev->background);
|
||||
elandev->background =
|
||||
g_malloc(elandev->frame_width * elandev->frame_height *
|
||||
sizeof(short));
|
||||
elan_save_frame(elandev, elandev->background);
|
||||
}
|
||||
|
||||
/* save a frame as part of the fingerprint image
|
||||
* background needs to have been captured for this routine to work
|
||||
* Elantech recommends 2-step non-linear normalization in order to reduce
|
||||
* 2^14 ADC resolution to 2^8 image:
|
||||
*
|
||||
* 1. background is subtracted (done here)
|
||||
*
|
||||
* 2. pixels are grouped in 3 groups by intensity and each group is mapped
|
||||
* separately onto the normalized frame (done in elan_process_frame_*)
|
||||
* ==== 16383 ____> ======== 255
|
||||
* /
|
||||
* ----- lvl3 __/
|
||||
* 35% pixels
|
||||
*
|
||||
* ----- lvl2 --------> ======== 156
|
||||
*
|
||||
* 30% pixels
|
||||
* ----- lvl1 --------> ======== 99
|
||||
*
|
||||
* 35% pixels
|
||||
* ----- lvl0 __
|
||||
* \
|
||||
* ======== 0 \____> ======== 0
|
||||
*
|
||||
* For some devices we don't do 2. but instead do a simple linear mapping
|
||||
* because it seems to produce better results (or at least as good):
|
||||
* ==== 16383 ___> ======== 255
|
||||
* /
|
||||
* ------ max __/
|
||||
*
|
||||
*
|
||||
* ------ min __
|
||||
* \
|
||||
* ======== 0 \___> ======== 0
|
||||
*/
|
||||
static int elan_save_img_frame(struct elan_dev *elandev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
unsigned int frame_size = elandev->frame_width * elandev->frame_height;
|
||||
unsigned short *frame = g_malloc(frame_size * sizeof(short));
|
||||
elan_save_frame(elandev, frame);
|
||||
unsigned int sum = 0;
|
||||
|
||||
for (int i = 0; i < frame_size; i++) {
|
||||
if (elandev->background[i] > frame[i])
|
||||
frame[i] = 0;
|
||||
else
|
||||
frame[i] -= elandev->background[i];
|
||||
sum += frame[i];
|
||||
}
|
||||
|
||||
if (sum == 0) {
|
||||
fp_dbg
|
||||
("frame darker than background; finger present during calibration?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
elandev->frames = g_slist_prepend(elandev->frames, frame);
|
||||
elandev->num_frames += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void elan_process_frame_linear(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];
|
||||
}
|
||||
|
||||
g_assert(max != min);
|
||||
|
||||
unsigned short px;
|
||||
for (int i = 0; i < frame_size; i++) {
|
||||
px = raw_frame[i];
|
||||
px = (px - min) * 0xff / (max - min);
|
||||
frame->data[i] = (unsigned char)px;
|
||||
}
|
||||
|
||||
*frames = g_slist_prepend(*frames, frame);
|
||||
}
|
||||
|
||||
static void elan_process_frame_thirds(unsigned short *raw_frame,
|
||||
GSList ** frames)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
unsigned int frame_size =
|
||||
assembling_ctx.frame_width * assembling_ctx.frame_height;
|
||||
struct fpi_frame *frame =
|
||||
g_malloc(frame_size + sizeof(struct fpi_frame));
|
||||
|
||||
unsigned short lvl0, lvl1, lvl2, lvl3;
|
||||
unsigned short *sorted = g_malloc(frame_size * sizeof(short));
|
||||
memcpy(sorted, raw_frame, frame_size * sizeof(short));
|
||||
qsort(sorted, frame_size, sizeof(short), cmp_short);
|
||||
lvl0 = sorted[0];
|
||||
lvl1 = sorted[frame_size * 3 / 10];
|
||||
lvl2 = sorted[frame_size * 65 / 100];
|
||||
lvl3 = sorted[frame_size - 1];
|
||||
g_free(sorted);
|
||||
|
||||
unsigned short px;
|
||||
for (int i = 0; i < frame_size; i++) {
|
||||
px = raw_frame[i];
|
||||
if (lvl0 <= px && px < lvl1)
|
||||
px = (px - lvl0) * 99 / (lvl1 - lvl0);
|
||||
else if (lvl1 <= px && px < lvl2)
|
||||
px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1));
|
||||
else // (lvl2 <= px && px <= lvl3)
|
||||
px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2));
|
||||
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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
int num_frames;
|
||||
GSList *raw_frames;
|
||||
GSList *frames = NULL;
|
||||
struct fp_img *img;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
num_frames = elandev->num_frames - ELAN_SKIP_LAST_FRAMES;
|
||||
raw_frames = g_slist_nth(elandev->frames, ELAN_SKIP_LAST_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(raw_frames, (GFunc) elandev->process_frame, &frames);
|
||||
fpi_do_movement_estimation(&assembling_ctx, frames, num_frames);
|
||||
img = fpi_assemble_frames(&assembling_ctx, frames, num_frames);
|
||||
|
||||
img->flags |= FP_IMG_PARTIAL;
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
}
|
||||
|
||||
static void elan_cmd_done(fpi_ssm *ssm)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
|
||||
static void elan_cmd_cb(struct libusb_transfer *transfer,
|
||||
struct fp_dev *_dev,
|
||||
fpi_ssm *ssm,
|
||||
void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev;
|
||||
struct elan_dev *elandev;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
fp_dbg("transfer cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
dev = FP_IMG_DEV(_dev);
|
||||
elandev = FP_INSTANCE_DATA(_dev);
|
||||
elandev->cur_transfer = NULL;
|
||||
|
||||
switch (transfer->status) {
|
||||
case LIBUSB_TRANSFER_COMPLETED:
|
||||
if (transfer->length != transfer->actual_length) {
|
||||
fp_dbg("transfer length error: expected %d, got %d",
|
||||
transfer->length, transfer->actual_length);
|
||||
elan_dev_reset(elandev);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
} else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) {
|
||||
/* just finished receiving */
|
||||
elandev->last_read = g_memdup(transfer->buffer, transfer->actual_length);
|
||||
dbg_buf(transfer->buffer, transfer->actual_length);
|
||||
elan_cmd_done(ssm);
|
||||
} else {
|
||||
/* just finished sending */
|
||||
G_DEBUG_HERE();
|
||||
elan_cmd_read(ssm, dev);
|
||||
}
|
||||
break;
|
||||
case LIBUSB_TRANSFER_TIMED_OUT:
|
||||
fp_dbg("transfer timed out");
|
||||
fpi_ssm_mark_failed(ssm, -ETIMEDOUT);
|
||||
break;
|
||||
default:
|
||||
fp_dbg("transfer failed: %d", transfer->status);
|
||||
elan_dev_reset(elandev);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void elan_cmd_read(fpi_ssm *ssm, struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
int response_len = elandev->cmd->response_len;
|
||||
unsigned char *buffer;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (elandev->cmd->response_len == ELAN_CMD_SKIP_READ) {
|
||||
fp_dbg("skipping read, not expecting anything");
|
||||
elan_cmd_done(ssm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (elandev->dev_type == ELAN_0C42) {
|
||||
/* ELAN_0C42 sends an extra byte in one byte responses */
|
||||
if (elandev->cmd->response_len == 1)
|
||||
response_len = 2;
|
||||
}
|
||||
|
||||
if (elandev->cmd->cmd == get_image_cmd.cmd)
|
||||
/* raw data has 2-byte "pixels" and the frame is vertical */
|
||||
response_len =
|
||||
elandev->raw_frame_height * elandev->frame_width * 2;
|
||||
|
||||
g_clear_pointer(&elandev->last_read, g_free);
|
||||
buffer = g_malloc(response_len);
|
||||
|
||||
elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
|
||||
ssm,
|
||||
elandev->cmd->response_in,
|
||||
buffer,
|
||||
response_len,
|
||||
elan_cmd_cb,
|
||||
NULL,
|
||||
elandev->cmd_timeout);
|
||||
int r = fpi_usb_submit_transfer(elandev->cur_transfer);
|
||||
if (r < 0)
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
|
||||
static void
|
||||
elan_run_cmd(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
const struct elan_cmd *cmd,
|
||||
int cmd_timeout)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
dbg_buf(cmd->cmd, 2);
|
||||
|
||||
elandev->cmd = cmd;
|
||||
if (cmd_timeout != -1)
|
||||
elandev->cmd_timeout = cmd_timeout;
|
||||
|
||||
if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) {
|
||||
fp_dbg("skipping command 0x%x 0x%x for this device (for devices 0x%x but device is 0x%x)",
|
||||
cmd->cmd[0], cmd->cmd[1], cmd->devices, elandev->dev_type);
|
||||
elan_cmd_done(ssm);
|
||||
return;
|
||||
}
|
||||
|
||||
elandev->cur_transfer = fpi_usb_fill_bulk_transfer(FP_DEV(dev),
|
||||
ssm,
|
||||
ELAN_EP_CMD_OUT,
|
||||
g_memdup((char *) cmd->cmd, ELAN_CMD_LEN),
|
||||
ELAN_CMD_LEN,
|
||||
elan_cmd_cb,
|
||||
NULL,
|
||||
elandev->cmd_timeout);
|
||||
int r = fpi_usb_submit_transfer(elandev->cur_transfer);
|
||||
if (r < 0)
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
|
||||
enum stop_capture_states {
|
||||
STOP_CAPTURE,
|
||||
STOP_CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void stop_capture_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case STOP_CAPTURE:
|
||||
elan_run_cmd(ssm, FP_IMG_DEV(dev), &stop_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
|
||||
int error = fpi_ssm_get_error(ssm);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
|
||||
if (!error) {
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
|
||||
/* If verify or identify fails because of short swipe, we need to restart
|
||||
* capture manually. It feels like libfprint or the application should know
|
||||
* better if they want to retry, but they don't. Unless we've been asked to
|
||||
* deactivate, try to re-enter the capture loop. Since state change is
|
||||
* async, there's still a chance to be deactivated by another pending
|
||||
* event. */
|
||||
if (elandev->dev_state_next != IMGDEV_STATE_INACTIVE)
|
||||
dev_change_state(dev, IMGDEV_STATE_AWAIT_FINGER_ON);
|
||||
|
||||
} else if (error != -ECANCELED)
|
||||
fpi_imgdev_abort_scan(dev, error);
|
||||
}
|
||||
|
||||
static void elan_stop_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
elan_dev_reset(elandev);
|
||||
|
||||
fpi_ssm *ssm =
|
||||
fpi_ssm_new(FP_DEV(dev), stop_capture_run_state,
|
||||
STOP_CAPTURE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, stop_capture_complete);
|
||||
}
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_LED_ON,
|
||||
CAPTURE_WAIT_FINGER,
|
||||
CAPTURE_READ_DATA,
|
||||
CAPTURE_CHECK_ENOUGH_FRAMES,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CAPTURE_LED_ON:
|
||||
elan_run_cmd(ssm, dev, &led_on_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
case CAPTURE_WAIT_FINGER:
|
||||
elan_run_cmd(ssm, dev, &pre_scan_cmd, -1);
|
||||
break;
|
||||
case CAPTURE_READ_DATA:
|
||||
/* 0x55 - finger present
|
||||
* 0xff - device not calibrated (probably) */
|
||||
if (elandev->last_read && elandev->last_read[0] == 0x55) {
|
||||
if (elandev->dev_state == IMGDEV_STATE_AWAIT_FINGER_ON)
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
|
||||
} else
|
||||
fpi_ssm_mark_failed(ssm, -EBADMSG);
|
||||
break;
|
||||
case CAPTURE_CHECK_ENOUGH_FRAMES:
|
||||
r = elan_save_img_frame(elandev);
|
||||
if (r < 0)
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
else 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);
|
||||
} else {
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (fpi_ssm_get_error(ssm) == -ECANCELED) {
|
||||
fpi_ssm_free(ssm);
|
||||
return;
|
||||
}
|
||||
|
||||
/* either max frames captured or timed out waiting for the next frame */
|
||||
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);
|
||||
else {
|
||||
fp_dbg("swipe too short: want >= %d frames, got %d",
|
||||
ELAN_MIN_FRAMES, elandev->num_frames);
|
||||
fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT);
|
||||
}
|
||||
|
||||
/* other error
|
||||
* It says "...abort_scan" but reporting 1 during verification makes it
|
||||
* successful! */
|
||||
else
|
||||
fpi_imgdev_abort_scan(dev, fpi_ssm_get_error(ssm));
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static void elan_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
elan_dev_reset(elandev);
|
||||
fpi_ssm *ssm =
|
||||
fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, capture_complete);
|
||||
}
|
||||
|
||||
/* this function needs to have elandev->background and elandev->last_read to be
|
||||
* the calibration mean */
|
||||
static int elan_need_calibration(struct elan_dev *elandev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
unsigned short calib_mean =
|
||||
elandev->last_read[0] * 0xff + elandev->last_read[1];
|
||||
unsigned int bg_mean = 0, delta;
|
||||
unsigned int frame_size = elandev->frame_width * elandev->frame_height;
|
||||
|
||||
g_assert(frame_size != 0);
|
||||
|
||||
if (elandev->dev_type == ELAN_0C42) {
|
||||
if (calib_mean > 5500 ||
|
||||
calib_mean < 2500) {
|
||||
fp_dbg("Forcing needed recalibration");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < frame_size; i++)
|
||||
bg_mean += elandev->background[i];
|
||||
bg_mean /= frame_size;
|
||||
|
||||
delta =
|
||||
bg_mean > calib_mean ? bg_mean - calib_mean : calib_mean - bg_mean;
|
||||
|
||||
fp_dbg("calibration mean: %d, bg mean: %d, delta: %d", calib_mean,
|
||||
bg_mean, delta);
|
||||
|
||||
return delta > ELAN_CALIBRATION_MAX_DELTA ? 1 : 0;
|
||||
}
|
||||
|
||||
enum calibrate_states {
|
||||
CALIBRATE_GET_BACKGROUND,
|
||||
CALIBRATE_SAVE_BACKGROUND,
|
||||
CALIBRATE_GET_MEAN,
|
||||
CALIBRATE_CHECK_NEEDED,
|
||||
CALIBRATE_GET_STATUS,
|
||||
CALIBRATE_CHECK_STATUS,
|
||||
CALIBRATE_REPEAT_STATUS,
|
||||
CALIBRATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static gboolean elan_supports_calibration(struct elan_dev *elandev)
|
||||
{
|
||||
if (elandev->dev_type == ELAN_0C42)
|
||||
return TRUE;
|
||||
|
||||
return elandev->fw_ver >= ELAN_MIN_CALIBRATION_FW;
|
||||
}
|
||||
|
||||
static void calibrate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CALIBRATE_GET_BACKGROUND:
|
||||
elan_run_cmd(ssm, dev, &get_image_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
case CALIBRATE_SAVE_BACKGROUND:
|
||||
elan_save_background(elandev);
|
||||
if (!elan_supports_calibration(elandev)) {
|
||||
fp_dbg("FW does not support calibration");
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case CALIBRATE_GET_MEAN:
|
||||
elan_run_cmd(ssm, dev, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
case CALIBRATE_CHECK_NEEDED:
|
||||
if (elan_need_calibration(elandev)) {
|
||||
elandev->calib_status = 0;
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
break;
|
||||
case CALIBRATE_GET_STATUS:
|
||||
elandev->calib_atts_left -= 1;
|
||||
if (elandev->calib_atts_left)
|
||||
elan_run_cmd(ssm, dev, &get_calib_status_cmd,
|
||||
ELAN_CMD_TIMEOUT);
|
||||
else {
|
||||
fp_dbg("calibration failed");
|
||||
fpi_ssm_mark_failed(ssm, -1);
|
||||
}
|
||||
break;
|
||||
case CALIBRATE_CHECK_STATUS:
|
||||
/* 0x01 - retry, 0x03 - ok
|
||||
* It appears that when reading the response soon after 0x4023 the device
|
||||
* can return 0x03, and only after some time (up to 100 ms) the response
|
||||
* changes to 0x01. It stays that way for some time and then changes back
|
||||
* to 0x03. Because of this we don't just expect 0x03, we want to see 0x01
|
||||
* first. This is to make sure that a full calibration loop has completed */
|
||||
fp_dbg("calibration status: 0x%02x", elandev->last_read[0]);
|
||||
if (elandev->calib_status == 0x01
|
||||
&& elandev->last_read[0] == 0x03) {
|
||||
elandev->calib_status = 0x03;
|
||||
fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND);
|
||||
} else {
|
||||
fpi_timeout *timeout;
|
||||
|
||||
if (elandev->calib_status == 0x00
|
||||
&& elandev->last_read[0] == 0x01)
|
||||
elandev->calib_status = 0x01;
|
||||
timeout = fpi_timeout_add(50, fpi_ssm_next_state_timeout_cb, _dev, ssm);
|
||||
fpi_timeout_set_name(timeout, "calibrate_run_state");
|
||||
}
|
||||
break;
|
||||
case CALIBRATE_REPEAT_STATUS:
|
||||
fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_STATUS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void calibrate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
|
||||
if (fpi_ssm_get_error(ssm) != -ECANCELED)
|
||||
elan_capture(dev);
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static void elan_calibrate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
elan_dev_reset(elandev);
|
||||
elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS;
|
||||
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), calibrate_run_state,
|
||||
CALIBRATE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, calibrate_complete);
|
||||
}
|
||||
|
||||
enum activate_states {
|
||||
ACTIVATE_GET_FW_VER,
|
||||
ACTIVATE_SET_FW_VER,
|
||||
ACTIVATE_GET_SENSOR_DIM,
|
||||
ACTIVATE_SET_SENSOR_DIM,
|
||||
ACTIVATE_CMD_1,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case ACTIVATE_GET_FW_VER:
|
||||
elan_run_cmd(ssm, dev, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
case ACTIVATE_SET_FW_VER:
|
||||
elandev->fw_ver =
|
||||
(elandev->last_read[0] << 8 | elandev->last_read[1]);
|
||||
fp_dbg("FW ver 0x%04hx", elandev->fw_ver);
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case ACTIVATE_GET_SENSOR_DIM:
|
||||
elan_run_cmd(ssm, dev, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
case ACTIVATE_SET_SENSOR_DIM:
|
||||
/* see elan_save_frame for details */
|
||||
if (elandev->dev_type & ELAN_NOT_ROTATED) {
|
||||
elandev->frame_width = elandev->last_read[0];
|
||||
elandev->frame_height = elandev->raw_frame_height =
|
||||
elandev->last_read[2];
|
||||
} else {
|
||||
elandev->frame_width = elandev->last_read[2];
|
||||
elandev->frame_height = elandev->raw_frame_height =
|
||||
elandev->last_read[0];
|
||||
}
|
||||
/* Work-around sensors returning the sizes as zero-based index
|
||||
* rather than the number of pixels. */
|
||||
if ((elandev->frame_width % 2 == 1) &&
|
||||
(elandev->frame_height % 2 == 1)) {
|
||||
elandev->frame_width++;
|
||||
elandev->frame_height++;
|
||||
elandev->raw_frame_height = elandev->frame_height;
|
||||
}
|
||||
if (elandev->frame_height > ELAN_MAX_FRAME_HEIGHT)
|
||||
elandev->frame_height = ELAN_MAX_FRAME_HEIGHT;
|
||||
fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width,
|
||||
elandev->raw_frame_height);
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
case ACTIVATE_CMD_1:
|
||||
/* TODO: find out what this does, if we need it */
|
||||
elan_run_cmd(ssm, dev, &activate_cmd_1, ELAN_CMD_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (fpi_ssm_get_error(ssm) != -ECANCELED) {
|
||||
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
|
||||
}
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
static void elan_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
elan_dev_reset(elandev);
|
||||
|
||||
fpi_ssm *ssm =
|
||||
fpi_ssm_new(FP_DEV(dev), activate_run_state, ACTIVATE_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, activate_complete);
|
||||
}
|
||||
|
||||
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_dev_get_usb_dev(FP_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));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), elandev);
|
||||
|
||||
/* common params */
|
||||
elandev->dev_type = driver_data;
|
||||
elandev->background = NULL;
|
||||
elandev->process_frame = elan_process_frame_thirds;
|
||||
|
||||
switch (driver_data) {
|
||||
case ELAN_0907:
|
||||
elandev->process_frame = elan_process_frame_linear;
|
||||
break;
|
||||
}
|
||||
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void elan_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
elan_dev_reset(elandev);
|
||||
g_free(elandev->background);
|
||||
g_free(elandev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
elan_activate(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void elan_change_state(struct fp_img_dev *dev)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
enum fp_imgdev_state next_state = elandev->dev_state_next;
|
||||
|
||||
if (elandev->dev_state == next_state) {
|
||||
fp_dbg("already in %d", next_state);
|
||||
return;
|
||||
} else
|
||||
fp_dbg("changing to %d", next_state);
|
||||
|
||||
switch (next_state) {
|
||||
case IMGDEV_STATE_INACTIVE:
|
||||
if (elandev->cur_transfer)
|
||||
/* deactivation will complete in transfer callback */
|
||||
fpi_usb_cancel_transfer(elandev->cur_transfer);
|
||||
else
|
||||
elan_deactivate(dev);
|
||||
break;
|
||||
case IMGDEV_STATE_AWAIT_FINGER_ON:
|
||||
/* activation completed or another enroll stage started */
|
||||
elan_calibrate(dev);
|
||||
break;
|
||||
case IMGDEV_STATE_CAPTURE:
|
||||
/* not used */
|
||||
break;
|
||||
case IMGDEV_STATE_AWAIT_FINGER_OFF:
|
||||
elan_stop_capture(dev);
|
||||
}
|
||||
|
||||
elandev->dev_state = next_state;
|
||||
}
|
||||
|
||||
static void
|
||||
elan_change_state_async(struct fp_dev *dev,
|
||||
void *data)
|
||||
{
|
||||
g_message ("state change dev: %p", dev);
|
||||
elan_change_state(FP_IMG_DEV (dev));
|
||||
}
|
||||
|
||||
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state)
|
||||
{
|
||||
struct elan_dev *elandev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_timeout *timeout;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
switch (state) {
|
||||
case IMGDEV_STATE_INACTIVE:
|
||||
case IMGDEV_STATE_AWAIT_FINGER_ON:
|
||||
case IMGDEV_STATE_AWAIT_FINGER_OFF: {
|
||||
char *name;
|
||||
|
||||
/* schedule state change instead of calling it directly to allow all actions
|
||||
* related to the previous state to complete */
|
||||
elandev->dev_state_next = state;
|
||||
timeout = fpi_timeout_add(10, elan_change_state_async, FP_DEV(dev), NULL);
|
||||
|
||||
name = g_strdup_printf ("dev_change_state to %d", state);
|
||||
fpi_timeout_set_name(timeout, name);
|
||||
g_free (name);
|
||||
|
||||
break;
|
||||
}
|
||||
case IMGDEV_STATE_CAPTURE:
|
||||
/* TODO MAYBE: split capture ssm into smaller ssms and use this state */
|
||||
elandev->dev_state = state;
|
||||
elandev->dev_state_next = state;
|
||||
break;
|
||||
default:
|
||||
fp_err("unrecognized state %d", state);
|
||||
fpi_imgdev_session_error(dev, -EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* as of time of writing libfprint never checks the return value */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
|
||||
dev_change_state(dev, IMGDEV_STATE_INACTIVE);
|
||||
}
|
||||
|
||||
struct fp_img_driver elan_driver = {
|
||||
.driver = {
|
||||
.id = ELAN_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "ElanTech Fingerprint Sensor",
|
||||
.id_table = elan_id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
.flags = 0,
|
||||
|
||||
.bz3_threshold = 24,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
.change_state = dev_change_state,
|
||||
};
|
|
@ -1,224 +0,0 @@
|
|||
/*
|
||||
* 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 <libusb.h>
|
||||
|
||||
#define ELAN_VEND_ID 0x04f3
|
||||
|
||||
/* a default device type */
|
||||
#define ELAN_ALL_DEV 0
|
||||
|
||||
/* devices with quirks */
|
||||
#define ELAN_0907 (1 << 0)
|
||||
#define ELAN_0C03 (1 << 1)
|
||||
#define ELAN_0C42 (1 << 2)
|
||||
|
||||
/* devices which don't require frame rotation before assembling */
|
||||
#define ELAN_NOT_ROTATED ELAN_0C03
|
||||
|
||||
/* min FW version that supports calibration */
|
||||
#define ELAN_MIN_CALIBRATION_FW 0x0138
|
||||
|
||||
/* max difference between background image mean and calibration mean
|
||||
* (the response value of get_calib_mean_cmd)*/
|
||||
#define ELAN_CALIBRATION_MAX_DELTA 500
|
||||
|
||||
/* times to retry reading calibration status during one session
|
||||
* generally prevents calibration from looping indefinitely */
|
||||
#define ELAN_CALIBRATION_ATTEMPTS 10
|
||||
|
||||
/* min and max frames in a capture */
|
||||
#define ELAN_MIN_FRAMES 7
|
||||
#define ELAN_MAX_FRAMES 30
|
||||
|
||||
/* crop frames to this height to improve stitching */
|
||||
#define ELAN_MAX_FRAME_HEIGHT 50
|
||||
|
||||
/* 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 2
|
||||
|
||||
#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)
|
||||
|
||||
/* used as response length to tell the driver to skip reading response */
|
||||
#define ELAN_CMD_SKIP_READ 0
|
||||
|
||||
/* 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;
|
||||
unsigned short devices;
|
||||
};
|
||||
|
||||
static const struct elan_cmd get_sensor_dim_cmd = {
|
||||
.cmd = {0x00, 0x0c},
|
||||
.response_len = 0x4,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct elan_cmd get_fw_ver_cmd = {
|
||||
.cmd = {0x40, 0x19},
|
||||
.response_len = 0x2,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
/* unknown, returns 0x0 0x1 on 0907 */
|
||||
static const struct elan_cmd activate_cmd_1 = {
|
||||
.cmd = {0x40, 0x2a},
|
||||
.response_len = 0x2,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_0907,
|
||||
};
|
||||
|
||||
static const struct elan_cmd get_image_cmd = {
|
||||
.cmd = {0x00, 0x09},
|
||||
/* raw frame sizes are calculated from image dimensions reported by the
|
||||
* device */
|
||||
.response_len = -1,
|
||||
.response_in = ELAN_EP_IMG_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct elan_cmd read_sensor_status_cmd = {
|
||||
.cmd = {0x40, 0x13},
|
||||
.response_len = 0x1,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct elan_cmd get_calib_status_cmd = {
|
||||
.cmd = {0x40, 0x23},
|
||||
.response_len = 0x1,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct elan_cmd get_calib_mean_cmd = {
|
||||
.cmd = {0x40, 0x24},
|
||||
.response_len = 0x2,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct elan_cmd led_on_cmd = {
|
||||
.cmd = {0x40, 0x31},
|
||||
.response_len = ELAN_CMD_SKIP_READ,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
/* wait for finger
|
||||
* subsequent read will not complete until finger is placed on the reader */
|
||||
static const struct elan_cmd pre_scan_cmd = {
|
||||
.cmd = {0x40, 0x3f},
|
||||
.response_len = 0x1,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
/* led off, stop waiting for finger */
|
||||
static const struct elan_cmd stop_cmd = {
|
||||
.cmd = {0x00, 0x0b},
|
||||
.response_len = ELAN_CMD_SKIP_READ,
|
||||
.response_in = ELAN_EP_CMD_IN,
|
||||
.devices = ELAN_ALL_DEV,
|
||||
};
|
||||
|
||||
static const struct usb_id elan_id_table[] = {
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0903,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0907,.driver_data = ELAN_0907},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c01,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c02,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_0C03},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c04,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c05,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c06,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c07,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c08,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c09,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0a,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0b,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0c,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0d,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0e,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c0f,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c10,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c11,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c12,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c13,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c14,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c15,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c16,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c17,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c18,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c19,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1a,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1b,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1c,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1d,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1e,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c1f,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c20,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c21,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c22,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c23,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c24,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c25,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c26,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c27,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c28,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c29,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2a,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2b,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2c,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2d,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2e,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c2f,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c30,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c31,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c32,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c33,.driver_data = ELAN_ALL_DEV},
|
||||
{.vendor = ELAN_VEND_ID,.product = 0x0c42,.driver_data = ELAN_0C42},
|
||||
{0, 0, 0,},
|
||||
};
|
||||
|
||||
static void elan_cmd_done(fpi_ssm *ssm);
|
||||
static void elan_cmd_read(fpi_ssm *ssm, struct fp_img_dev *dev);
|
||||
|
||||
static void elan_calibrate(struct fp_img_dev *dev);
|
||||
static void elan_capture(struct fp_img_dev *dev);
|
||||
static void elan_deactivate(struct fp_img_dev *dev);
|
||||
|
||||
static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* LGPL CRC code copied from GStreamer-0.10.10:
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot 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; version
|
||||
* 2.1 of the License.
|
||||
*
|
||||
* 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 "upek_proto.h"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* LGPL CRC code copied from GStreamer-0.10.10:
|
||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||
* Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot 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; version
|
||||
* 2.1 of the License.
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
uint16_t udf_crc(unsigned char *buffer, size_t size);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,319 +0,0 @@
|
|||
/*
|
||||
* 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 },
|
||||
};
|
|
@ -1,478 +0,0 @@
|
|||
/*
|
||||
* UPEK TouchChip driver for libfprint
|
||||
* Copyright (C) 2007 Jan-Michael Brummer <buzz2@gmx.de>
|
||||
* Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* 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"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "upektc.h"
|
||||
|
||||
#define UPEKTC_EP_IN (2 | LIBUSB_ENDPOINT_IN)
|
||||
#define UPEKTC_EP_OUT (3 | LIBUSB_ENDPOINT_OUT)
|
||||
#define UPEKET_EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
#define UPEKET_EP_OUT (2 | LIBUSB_ENDPOINT_OUT)
|
||||
#define BULK_TIMEOUT 4000
|
||||
|
||||
struct upektc_dev {
|
||||
gboolean deactivating;
|
||||
const struct setup_cmd *setup_commands;
|
||||
size_t setup_commands_len;
|
||||
int ep_in;
|
||||
int ep_out;
|
||||
int init_idx;
|
||||
int sum_threshold;
|
||||
};
|
||||
|
||||
enum upektc_driver_data {
|
||||
UPEKTC_2015,
|
||||
UPEKTC_3001,
|
||||
};
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev);
|
||||
static void complete_deactivation(struct fp_img_dev *dev);
|
||||
static void start_finger_detection(struct fp_img_dev *dev);
|
||||
|
||||
/****** INITIALIZATION/DEINITIALIZATION ******/
|
||||
|
||||
enum activate_states {
|
||||
WRITE_INIT,
|
||||
READ_DATA,
|
||||
ACTIVATE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void
|
||||
upektc_next_init_cmd(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
upekdev->init_idx += 1;
|
||||
if (upekdev->init_idx == upekdev->setup_commands_len)
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
else
|
||||
fpi_ssm_jump_to_state(ssm, WRITE_INIT);
|
||||
}
|
||||
|
||||
static void write_init_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) &&
|
||||
(transfer->length == transfer->actual_length)) {
|
||||
if (upekdev->setup_commands[upekdev->init_idx].response_len)
|
||||
fpi_ssm_next_state(ssm);
|
||||
else
|
||||
upektc_next_init_cmd(ssm, dev);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void read_init_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
|
||||
upektc_next_init_cmd(ssm, dev);
|
||||
else
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case WRITE_INIT:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case READ_DATA:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(upekdev->setup_commands[upekdev->init_idx].response_len);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data,
|
||||
upekdev->setup_commands[upekdev->init_idx].response_len,
|
||||
read_init_data_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
fp_dbg("status %d", fpi_ssm_get_error(ssm));
|
||||
fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm));
|
||||
|
||||
if (!fpi_ssm_get_error(ssm))
|
||||
start_finger_detection(dev);
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
|
||||
/****** FINGER PRESENCE DETECTION ******/
|
||||
|
||||
static int finger_present(unsigned char *img, size_t len, int sum_threshold)
|
||||
{
|
||||
int i, sum;
|
||||
|
||||
sum = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (img[i] < 160) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
fp_dbg("finger_present: sum is %d\n", sum);
|
||||
return sum < sum_threshold ? 0 : 1;
|
||||
}
|
||||
|
||||
static void finger_det_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct fp_img_dev *dev = transfer->user_data;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
unsigned char *data = transfer->buffer;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("data transfer status %d\n", transfer->status);
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto out;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fp_dbg("expected %d, got %d bytes", transfer->length,
|
||||
transfer->actual_length);
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
}
|
||||
|
||||
if (finger_present(data, IMAGE_SIZE, upekdev->sum_threshold)) {
|
||||
/* finger present, start capturing */
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
start_capture(dev);
|
||||
} else {
|
||||
/* no finger, poll for a new histogram */
|
||||
start_finger_detection(dev);
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void finger_det_cmd_cb(struct libusb_transfer *t)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
unsigned char *data;
|
||||
int r;
|
||||
struct fp_img_dev *dev = t->user_data;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (t->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("req transfer status %d\n", t->status);
|
||||
fpi_imgdev_session_error(dev, -EIO);
|
||||
goto exit_free_transfer;
|
||||
} else if (t->length != t->actual_length) {
|
||||
fp_dbg("expected %d, sent %d bytes", t->length, t->actual_length);
|
||||
fpi_imgdev_session_error(dev, -EPROTO);
|
||||
goto exit_free_transfer;
|
||||
}
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
data = g_malloc(IMAGE_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
|
||||
finger_det_data_cb, dev, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
exit_free_transfer:
|
||||
libusb_free_transfer(t);
|
||||
}
|
||||
|
||||
static void start_finger_detection(struct fp_img_dev *dev)
|
||||
{
|
||||
int r;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
struct libusb_transfer *transfer;
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (upekdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
|
||||
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
|
||||
finger_det_cmd_cb, dev, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
}
|
||||
}
|
||||
|
||||
/****** CAPTURE ******/
|
||||
|
||||
enum capture_states {
|
||||
CAPTURE_WRITE_CMD,
|
||||
CAPTURE_READ_DATA,
|
||||
CAPTURE_NUM_STATES,
|
||||
};
|
||||
|
||||
static void capture_cmd_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
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_failed(ssm, -EIO);
|
||||
}
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
unsigned char *data = transfer->buffer;
|
||||
struct fp_img *img;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fp_dbg("request is not completed, %d", transfer->status);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
} else if (transfer->length != transfer->actual_length) {
|
||||
fp_dbg("expected %d, sent %d bytes", transfer->length, transfer->actual_length);
|
||||
fpi_ssm_mark_failed(ssm, -EPROTO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
img = fpi_img_new(IMAGE_SIZE);
|
||||
memcpy(img->data, data, IMAGE_SIZE);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
out:
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CAPTURE_WRITE_CMD:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_out,
|
||||
(unsigned char *)scan_cmd, UPEKTC_CMD_LEN,
|
||||
capture_cmd_cb, ssm, BULK_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, -ENOMEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAPTURE_READ_DATA:
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
|
||||
data = g_malloc(IMAGE_SIZE);
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_DEV(dev)), upekdev->ep_in, data, IMAGE_SIZE,
|
||||
capture_read_data_cb, ssm, BULK_TIMEOUT);
|
||||
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
fp_dbg("Capture completed");
|
||||
if (upekdev->deactivating)
|
||||
complete_deactivation(dev);
|
||||
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);
|
||||
}
|
||||
|
||||
static void start_capture(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
if (upekdev->deactivating) {
|
||||
complete_deactivation(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, dev);
|
||||
G_DEBUG_HERE();
|
||||
fpi_ssm_start(ssm, capture_sm_complete);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, dev);
|
||||
upekdev->init_idx = 0;
|
||||
fpi_ssm_start(ssm, activate_sm_complete);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
upekdev->deactivating = TRUE;
|
||||
}
|
||||
|
||||
static void complete_deactivation(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
G_DEBUG_HERE();
|
||||
|
||||
upekdev->deactivating = FALSE;
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
/* TODO check that device has endpoints we're using */
|
||||
int r;
|
||||
struct upektc_dev *upekdev;
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_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_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(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 = 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 = G_N_ELEMENTS(upeket_setup_commands);
|
||||
upekdev->sum_threshold = UPEKET_SUM_THRESHOLD;
|
||||
break;
|
||||
default:
|
||||
fp_err("Device variant %lu is not known\n", driver_data);
|
||||
g_free(upekdev);
|
||||
fp_dev_set_instance_data(FP_DEV(dev), NULL);
|
||||
return -ENODEV;
|
||||
break;
|
||||
}
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
void *user_data;
|
||||
user_data = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(user_data);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x0483, .product = 0x2015, .driver_data = UPEKTC_2015 },
|
||||
{ .vendor = 0x147e, .product = 0x3001, .driver_data = UPEKTC_3001 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver upektc_driver = {
|
||||
.driver = {
|
||||
.id = UPEKTC_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "UPEK TouchChip/Eikon Touch 300",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = IMAGE_HEIGHT,
|
||||
.img_width = IMAGE_WIDTH,
|
||||
|
||||
.bz3_threshold = 30,
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,647 +0,0 @@
|
|||
/*
|
||||
* 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 "upek_proto.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 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(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
const unsigned char *buf,
|
||||
size_t buf_size,
|
||||
unsigned char seq,
|
||||
libusb_transfer_cb_fn cb)
|
||||
{
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
int r;
|
||||
|
||||
BUG_ON(buf_size > MAX_CMD_SIZE);
|
||||
|
||||
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_dev_get_usb_dev(FP_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_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
upektc_img_read_data(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
size_t buf_size,
|
||||
size_t buf_offset,
|
||||
libusb_transfer_cb_fn cb)
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
int r;
|
||||
|
||||
BUG_ON(buf_size > MAX_RESPONSE_SIZE);
|
||||
|
||||
transfer->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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_failed(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)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if ((transfer->status != LIBUSB_TRANSFER_COMPLETED) ||
|
||||
(transfer->length != transfer->actual_length)) {
|
||||
fpi_ssm_mark_failed(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)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(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_failed(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("Unknown response!\n");
|
||||
fpi_ssm_mark_failed(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_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void capture_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case CAPTURE_INIT_CAPTURE:
|
||||
upektc_img_submit_req(ssm, dev, 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, dev, SHORT_RESPONSE_SIZE, 0, capture_read_data_cb);
|
||||
else
|
||||
upektc_img_read_data(ssm, dev, 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, dev, 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, dev, upek2020_ack_08, sizeof(upek2020_ack_08),
|
||||
0, capture_reqs_cb);
|
||||
break;
|
||||
case CAPTURE_ACK_FRAME:
|
||||
upektc_img_submit_req(ssm, dev, upek2020_ack_frame, sizeof(upek2020_ack_frame),
|
||||
upekdev->seq, capture_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void capture_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
upekdev->image_size = 0;
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), capture_run_state, CAPTURE_NUM_STATES, 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)
|
||||
{
|
||||
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_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: process response properly */
|
||||
static void deactivate_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void deactivate_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case DEACTIVATE_DEINIT:
|
||||
upektc_img_submit_req(ssm, dev, upek2020_deinit, sizeof(upek2020_deinit),
|
||||
upekdev->seq, deactivate_reqs_cb);
|
||||
upekdev->seq++;
|
||||
break;
|
||||
case DEACTIVATE_READ_DEINIT_DATA:
|
||||
upektc_img_read_data(ssm, dev, SHORT_RESPONSE_SIZE, 0, deactivate_read_data_cb);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void deactivate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_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 = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm;
|
||||
|
||||
upekdev->image_size = 0;
|
||||
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), deactivate_run_state, DEACTIVATE_NUM_STATES, 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)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_reqs_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
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_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: process response properly */
|
||||
static void init_read_data_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_run_state(fpi_ssm *ssm, struct fp_dev *dev, void *user_data)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
struct fp_img_dev *idev = user_data;
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(dev);
|
||||
int r;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case ACTIVATE_CONTROL_REQ_1:
|
||||
case ACTIVATE_CONTROL_REQ_2:
|
||||
{
|
||||
unsigned char *data;
|
||||
|
||||
transfer = fpi_usb_alloc();
|
||||
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_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACTIVATE_INIT_1:
|
||||
upektc_img_submit_req(ssm, idev, upek2020_init_1, sizeof(upek2020_init_1),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_2:
|
||||
upektc_img_submit_req(ssm, idev, upek2020_init_2, sizeof(upek2020_init_2),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_3:
|
||||
upektc_img_submit_req(ssm, idev, upek2020_init_3, sizeof(upek2020_init_3),
|
||||
0, init_reqs_cb);
|
||||
break;
|
||||
case ACTIVATE_INIT_4:
|
||||
upektc_img_submit_req(ssm, idev, 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, idev, SHORT_RESPONSE_SIZE, 0, init_read_data_cb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_sm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
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)
|
||||
{
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), activate_run_state,
|
||||
ACTIVATE_NUM_STATES, 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 = FP_INSTANCE_DATA(FP_DEV(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_dev_get_usb_dev(FP_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));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), upekdev);
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct upektc_img_dev *upekdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(upekdev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_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,
|
||||
};
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* 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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,375 +0,0 @@
|
|||
/*
|
||||
* Veridicom 5thSense driver for libfprint
|
||||
* Copyright (C) 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 "vcom5s"
|
||||
|
||||
#include "drivers_api.h"
|
||||
|
||||
/* TODO:
|
||||
* calibration?
|
||||
* image size: windows gets 300x300 through vpas enrollment util?
|
||||
* (probably just increase bulk read size?)
|
||||
* powerdown? does windows do anything special on exit?
|
||||
*/
|
||||
|
||||
#define CTRL_IN 0xc0
|
||||
#define CTRL_OUT 0x40
|
||||
#define CTRL_TIMEOUT 1000
|
||||
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
|
||||
|
||||
#define IMG_WIDTH 300
|
||||
#define IMG_HEIGHT 288
|
||||
#define ROWS_PER_RQ 12
|
||||
#define NR_REQS (IMG_HEIGHT / ROWS_PER_RQ)
|
||||
#define RQ_SIZE (IMG_WIDTH * ROWS_PER_RQ)
|
||||
#define IMG_SIZE (IMG_WIDTH * IMG_HEIGHT)
|
||||
|
||||
struct v5s_dev {
|
||||
int capture_iteration;
|
||||
struct fp_img *capture_img;
|
||||
gboolean loop_running;
|
||||
gboolean deactivating;
|
||||
};
|
||||
|
||||
enum v5s_regs {
|
||||
/* when using gain 0x29:
|
||||
* a value of 0x00 produces mostly-black image
|
||||
* 0x09 destroys ridges (too white)
|
||||
* 0x01 or 0x02 seem good values */
|
||||
REG_CONTRAST = 0x02,
|
||||
|
||||
/* when using contrast 0x01:
|
||||
* a value of 0x00 will produce an all-black image.
|
||||
* 0x29 produces a good contrast image: ridges quite dark, but some
|
||||
* light grey noise as background
|
||||
* 0x46 produces all-white image with grey ridges (not very dark) */
|
||||
REG_GAIN = 0x03,
|
||||
};
|
||||
|
||||
enum v5s_cmd {
|
||||
/* scan one row. has parameter, at a guess this is which row to scan? */
|
||||
CMD_SCAN_ONE_ROW = 0xc0,
|
||||
|
||||
/* scan whole image */
|
||||
CMD_SCAN = 0xc1,
|
||||
};
|
||||
|
||||
/***** REGISTER I/O *****/
|
||||
|
||||
static void sm_write_reg_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void
|
||||
sm_write_reg(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
unsigned char reg,
|
||||
unsigned char value)
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
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, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_write_reg_cb,
|
||||
ssm, CTRL_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
static void sm_exec_cmd_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
|
||||
g_free(transfer->buffer);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void
|
||||
sm_exec_cmd(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
unsigned char cmd,
|
||||
unsigned char param)
|
||||
{
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
unsigned char *data;
|
||||
int r;
|
||||
|
||||
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, fpi_dev_get_usb_dev(FP_DEV(dev)), data, sm_exec_cmd_cb,
|
||||
ssm, CTRL_TIMEOUT);
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
g_free(data);
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
/***** FINGER DETECTION *****/
|
||||
|
||||
/* We take 64x64 pixels at the center of the image, determine the average
|
||||
* pixel intensity, and threshold it. */
|
||||
#define DETBOX_ROW_START 111
|
||||
#define DETBOX_COL_START 117
|
||||
#define DETBOX_ROWS 64
|
||||
#define DETBOX_COLS 64
|
||||
#define DETBOX_ROW_END (DETBOX_ROW_START + DETBOX_ROWS)
|
||||
#define DETBOX_COL_END (DETBOX_COL_START + DETBOX_COLS)
|
||||
#define FINGER_PRESENCE_THRESHOLD 100
|
||||
|
||||
static gboolean finger_is_present(unsigned char *data)
|
||||
{
|
||||
int row;
|
||||
uint16_t imgavg = 0;
|
||||
|
||||
for (row = DETBOX_ROW_START; row < DETBOX_ROW_END; row++) {
|
||||
unsigned char *rowdata = data + (row * IMG_WIDTH);
|
||||
uint16_t rowavg = 0;
|
||||
int col;
|
||||
|
||||
for (col = DETBOX_COL_START; col < DETBOX_COL_END; col++)
|
||||
rowavg += rowdata[col];
|
||||
rowavg /= DETBOX_COLS;
|
||||
imgavg += rowavg;
|
||||
}
|
||||
imgavg /= DETBOX_ROWS;
|
||||
fp_dbg("img avg %d", imgavg);
|
||||
|
||||
return (imgavg <= FINGER_PRESENCE_THRESHOLD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** IMAGE ACQUISITION *****/
|
||||
|
||||
static void capture_iterate(fpi_ssm *ssm, struct fp_img_dev *dev);
|
||||
|
||||
static void capture_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (++vdev->capture_iteration == NR_REQS) {
|
||||
struct fp_img *img = vdev->capture_img;
|
||||
/* must clear this early, otherwise the call chain takes us into
|
||||
* loopsm_complete where we would free it, when in fact we are
|
||||
* supposed to be handing off this image */
|
||||
vdev->capture_img = NULL;
|
||||
|
||||
fpi_imgdev_report_finger_status(dev, finger_is_present(img->data));
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
fpi_ssm_next_state(ssm);
|
||||
} else {
|
||||
capture_iterate(ssm, dev);
|
||||
}
|
||||
|
||||
out:
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void
|
||||
capture_iterate(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
int iteration = vdev->capture_iteration;
|
||||
struct libusb_transfer *transfer = fpi_usb_alloc();
|
||||
int r;
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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;
|
||||
r = libusb_submit_transfer(transfer);
|
||||
if (r < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
fpi_ssm_mark_failed(ssm, r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sm_do_capture(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
G_DEBUG_HERE();
|
||||
vdev->capture_img = fpi_img_new_for_imgdev(dev);
|
||||
vdev->capture_iteration = 0;
|
||||
capture_iterate(ssm, dev);
|
||||
}
|
||||
|
||||
/***** CAPTURE LOOP *****/
|
||||
|
||||
enum loop_states {
|
||||
LOOP_SET_CONTRAST,
|
||||
LOOP_SET_GAIN,
|
||||
LOOP_CMD_SCAN,
|
||||
LOOP_CAPTURE,
|
||||
LOOP_CAPTURE_DONE,
|
||||
LOOP_NUM_STATES,
|
||||
};
|
||||
|
||||
static void loop_run_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case LOOP_SET_CONTRAST:
|
||||
sm_write_reg(ssm, dev, REG_CONTRAST, 0x01);
|
||||
break;
|
||||
case LOOP_SET_GAIN:
|
||||
sm_write_reg(ssm, dev, REG_GAIN, 0x29);
|
||||
break;
|
||||
case LOOP_CMD_SCAN:
|
||||
if (vdev->deactivating) {
|
||||
fp_dbg("deactivating, marking completed");
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
} else
|
||||
sm_exec_cmd(ssm, dev, CMD_SCAN, 0x00);
|
||||
break;
|
||||
case LOOP_CAPTURE:
|
||||
sm_do_capture(ssm, dev);
|
||||
break;
|
||||
case LOOP_CAPTURE_DONE:
|
||||
fpi_ssm_jump_to_state(ssm, LOOP_CMD_SCAN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void loopsm_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(_dev);
|
||||
int r = fpi_ssm_get_error(ssm);
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
fp_img_free(vdev->capture_img);
|
||||
vdev->capture_img = NULL;
|
||||
vdev->loop_running = FALSE;
|
||||
|
||||
if (r)
|
||||
fpi_imgdev_session_error(dev, r);
|
||||
|
||||
if (vdev->deactivating)
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(dev), loop_run_state,
|
||||
LOOP_NUM_STATES, dev);
|
||||
vdev->deactivating = FALSE;
|
||||
fpi_ssm_start(ssm, loopsm_complete);
|
||||
vdev->loop_running = TRUE;
|
||||
fpi_imgdev_activate_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
struct v5s_dev *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
if (vdev->loop_running)
|
||||
vdev->deactivating = TRUE;
|
||||
else
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
int r;
|
||||
struct v5s_dev *v5s_dev;
|
||||
|
||||
v5s_dev = g_malloc0(sizeof(struct v5s_dev));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), v5s_dev);
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
if (r < 0)
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
|
||||
if (r == 0)
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void dev_deinit(struct fp_img_dev *dev)
|
||||
{
|
||||
struct v5s_dev *v5s_dev;
|
||||
v5s_dev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
g_free(v5s_dev);
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
static const struct usb_id id_table[] = {
|
||||
{ .vendor = 0x061a, .product = 0x0110 },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver vcom5s_driver = {
|
||||
.driver = {
|
||||
.id = VCOM5S_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "Veridicom 5thSense",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_PRESS,
|
||||
},
|
||||
.flags = 0,
|
||||
.img_height = IMG_HEIGHT,
|
||||
.img_width = IMG_WIDTH,
|
||||
|
||||
.open = dev_init,
|
||||
.close = dev_deinit,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
||||
|
|
@ -1,791 +0,0 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
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_failed(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_failed(ssm, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
|
||||
/* Send data to EP1, the only out endpoint */
|
||||
static void
|
||||
async_write(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
void *data,
|
||||
int len)
|
||||
{
|
||||
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(dev));
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
|
||||
vdev->transfer = fpi_usb_alloc();
|
||||
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)
|
||||
{
|
||||
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_failed(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_failed(ssm, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
|
||||
/* Receive data from the given ep and compare with expected */
|
||||
static void
|
||||
async_read(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev,
|
||||
int ep,
|
||||
void *data,
|
||||
int len)
|
||||
{
|
||||
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
|
||||
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||
|
||||
ep |= LIBUSB_ENDPOINT_IN;
|
||||
|
||||
vdev->transfer = fpi_usb_alloc();
|
||||
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)
|
||||
{
|
||||
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_failed(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(fpi_ssm *ssm, int ep)
|
||||
{
|
||||
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
|
||||
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||
|
||||
int len = VFS_USB_BUFFER_SIZE;
|
||||
unsigned char *data = g_malloc(VFS_USB_BUFFER_SIZE);
|
||||
|
||||
ep |= LIBUSB_ENDPOINT_IN;
|
||||
|
||||
vdev->transfer = fpi_usb_alloc();
|
||||
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 = FP_INSTANCE_DATA(FP_DEV(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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *idev = user_data;
|
||||
|
||||
short result;
|
||||
char command04 = 0x04;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case SUBSM1_COMMAND_04:
|
||||
async_write(ssm, idev, &command04, sizeof(command04));
|
||||
break;
|
||||
|
||||
case SUBSM1_RETURN_CODE:
|
||||
async_read(ssm, idev, 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_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send command to clear EP2 */
|
||||
static void
|
||||
clear_ep2(fpi_ssm *ssm,
|
||||
struct fp_img_dev *idev)
|
||||
{
|
||||
fpi_ssm *subsm =
|
||||
fpi_ssm_new(FP_DEV(idev), clear_ep2_ssm, SUBSM1_STATES, idev);
|
||||
fpi_ssm_start_subsm(ssm, subsm);
|
||||
}
|
||||
|
||||
static void send_control_packet_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *idev = user_data;
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
short result;
|
||||
unsigned char *commit_result = NULL;
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case SUBSM2_SEND_CONTROL:
|
||||
async_write(ssm, idev, vdev->control_packet, VFS_CONTROL_PACKET_SIZE);
|
||||
break;
|
||||
|
||||
case SUBSM2_RETURN_CODE:
|
||||
async_read(ssm, idev, 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, idev, commit_out, sizeof(commit_out));
|
||||
break;
|
||||
|
||||
case SUBSM2_COMMIT_RESPONSE:
|
||||
commit_result = g_malloc(VFS_COMMIT_RESPONSE_SIZE);
|
||||
async_read(ssm, idev, 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, idev, 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_failed(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, idev);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
|
||||
default:
|
||||
fp_err("Unknown SUBSM2 state");
|
||||
fpi_imgdev_session_error(idev, -EIO);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send device state control packet */
|
||||
static void
|
||||
send_control_packet(fpi_ssm *ssm,
|
||||
struct fp_img_dev *idev)
|
||||
{
|
||||
fpi_ssm *subsm =
|
||||
fpi_ssm_new(FP_DEV(idev), send_control_packet_ssm, SUBSM2_STATES, 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)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(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_failed(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_failed(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_failed(ssm, -EIO);
|
||||
}
|
||||
|
||||
static void receive_callback(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = transfer->user_data;
|
||||
struct fp_img_dev *idev = fpi_ssm_get_user_data(ssm);
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(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_failed(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(struct fp_dev *dev,
|
||||
void *data)
|
||||
{
|
||||
fpi_ssm *ssm = data;
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(dev);
|
||||
|
||||
/* 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(struct fp_dev *dev,
|
||||
void *data)
|
||||
{
|
||||
fpi_ssm *ssm = data;
|
||||
fpi_ssm_jump_to_state(ssm, SSM_TURN_ON);
|
||||
}
|
||||
|
||||
/* Main SSM loop */
|
||||
static void activate_ssm(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *idev = user_data;
|
||||
struct libusb_device_handle *usb_dev = fpi_dev_get_usb_dev(FP_DEV(idev));
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
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, idev);
|
||||
break;
|
||||
|
||||
case SSM_TURN_OFF:
|
||||
/* Set control_packet argument */
|
||||
vdev->control_packet = turn_off;
|
||||
|
||||
send_control_packet(ssm, idev);
|
||||
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, idev);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Asynchronously enquire an interrupt */
|
||||
vdev->transfer = fpi_usb_alloc();
|
||||
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, _dev, 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 = fpi_usb_alloc();
|
||||
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, fpi_ssm_next_state_timeout_cb, _dev, 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, idev);
|
||||
break;
|
||||
|
||||
case SSM_WAIT_ANOTHER_SCAN:
|
||||
/* Orange light is on now */
|
||||
fpi_timeout_add(VFS_SSM_ORANGE_TIMEOUT, another_scan, _dev, ssm);
|
||||
break;
|
||||
|
||||
default:
|
||||
fp_err("Unknown state");
|
||||
fpi_imgdev_session_error(idev, -EIO);
|
||||
fpi_ssm_mark_failed(ssm, -EIO);
|
||||
}
|
||||
}
|
||||
|
||||
/* Driver functions */
|
||||
|
||||
/* Callback for dev_activate ssm */
|
||||
static void dev_activate_callback(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
vdev->ssm_active = 0;
|
||||
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
/* Activate device */
|
||||
static int dev_activate(struct fp_img_dev *idev)
|
||||
{
|
||||
struct vfs_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||
|
||||
/* Initialize flags */
|
||||
vdev->active = 1;
|
||||
vdev->need_report = 1;
|
||||
vdev->ssm_active = 1;
|
||||
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(idev), activate_ssm, SSM_STATES, 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 = FP_INSTANCE_DATA(FP_DEV(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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
/* Notify open complete */
|
||||
fpi_imgdev_open_complete(user_data, 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_dev_get_usb_dev(FP_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));
|
||||
fp_dev_set_instance_data(FP_DEV(idev), vdev);
|
||||
|
||||
/* Clearing previous device state */
|
||||
fpi_ssm *ssm = fpi_ssm_new(FP_DEV(idev), activate_ssm, SSM_STATES, 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 = FP_INSTANCE_DATA(FP_DEV(idev));
|
||||
g_free(vdev);
|
||||
|
||||
/* Release usb interface */
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_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,
|
||||
};
|
|
@ -1,382 +0,0 @@
|
|||
/*
|
||||
* 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,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,293 +0,0 @@
|
|||
/*
|
||||
* vfs301/vfs300 fingerprint reader driver
|
||||
* https://github.com/andree182/vfs301
|
||||
*
|
||||
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define FP_COMPONENT "vfs301"
|
||||
|
||||
#include "drivers_api.h"
|
||||
#include "vfs301_proto.h"
|
||||
|
||||
#ifndef ETIME
|
||||
#define ETIME ETIMEDOUT /* For kFreeBSD */
|
||||
#endif
|
||||
|
||||
/************************** GENERIC STUFF *************************************/
|
||||
|
||||
/* Submit asynchronous sleep */
|
||||
static void
|
||||
async_sleep(unsigned int msec,
|
||||
fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
/* Add timeout */
|
||||
if (fpi_timeout_add(msec, fpi_ssm_next_state_timeout_cb, FP_DEV(dev), ssm) == NULL) {
|
||||
/* Failed to add timeout */
|
||||
fp_err("failed to add timeout");
|
||||
fpi_imgdev_session_error(dev, -ETIME);
|
||||
fpi_ssm_mark_failed(ssm, -ETIME);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
submit_image(fpi_ssm *ssm,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
vfs301_dev_t *vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
int height;
|
||||
struct fp_img *img;
|
||||
|
||||
#if 0
|
||||
/* XXX: This is probably handled by libfprint automagically? */
|
||||
if (vdev->scanline_count < 20) {
|
||||
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
img = fpi_img_new(VFS301_FP_OUTPUT_WIDTH * vdev->scanline_count);
|
||||
if (img == NULL)
|
||||
return 0;
|
||||
|
||||
vfs301_extract_image(vdev, img->data, &height);
|
||||
|
||||
/* TODO: how to detect flip? should the resulting image be
|
||||
* oriented so that it is equal e.g. to a fingerprint on a paper,
|
||||
* or to the finger when I look at it?) */
|
||||
img->flags = FP_IMG_COLORS_INVERTED | FP_IMG_V_FLIPPED;
|
||||
|
||||
img->width = VFS301_FP_OUTPUT_WIDTH;
|
||||
img->height = height;
|
||||
|
||||
img = fpi_img_realloc(img, img->height * img->width);
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Loop ssm states */
|
||||
enum
|
||||
{
|
||||
/* Step 0 - Scan finger */
|
||||
M_REQUEST_PRINT,
|
||||
M_WAIT_PRINT,
|
||||
M_CHECK_PRINT,
|
||||
M_READ_PRINT_START,
|
||||
M_READ_PRINT_WAIT,
|
||||
M_READ_PRINT_POLL,
|
||||
M_SUBMIT_PRINT,
|
||||
|
||||
/* Number of states */
|
||||
M_LOOP_NUM_STATES,
|
||||
};
|
||||
|
||||
/* Exec loop sequential state machine */
|
||||
static void m_loop_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
switch (fpi_ssm_get_cur_state(ssm)) {
|
||||
case M_REQUEST_PRINT:
|
||||
vfs301_proto_request_fingerprint(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
|
||||
case M_WAIT_PRINT:
|
||||
/* Wait fingerprint scanning */
|
||||
async_sleep(200, ssm, dev);
|
||||
break;
|
||||
|
||||
case M_CHECK_PRINT:
|
||||
if (!vfs301_proto_peek_event(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev))
|
||||
fpi_ssm_jump_to_state(ssm, M_WAIT_PRINT);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
|
||||
case M_READ_PRINT_START:
|
||||
fpi_imgdev_report_finger_status(dev, TRUE);
|
||||
vfs301_proto_process_event_start(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
|
||||
fpi_ssm_next_state(ssm);
|
||||
break;
|
||||
|
||||
case M_READ_PRINT_WAIT:
|
||||
/* Wait fingerprint scanning */
|
||||
async_sleep(200, ssm, dev);
|
||||
break;
|
||||
|
||||
case M_READ_PRINT_POLL:
|
||||
{
|
||||
int rv = vfs301_proto_process_event_poll(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
|
||||
g_assert(rv != VFS301_FAILURE);
|
||||
if (rv == VFS301_ONGOING)
|
||||
fpi_ssm_jump_to_state(ssm, M_READ_PRINT_WAIT);
|
||||
else
|
||||
fpi_ssm_next_state(ssm);
|
||||
}
|
||||
break;
|
||||
|
||||
case M_SUBMIT_PRINT:
|
||||
if (submit_image(ssm, dev)) {
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
/* NOTE: finger off is expected only after submitting image... */
|
||||
fpi_imgdev_report_finger_status(dev, FALSE);
|
||||
} else {
|
||||
fpi_ssm_jump_to_state(ssm, M_REQUEST_PRINT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Complete loop sequential state machine */
|
||||
static void m_loop_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
/* Free sequential state machine */
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
/* Exec init sequential state machine */
|
||||
static void m_init_state(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
vfs301_dev_t *vdev = FP_INSTANCE_DATA(_dev);
|
||||
|
||||
g_assert(fpi_ssm_get_cur_state(ssm) == 0);
|
||||
|
||||
vfs301_proto_init(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
|
||||
|
||||
fpi_ssm_mark_completed(ssm);
|
||||
}
|
||||
|
||||
/* Complete init sequential state machine */
|
||||
static void m_init_complete(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
fpi_ssm *ssm_loop;
|
||||
|
||||
if (!fpi_ssm_get_error(ssm)) {
|
||||
/* Notify activate complete */
|
||||
fpi_imgdev_activate_complete(dev, 0);
|
||||
|
||||
/* Start loop ssm */
|
||||
ssm_loop = fpi_ssm_new(FP_DEV(dev), m_loop_state, M_LOOP_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm_loop, m_loop_complete);
|
||||
}
|
||||
|
||||
/* Free sequential state machine */
|
||||
fpi_ssm_free(ssm);
|
||||
}
|
||||
|
||||
/* Activate device */
|
||||
static int dev_activate(struct fp_img_dev *dev)
|
||||
{
|
||||
fpi_ssm *ssm;
|
||||
|
||||
/* Start init ssm */
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), m_init_state, 1, dev);
|
||||
fpi_ssm_start(ssm, m_init_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deactivate device */
|
||||
static void dev_deactivate(struct fp_img_dev *dev)
|
||||
{
|
||||
vfs301_dev_t *vdev;
|
||||
|
||||
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
vfs301_proto_deinit(fpi_dev_get_usb_dev(FP_DEV(dev)), vdev);
|
||||
fpi_imgdev_deactivate_complete(dev);
|
||||
}
|
||||
|
||||
static int dev_open(struct fp_img_dev *dev, unsigned long driver_data)
|
||||
{
|
||||
vfs301_dev_t *vdev = NULL;
|
||||
int r;
|
||||
|
||||
/* Claim usb interface */
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
if (r < 0) {
|
||||
/* Interface not claimed, return error */
|
||||
fp_err("could not claim interface 0: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Initialize private structure */
|
||||
vdev = g_malloc0(sizeof(vfs301_dev_t));
|
||||
fp_dev_set_instance_data(FP_DEV(dev), vdev);
|
||||
|
||||
vdev->scanline_buf = malloc(0);
|
||||
vdev->scanline_count = 0;
|
||||
|
||||
/* Notify open complete */
|
||||
fpi_imgdev_open_complete(dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_close(struct fp_img_dev *dev)
|
||||
{
|
||||
vfs301_dev_t *vdev;
|
||||
|
||||
/* Release private structure */
|
||||
vdev = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
free(vdev->scanline_buf);
|
||||
g_free(vdev);
|
||||
|
||||
/* Release usb interface */
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
|
||||
/* Notify close complete */
|
||||
fpi_imgdev_close_complete(dev);
|
||||
}
|
||||
|
||||
/* Usb id table of device */
|
||||
static const struct usb_id id_table[] =
|
||||
{
|
||||
{ .vendor = 0x138a, .product = 0x0005 /* vfs301 */ },
|
||||
{ .vendor = 0x138a, .product = 0x0008 /* vfs300 */ },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
/* Device driver definition */
|
||||
struct fp_img_driver vfs301_driver =
|
||||
{
|
||||
/* Driver specification */
|
||||
.driver =
|
||||
{
|
||||
.id = VFS301_ID,
|
||||
.name = FP_COMPONENT,
|
||||
.full_name = "Validity VFS301",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
|
||||
/* Image specification */
|
||||
.flags = 0,
|
||||
.img_width = VFS301_FP_WIDTH,
|
||||
.img_height = -1,
|
||||
.bz3_threshold = 24,
|
||||
|
||||
/* Routine specification */
|
||||
.open = dev_open,
|
||||
.close = dev_close,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
|
@ -1,631 +0,0 @@
|
|||
/*
|
||||
* vfs301/vfs300 fingerprint reader driver
|
||||
* https://github.com/andree182/vfs301
|
||||
*
|
||||
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - async communication everywhere :)
|
||||
* - protocol decyphering
|
||||
* - what is needed and what is redundant
|
||||
* - is some part of the initial data the firmware?
|
||||
* - describe some interesting structures better
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "fpi-usb.h"
|
||||
#include "vfs301_proto.h"
|
||||
#include "vfs301_proto_fragments.h"
|
||||
|
||||
/************************** USB STUFF *****************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
static void usb_print_packet(int dir, int rv, const unsigned char *data, int length)
|
||||
{
|
||||
fprintf(stderr, "%s, rv %d, len %d\n", dir ? "send" : "recv", rv, length);
|
||||
|
||||
#ifdef PRINT_VERBOSE
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIN(length, 128); i++) {
|
||||
fprintf(stderr, "%.2X ", data[i]);
|
||||
if (i % 8 == 7)
|
||||
fprintf(stderr, " ");
|
||||
if (i % 32 == 31)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int usb_recv(
|
||||
vfs301_dev_t *dev,
|
||||
struct libusb_device_handle *devh, unsigned char endpoint, int max_bytes)
|
||||
{
|
||||
g_assert(max_bytes <= sizeof(dev->recv_buf));
|
||||
|
||||
int r = libusb_bulk_transfer(
|
||||
devh, endpoint,
|
||||
dev->recv_buf, max_bytes,
|
||||
&dev->recv_len, VFS301_DEFAULT_WAIT_TIMEOUT
|
||||
);
|
||||
|
||||
#ifdef DEBUG
|
||||
usb_print_packet(0, r, dev->recv_buf, dev->recv_len);
|
||||
#endif
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_send(
|
||||
struct libusb_device_handle *devh, const unsigned char *data, int length)
|
||||
{
|
||||
int transferred = 0;
|
||||
|
||||
int r = libusb_bulk_transfer(
|
||||
devh, VFS301_SEND_ENDPOINT,
|
||||
(unsigned char *)data, length, &transferred, VFS301_DEFAULT_WAIT_TIMEOUT
|
||||
);
|
||||
|
||||
#ifdef DEBUG
|
||||
usb_print_packet(1, r, data, length);
|
||||
#endif
|
||||
|
||||
g_assert(r == 0);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (transferred < length)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************** OUT MESSAGES GENERATION ***************************/
|
||||
|
||||
static void vfs301_proto_generate_0B(int subtype, unsigned char *data, int *len)
|
||||
{
|
||||
*data = 0x0B;
|
||||
*len = 1;
|
||||
data++;
|
||||
|
||||
memset(data, 0, 39);
|
||||
*len += 38;
|
||||
|
||||
data[20] = subtype;
|
||||
|
||||
switch (subtype) {
|
||||
case 0x04:
|
||||
data[34] = 0x9F;
|
||||
break;
|
||||
case 0x05:
|
||||
data[34] = 0xAB;
|
||||
len++;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define HEX_TO_INT(c) \
|
||||
(((c) >= '0' && (c) <= '9') ? ((c) - '0') : ((c) - 'A' + 10))
|
||||
|
||||
static void translate_str(const char **srcL, unsigned char *data, int *len)
|
||||
{
|
||||
const char *src;
|
||||
unsigned char *dataOrig = data;
|
||||
|
||||
while (*srcL != NULL) {
|
||||
src = *srcL;
|
||||
while (*src != '\0') {
|
||||
g_assert(*src != '\0');
|
||||
g_assert(*(src +1) != '\0');
|
||||
*data = (unsigned char)((HEX_TO_INT(*src) << 4) | (HEX_TO_INT(*(src + 1))));
|
||||
|
||||
data++;
|
||||
src += 2;
|
||||
}
|
||||
|
||||
srcL++;
|
||||
}
|
||||
|
||||
*len = data - dataOrig;
|
||||
}
|
||||
|
||||
static void vfs301_proto_generate(int type, int subtype, unsigned char *data, int *len)
|
||||
{
|
||||
switch (type) {
|
||||
case 0x01:
|
||||
case 0x04:
|
||||
/* After cmd 0x04 is sent, a data is received on VALIDITY_RECEIVE_ENDPOINT_CTRL.
|
||||
* If it is 0x0000:
|
||||
* additional 64B and 224B are read from _DATA, then vfs301_next_scan_FA00 is
|
||||
* sent, 0000 received from _CTRL, and then continue with wait loop
|
||||
* If it is 0x1204:
|
||||
* => reinit?
|
||||
*/
|
||||
case 0x17:
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
*data = type;
|
||||
*len = 1;
|
||||
break;
|
||||
case 0x0B:
|
||||
vfs301_proto_generate_0B(subtype, data, len);
|
||||
break;
|
||||
case 0x02D0:
|
||||
{
|
||||
const char **dataLs[] = {
|
||||
vfs301_02D0_01,
|
||||
vfs301_02D0_02,
|
||||
vfs301_02D0_03,
|
||||
vfs301_02D0_04,
|
||||
vfs301_02D0_05,
|
||||
vfs301_02D0_06,
|
||||
vfs301_02D0_07,
|
||||
};
|
||||
g_assert((int)subtype <= (int)(sizeof(dataLs) / sizeof(dataLs[0])));
|
||||
translate_str(dataLs[subtype - 1], data, len);
|
||||
}
|
||||
break;
|
||||
case 0x0220:
|
||||
switch (subtype) {
|
||||
case 1:
|
||||
translate_str(vfs301_0220_01, data, len);
|
||||
break;
|
||||
case 2:
|
||||
translate_str(vfs301_0220_02, data, len);
|
||||
break;
|
||||
case 3:
|
||||
translate_str(vfs301_0220_03, data, len);
|
||||
break;
|
||||
case 0xFA00:
|
||||
case 0x2C01:
|
||||
case 0x5E01:
|
||||
translate_str(vfs301_next_scan_template, data, len);
|
||||
unsigned char *field = data + *len - (sizeof(S4_TAIL) - 1) / 2 - 4;
|
||||
|
||||
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);
|
||||
*(field + 2) = *field;
|
||||
*(field + 3) = *(field + 1);
|
||||
break;
|
||||
default:
|
||||
g_assert(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************************** SCAN IMAGE PROCESSING *****************************/
|
||||
|
||||
#ifdef SCAN_FINISH_DETECTION
|
||||
static int img_is_finished_scan(fp_line_t *lines, int no_lines)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int rv = 1;
|
||||
|
||||
for (i = no_lines - VFS301_FP_SUM_LINES; i < no_lines; i++) {
|
||||
/* check the line for fingerprint data */
|
||||
for (j = 0; j < sizeof(lines[i].sum2); j++) {
|
||||
if (lines[i].sum2[j] > (VFS301_FP_SUM_MEDIAN + VFS301_FP_SUM_EMPTY_RANGE))
|
||||
rv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int scanline_diff(const unsigned char *scanlines, int prev, int cur)
|
||||
{
|
||||
const unsigned char *line1 = scanlines + prev * VFS301_FP_OUTPUT_WIDTH;
|
||||
const unsigned char *line2 = scanlines + cur * VFS301_FP_OUTPUT_WIDTH;
|
||||
int i;
|
||||
int diff;
|
||||
|
||||
#ifdef OUTPUT_RAW
|
||||
/* We only need the image, not the surrounding stuff. */
|
||||
line1 = ((vfs301_line_t*)line1)->scan;
|
||||
line2 = ((vfs301_line_t*)line2)->scan;
|
||||
#endif
|
||||
|
||||
/* TODO: This doesn't work too well when there are parallel lines in the
|
||||
* fingerprint. */
|
||||
for (diff = 0, i = 0; i < VFS301_FP_WIDTH; i++) {
|
||||
if (*line1 > *line2)
|
||||
diff += *line1 - *line2;
|
||||
else
|
||||
diff += *line2 - *line1;
|
||||
|
||||
line1++;
|
||||
line2++;
|
||||
}
|
||||
|
||||
return ((diff / VFS301_FP_WIDTH) > VFS301_FP_LINE_DIFF_THRESHOLD);
|
||||
}
|
||||
|
||||
/** Transform the input data to a normalized fingerprint scan */
|
||||
void vfs301_extract_image(
|
||||
vfs301_dev_t *vfs, unsigned char *output, int *output_height
|
||||
)
|
||||
{
|
||||
const unsigned char *scanlines = vfs->scanline_buf;
|
||||
int last_line;
|
||||
int i;
|
||||
|
||||
g_assert(vfs->scanline_count >= 1);
|
||||
|
||||
*output_height = 1;
|
||||
memcpy(output, scanlines, VFS301_FP_OUTPUT_WIDTH);
|
||||
last_line = 0;
|
||||
|
||||
/* The following algorithm is quite trivial - it just picks lines that
|
||||
* differ more than VFS301_FP_LINE_DIFF_THRESHOLD.
|
||||
* TODO: A nicer approach would be to pick those lines and then do some kind
|
||||
* of bi/tri-linear resampling to get the output (so that we don't get so
|
||||
* many false edges etc.).
|
||||
*/
|
||||
for (i = 1; i < vfs->scanline_count; i++) {
|
||||
if (scanline_diff(scanlines, last_line, i)) {
|
||||
memcpy(
|
||||
output + VFS301_FP_OUTPUT_WIDTH * (*output_height),
|
||||
scanlines + VFS301_FP_OUTPUT_WIDTH * i,
|
||||
VFS301_FP_OUTPUT_WIDTH
|
||||
);
|
||||
last_line = i;
|
||||
(*output_height)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int img_process_data(
|
||||
int first_block, vfs301_dev_t *dev, const unsigned char *buf, int len
|
||||
)
|
||||
{
|
||||
vfs301_line_t *lines = (vfs301_line_t*)buf;
|
||||
int no_lines = len / sizeof(vfs301_line_t);
|
||||
int i;
|
||||
/*int no_nonempty;*/
|
||||
unsigned char *cur_line;
|
||||
int last_img_height;
|
||||
#ifdef SCAN_FINISH_DETECTION
|
||||
int finished_scan;
|
||||
#endif
|
||||
|
||||
if (first_block) {
|
||||
last_img_height = 0;
|
||||
dev->scanline_count = no_lines;
|
||||
} else {
|
||||
last_img_height = dev->scanline_count;
|
||||
dev->scanline_count += no_lines;
|
||||
}
|
||||
|
||||
dev->scanline_buf = realloc(dev->scanline_buf, dev->scanline_count * VFS301_FP_OUTPUT_WIDTH);
|
||||
g_assert(dev->scanline_buf != NULL);
|
||||
|
||||
for (cur_line = dev->scanline_buf + last_img_height * VFS301_FP_OUTPUT_WIDTH, i = 0;
|
||||
i < no_lines;
|
||||
i++, cur_line += VFS301_FP_OUTPUT_WIDTH
|
||||
) {
|
||||
#ifndef OUTPUT_RAW
|
||||
memcpy(cur_line, lines[i].scan, VFS301_FP_OUTPUT_WIDTH);
|
||||
#else
|
||||
memcpy(cur_line, &lines[i], VFS301_FP_OUTPUT_WIDTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SCAN_FINISH_DETECTION
|
||||
finished_scan = img_is_finished_scan(lines, no_lines);
|
||||
|
||||
return !finished_scan;
|
||||
#else /* SCAN_FINISH_DETECTION */
|
||||
return 1; /* Just continue until data is coming */
|
||||
#endif
|
||||
}
|
||||
|
||||
/************************** PROTOCOL STUFF ************************************/
|
||||
|
||||
static unsigned char usb_send_buf[0x2000];
|
||||
|
||||
#define USB_RECV(from, len) \
|
||||
usb_recv(dev, devh, from, len)
|
||||
|
||||
#define USB_SEND(type, subtype) \
|
||||
{ \
|
||||
int len; \
|
||||
vfs301_proto_generate(type, subtype, usb_send_buf, &len); \
|
||||
usb_send(devh, usb_send_buf, len); \
|
||||
}
|
||||
|
||||
#define RAW_DATA(x) x, sizeof(x)
|
||||
|
||||
#define IS_VFS301_FP_SEQ_START(b) ((b[0] == 0x01) && (b[1] == 0xfe))
|
||||
|
||||
static int vfs301_proto_process_data(int first_block, vfs301_dev_t *dev)
|
||||
{
|
||||
int i;
|
||||
const unsigned char *buf = dev->recv_buf;
|
||||
int len = dev->recv_len;
|
||||
|
||||
if (first_block) {
|
||||
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--) {
|
||||
if (IS_VFS301_FP_SEQ_START(buf))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return img_process_data(first_block, dev, buf, len);
|
||||
}
|
||||
|
||||
void vfs301_proto_request_fingerprint(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
USB_SEND(0x0220, 0xFA00);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 000000000000 */
|
||||
}
|
||||
|
||||
int vfs301_proto_peek_event(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
const char no_event[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const char got_event[] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
USB_SEND(0x17, -1);
|
||||
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 {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
#define VARIABLE_ORDER(a, b) \
|
||||
{ \
|
||||
int _rv = a;\
|
||||
b; \
|
||||
if (_rv == -7) \
|
||||
a; \
|
||||
}
|
||||
|
||||
static void vfs301_proto_process_event_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
vfs301_dev_t *dev = transfer->user_data;
|
||||
struct libusb_device_handle *devh = transfer->dev_handle;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
dev->recv_progress = VFS301_FAILURE;
|
||||
goto end;
|
||||
} else if (transfer->actual_length < dev->recv_exp_amt) {
|
||||
/* TODO: process the data anyway? */
|
||||
dev->recv_progress = VFS301_ENDED;
|
||||
goto end;
|
||||
} else {
|
||||
dev->recv_len = transfer->actual_length;
|
||||
if (!vfs301_proto_process_data(dev->recv_exp_amt == VFS301_FP_RECV_LEN_1, dev)) {
|
||||
dev->recv_progress = VFS301_ENDED;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dev->recv_exp_amt = VFS301_FP_RECV_LEN_2;
|
||||
libusb_fill_bulk_transfer(
|
||||
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
|
||||
dev->recv_buf, dev->recv_exp_amt,
|
||||
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
|
||||
|
||||
if (libusb_submit_transfer(transfer) < 0) {
|
||||
printf("cb::continue fail\n");
|
||||
dev->recv_progress = VFS301_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
end:
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
void vfs301_proto_process_event_start(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
*
|
||||
* seen next_scan order:
|
||||
* o FA00
|
||||
* o FA00
|
||||
* o 2C01
|
||||
* o FA00
|
||||
* o FA00
|
||||
* o 2C01
|
||||
* o FA00
|
||||
* o FA00
|
||||
* o 2C01
|
||||
* o 5E01 !?
|
||||
* o FA00
|
||||
* o FA00
|
||||
* o 2C01
|
||||
* o FA00
|
||||
* o FA00
|
||||
* o 2C01
|
||||
*/
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 64);
|
||||
|
||||
/* now read the fingerprint data, while there are some */
|
||||
transfer = fpi_usb_alloc();
|
||||
dev->recv_progress = VFS301_ONGOING;
|
||||
dev->recv_exp_amt = VFS301_FP_RECV_LEN_1;
|
||||
|
||||
libusb_fill_bulk_transfer(
|
||||
transfer, devh, VFS301_RECEIVE_ENDPOINT_DATA,
|
||||
dev->recv_buf, dev->recv_exp_amt,
|
||||
vfs301_proto_process_event_cb, dev, VFS301_FP_RECV_TIMEOUT);
|
||||
|
||||
if (libusb_submit_transfer(transfer) < 0) {
|
||||
libusb_free_transfer(transfer);
|
||||
dev->recv_progress = VFS301_FAILURE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int /* vfs301_dev_t::recv_progress */ vfs301_proto_process_event_poll(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
if (dev->recv_progress != VFS301_ENDED)
|
||||
return dev->recv_progress;
|
||||
|
||||
/* Finish the scan process... */
|
||||
|
||||
USB_SEND(0x04, -1);
|
||||
/* the following may come in random order, data may not come at all, don't
|
||||
* try for too long... */
|
||||
VARIABLE_ORDER(
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 1204 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 16384)
|
||||
);
|
||||
|
||||
USB_SEND(0x0220, 2);
|
||||
VARIABLE_ORDER(
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760), /* seems to always come */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2) /* 0000 */
|
||||
);
|
||||
|
||||
return dev->recv_progress;
|
||||
}
|
||||
|
||||
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
USB_SEND(0x01, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||
USB_SEND(0x0B, 0x04);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 6); /* 000000000000 */
|
||||
USB_SEND(0x0B, 0x05);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 7); /* 00000000000000 */
|
||||
USB_SEND(0x19, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 64);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 4); /* 6BB4D0BC */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_1));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
|
||||
USB_SEND(0x01, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||
USB_SEND(0x1A, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_2));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_SEND(0x0220, 1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 256);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 32);
|
||||
|
||||
USB_SEND(0x1A, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_3));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
|
||||
USB_SEND(0x01, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||
USB_SEND(0x02D0, 1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 11648); /* 56 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 2);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 53248); /* 2 * 128 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 3);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 19968); /* 96 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 4);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5824); /* 28 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 5);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 6);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 6656); /* 32 * vfs301_init_line_t[] */
|
||||
USB_SEND(0x02D0, 7);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 832);
|
||||
usb_send(devh, RAW_DATA(vfs301_12));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
|
||||
USB_SEND(0x1A, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_2));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
USB_SEND(0x0220, 2);
|
||||
VARIABLE_ORDER(
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2), /* 0000 */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760)
|
||||
);
|
||||
|
||||
USB_SEND(0x1A, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_1));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
|
||||
USB_SEND(0x1A, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_06_4));
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
usb_send(devh, RAW_DATA(vfs301_24)); /* turns on white */
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2); /* 0000 */
|
||||
|
||||
USB_SEND(0x01, -1);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 38);
|
||||
USB_SEND(0x0220, 3);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 2368);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_CTRL, 36);
|
||||
USB_RECV(VFS301_RECEIVE_ENDPOINT_DATA, 5760);
|
||||
}
|
||||
|
||||
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev)
|
||||
{
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* vfs301/vfs300 fingerprint reader driver
|
||||
* https://github.com/andree182/vfs301
|
||||
*
|
||||
* Copyright (c) 2011-2012 Andrej Krutak <dev@andree.sk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <libusb.h>
|
||||
|
||||
enum {
|
||||
VFS301_DEFAULT_WAIT_TIMEOUT = 300,
|
||||
|
||||
VFS301_SEND_ENDPOINT = 0x01,
|
||||
VFS301_RECEIVE_ENDPOINT_CTRL = 0x81,
|
||||
VFS301_RECEIVE_ENDPOINT_DATA = 0x82
|
||||
};
|
||||
|
||||
#define VFS301_FP_RECV_LEN_1 (84032)
|
||||
#define VFS301_FP_RECV_LEN_2 (84096)
|
||||
|
||||
typedef struct {
|
||||
/* buffer for received data */
|
||||
unsigned char recv_buf[0x20000];
|
||||
int recv_len;
|
||||
|
||||
/* buffer to hold raw scanlines */
|
||||
unsigned char *scanline_buf;
|
||||
int scanline_count;
|
||||
|
||||
enum {
|
||||
VFS301_ONGOING = 0,
|
||||
VFS301_ENDED = 1,
|
||||
VFS301_FAILURE = -1
|
||||
} recv_progress;
|
||||
int recv_exp_amt;
|
||||
} vfs301_dev_t;
|
||||
|
||||
enum {
|
||||
/* Width of the scanned data in px */
|
||||
VFS301_FP_WIDTH = 200,
|
||||
|
||||
/* sizeof(fp_line_t) */
|
||||
VFS301_FP_FRAME_SIZE = 288,
|
||||
/* Width of output line */
|
||||
#ifndef OUTPUT_RAW
|
||||
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_WIDTH,
|
||||
#else
|
||||
VFS301_FP_OUTPUT_WIDTH = VFS301_FP_FRAME_SIZE,
|
||||
#endif
|
||||
|
||||
VFS301_FP_SUM_LINES = 3,
|
||||
|
||||
#ifdef SCAN_FINISH_DETECTION
|
||||
/* TODO: The following changes (seen ~60 and ~80) In that
|
||||
* case we'll need to calibrate this from empty data somehow... */
|
||||
VFS301_FP_SUM_MEDIAN = 60,
|
||||
VFS301_FP_SUM_EMPTY_RANGE = 5,
|
||||
#endif
|
||||
|
||||
/* Minimum average difference between returned lines */
|
||||
VFS301_FP_LINE_DIFF_THRESHOLD = 15,
|
||||
|
||||
/* Maximum waiting time for a single fingerprint frame */
|
||||
VFS301_FP_RECV_TIMEOUT = 2000
|
||||
};
|
||||
|
||||
/* Arrays of this structure is returned during the initialization as a response
|
||||
* to the 0x02D0 messages.
|
||||
* It seems to be always the same - what is it for? Some kind of confirmation?
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char sync_0x01;
|
||||
unsigned char sync_0xfe;
|
||||
|
||||
unsigned char counter_lo;
|
||||
unsigned char counter_hi; /* FIXME ? */
|
||||
|
||||
unsigned char flags[3];
|
||||
|
||||
unsigned char sync_0x00;
|
||||
|
||||
unsigned char scan[VFS301_FP_WIDTH];
|
||||
} vfs301_init_line_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned char sync_0x01;
|
||||
unsigned char sync_0xfe;
|
||||
|
||||
unsigned char counter_lo;
|
||||
unsigned char counter_hi;
|
||||
|
||||
unsigned char sync_0x08[2]; /* XXX: always? 0x08 0x08 */
|
||||
/* 0x08 | 0x18 - Looks like 0x08 marks good quality lines */
|
||||
unsigned char flag_1;
|
||||
unsigned char sync_0x00;
|
||||
|
||||
unsigned char scan[VFS301_FP_WIDTH];
|
||||
|
||||
/* A offsetted, stretched, inverted copy of scan... probably could
|
||||
* serve finger motion speed detection?
|
||||
* Seems to be subdivided to some 10B + 53B + 1B blocks */
|
||||
unsigned char mirror[64];
|
||||
|
||||
/* Some kind of sum of the scan, very low contrast */
|
||||
unsigned char sum1[2];
|
||||
unsigned char sum2[11];
|
||||
unsigned char sum3[3];
|
||||
} vfs301_line_t;
|
||||
|
||||
void vfs301_proto_init(struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
void vfs301_proto_deinit(struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
|
||||
void vfs301_proto_request_fingerprint(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
|
||||
/** returns 0 if no event is ready, or 1 if there is one... */
|
||||
int vfs301_proto_peek_event(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
void vfs301_proto_process_event_start(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
int vfs301_proto_process_event_poll(
|
||||
struct libusb_device_handle *devh, vfs301_dev_t *dev);
|
||||
|
||||
void vfs301_extract_image(vfs301_dev_t *vfs, unsigned char *output, int *output_height);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,907 +0,0 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
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_failed(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_failed(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_failed(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_failed(ssm, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* success */
|
||||
fpi_ssm_next_state(ssm);
|
||||
|
||||
out:
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void async_recv_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
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_failed(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_failed(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_failed(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_failed(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_failed(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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct usbexchange_data *data = user_data;
|
||||
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_failed(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 = fpi_usb_alloc();
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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 = fpi_usb_alloc();
|
||||
libusb_fill_bulk_transfer(transfer, fpi_dev_get_usb_dev(FP_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_failed(ssm, -EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
fp_err("USB transfer error: %s", strerror(ret));
|
||||
fpi_imgdev_session_error(data->device, ret);
|
||||
fpi_ssm_mark_failed(ssm, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_exchange_async(fpi_ssm *ssm,
|
||||
struct usbexchange_data *data)
|
||||
{
|
||||
fpi_ssm *subsm = fpi_ssm_new(FP_DEV(data->device),
|
||||
usbexchange_loop,
|
||||
data->stepcount,
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
submit_image(fpi_ssm *ssm,
|
||||
struct vfs5011_data *data,
|
||||
struct fp_img_dev *dev)
|
||||
{
|
||||
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, committing");
|
||||
|
||||
fpi_imgdev_image_captured(dev, img);
|
||||
}
|
||||
|
||||
static void chunk_capture_callback(struct libusb_transfer *transfer)
|
||||
{
|
||||
fpi_ssm *ssm = (fpi_ssm *)transfer->user_data;
|
||||
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
|
||||
struct vfs5011_data *data;
|
||||
|
||||
data = FP_INSTANCE_DATA(FP_DEV(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_failed(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, 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 = fpi_usb_alloc();
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
enum {READ_TIMEOUT = 0};
|
||||
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct vfs5011_data *data;
|
||||
int r;
|
||||
fpi_timeout *timeout;
|
||||
|
||||
data = FP_INSTANCE_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_dev_get_usb_dev(FP_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_failed(ssm, r);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEV_ACTIVATE_DATA_COMPLETE:
|
||||
timeout = fpi_timeout_add(1, fpi_ssm_next_state_timeout_cb, _dev, ssm);
|
||||
|
||||
if (timeout == NULL) {
|
||||
/* Failed to add timeout */
|
||||
fp_err("failed to add timeout");
|
||||
fpi_imgdev_session_error(dev, -1);
|
||||
fpi_ssm_mark_failed(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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct vfs5011_data *data;
|
||||
int r = fpi_ssm_get_error(ssm);
|
||||
|
||||
data = FP_INSTANCE_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, dev);
|
||||
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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct vfs5011_data *data;
|
||||
|
||||
data = FP_INSTANCE_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(fpi_ssm *ssm, struct fp_dev *_dev, void *user_data)
|
||||
{
|
||||
struct fp_img_dev *dev = user_data;
|
||||
struct vfs5011_data *data;
|
||||
|
||||
data = FP_INSTANCE_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);
|
||||
fp_dev_set_instance_data(FP_DEV(dev), data);
|
||||
|
||||
r = libusb_reset_device(fpi_dev_get_usb_dev(FP_DEV(dev)));
|
||||
if (r != 0) {
|
||||
fp_err("Failed to reset the device");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = libusb_claim_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
if (r != 0) {
|
||||
fp_err("Failed to claim interface: %s", libusb_error_name(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
fpi_ssm *ssm;
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), open_loop, DEV_OPEN_NUM_STATES, dev);
|
||||
fpi_ssm_start(ssm, open_loop_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dev_close(struct fp_img_dev *dev)
|
||||
{
|
||||
libusb_release_interface(fpi_dev_get_usb_dev(FP_DEV(dev)), 0);
|
||||
struct vfs5011_data *data;
|
||||
data = FP_INSTANCE_DATA(FP_DEV(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;
|
||||
fpi_ssm *ssm;
|
||||
|
||||
data = FP_INSTANCE_DATA(FP_DEV(dev));
|
||||
data->loop_running = TRUE;
|
||||
fp_dbg("creating ssm");
|
||||
ssm = fpi_ssm_new(FP_DEV(dev), activate_loop, DEV_ACTIVATE_NUM_STATES, 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)
|
||||
{
|
||||
struct vfs5011_data *data;
|
||||
|
||||
data = FP_INSTANCE_DATA(FP_DEV(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 = FP_INSTANCE_DATA(FP_DEV(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 = 0x0015 /* Validity device from Lenovo Preferred Pro USB Fingerprint Keyboard KUF1256 */ },
|
||||
{ .vendor = 0x138a, .product = 0x0017 /* Validity device from Lenovo T440 laptops */ },
|
||||
{ .vendor = 0x138a, .product = 0x0018 /* one more Validity device */ },
|
||||
{ 0, 0, 0, },
|
||||
};
|
||||
|
||||
struct fp_img_driver vfs5011_driver = {
|
||||
.driver = {
|
||||
.id = VFS5011_ID,
|
||||
.name = "vfs5011",
|
||||
.full_name = "Validity VFS5011",
|
||||
.id_table = id_table,
|
||||
.scan_type = FP_SCAN_TYPE_SWIPE,
|
||||
},
|
||||
|
||||
.flags = 0,
|
||||
.img_width = VFS5011_IMAGE_WIDTH,
|
||||
.img_height = -1,
|
||||
.bz3_threshold = 20,
|
||||
|
||||
.open = dev_open,
|
||||
.close = dev_close,
|
||||
.activate = dev_activate,
|
||||
.deactivate = dev_deactivate,
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#include "fprint.h"
|
||||
#include "fpi-log.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-dev-img.h"
|
||||
#include "fpi-core.h"
|
||||
#include "fpi-ssm.h"
|
||||
#include "fpi-poll.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-usb.h"
|
||||
#include "fpi-img.h"
|
||||
#include "fpi-assembling.h"
|
||||
#include "drivers/driver_ids.h"
|
||||
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* Internal/private definitions 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
|
||||
*/
|
||||
|
||||
#ifndef __FPRINT_INTERNAL_H__
|
||||
#define __FPRINT_INTERNAL_H__
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
#include "nbis-helpers.h"
|
||||
#include "fprint.h"
|
||||
#include "fpi-dev.h"
|
||||
#include "fpi-core.h"
|
||||
#include "fpi-log.h"
|
||||
#include "fpi-dev-img.h"
|
||||
#include "fpi-data.h"
|
||||
#include "fpi-img.h"
|
||||
#include "drivers/driver_ids.h"
|
||||
|
||||
/* Global variables */
|
||||
extern libusb_context *fpi_usb_ctx;
|
||||
extern GSList *opened_devices;
|
||||
|
||||
/* fp_print_data structure definition */
|
||||
enum fp_print_data_type {
|
||||
PRINT_DATA_RAW = 0, /* memset-imposed default */
|
||||
PRINT_DATA_NBIS_MINUTIAE
|
||||
};
|
||||
|
||||
struct fp_print_data {
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
enum fp_print_data_type type;
|
||||
GSList *prints;
|
||||
};
|
||||
|
||||
/* fp_dev structure definition */
|
||||
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 {
|
||||
struct fp_driver *drv;
|
||||
uint32_t devtype;
|
||||
|
||||
/* only valid if drv->type == DRIVER_IMAGING */
|
||||
struct fp_img_dev *img_dev;
|
||||
/* Link to the instance specific struct */
|
||||
void *instance_data;
|
||||
|
||||
int nr_enroll_stages;
|
||||
|
||||
/* FIXME: This will eventually have a bus type */
|
||||
libusb_device_handle *udev;
|
||||
|
||||
/* read-only to drivers */
|
||||
struct fp_print_data *verify_data;
|
||||
|
||||
/* 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_operation_stop_cb close_cb;
|
||||
void *close_cb_data;
|
||||
fp_enroll_stage_cb enroll_stage_cb;
|
||||
void *enroll_stage_cb_data;
|
||||
fp_operation_stop_cb enroll_stop_cb;
|
||||
void *enroll_stop_cb_data;
|
||||
fp_img_operation_cb verify_cb;
|
||||
void *verify_cb_data;
|
||||
fp_operation_stop_cb verify_stop_cb;
|
||||
void *verify_stop_cb_data;
|
||||
fp_identify_cb identify_cb;
|
||||
void *identify_cb_data;
|
||||
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;
|
||||
};
|
||||
|
||||
/* fp_img_dev structure definition */
|
||||
struct fp_img_dev {
|
||||
struct fp_dev *parent;
|
||||
|
||||
enum fp_imgdev_action action;
|
||||
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? */
|
||||
size_t identify_match_offset;
|
||||
};
|
||||
|
||||
/* fp_driver structure definition */
|
||||
|
||||
/* fp_img_driver structure definition */
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#define fpi_driver_to_img_driver(drv) \
|
||||
container_of((drv), struct fp_img_driver, driver)
|
||||
|
||||
/* fp_dscv_dev structure definition */
|
||||
struct fp_dscv_dev {
|
||||
struct libusb_device *udev;
|
||||
struct fp_driver *drv;
|
||||
unsigned long driver_data;
|
||||
uint32_t devtype;
|
||||
};
|
||||
|
||||
/* fp_dscv_print structure definition */
|
||||
struct fp_dscv_print {
|
||||
uint16_t driver_id;
|
||||
uint32_t devtype;
|
||||
enum fp_finger finger;
|
||||
char *path;
|
||||
};
|
||||
|
||||
/* fp_minutia structure definition */
|
||||
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_minutiae structure definition */
|
||||
struct fp_minutiae {
|
||||
int alloc;
|
||||
int num;
|
||||
struct fp_minutia **list;
|
||||
};
|
||||
|
||||
/* Defined in fpi-dev-img.c */
|
||||
void fpi_img_driver_setup(struct fp_img_driver *idriver);
|
||||
int fpi_imgdev_get_img_width(struct fp_img_dev *imgdev);
|
||||
int fpi_imgdev_get_img_height(struct fp_img_dev *imgdev);
|
||||
|
||||
/* Exported for use in command-line tools
|
||||
* Defined in fpi-core.c */
|
||||
struct fp_driver **fprint_get_drivers (void);
|
||||
|
||||
/* Defined in fpi-core.c */
|
||||
enum fp_print_data_type fpi_driver_get_data_type(struct fp_driver *drv);
|
||||
|
||||
/* Defined in fpi-data.c */
|
||||
void fpi_data_exit(void);
|
||||
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);
|
||||
|
||||
/* Defined in fpi-img.c */
|
||||
gboolean fpi_img_is_sane(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,
|
||||
struct fp_print_data *new_print);
|
||||
int fpi_img_compare_print_data_to_gallery(struct fp_print_data *print,
|
||||
struct fp_print_data **gallery, int match_threshold, size_t *match_offset);
|
||||
|
||||
/* Defined in fpi-poll.c */
|
||||
void fpi_timeout_cancel_all_for_dev(struct fp_dev *dev);
|
||||
void fpi_poll_init(void);
|
||||
void fpi_poll_exit(void);
|
||||
|
||||
/* Defined in fpi-async.c */
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#include "drivers_definitions.h"
|
||||
|
||||
#endif
|
|
@ -1,487 +0,0 @@
|
|||
/*
|
||||
* 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 "fpi-assembling.h"
|
||||
|
||||
/**
|
||||
* SECTION:fpi-assembling
|
||||
* @title: Image frame assembly
|
||||
* @short_description: Image frame assembly helpers
|
||||
*
|
||||
* Those are the helpers to manipulate capture data from fingerprint readers
|
||||
* into a uniform image that can be further processed. This is usually used
|
||||
* by drivers for devices which have a small sensor and thus need to capture
|
||||
* data in small stripes.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_do_movement_estimation:
|
||||
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
|
||||
* @stripes: a singly-linked list of #fpi_frame
|
||||
* @num_stripes: number of items in @stripes to process
|
||||
*
|
||||
* fpi_do_movement_estimation() estimates the movement between adjacent
|
||||
* frames, populating @delta_x and @delta_y values for each #fpi_frame.
|
||||
*
|
||||
* This function is used for devices that don't do movement estimation
|
||||
* in hardware. If hardware movement estimation is supported, the driver
|
||||
* should populate @delta_x and @delta_y instead.
|
||||
*
|
||||
* Note that @num_stripes might be shorter than the length of the list,
|
||||
* if some stripes should be skipped.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_assemble_frames:
|
||||
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
|
||||
* @stripes: linked list of #fpi_frame
|
||||
* @num_stripes: number of items in @stripes to process
|
||||
*
|
||||
* fpi_assemble_frames() assembles individual frames into a single image.
|
||||
* It expects @delta_x and @delta_y of #fpi_frame to be populated.
|
||||
*
|
||||
* Note that @num_stripes might be shorter than the length of the list,
|
||||
* if some stripes should be skipped.
|
||||
*
|
||||
* Returns: a newly allocated #fp_img.
|
||||
*/
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes)
|
||||
{
|
||||
GSList *stripe;
|
||||
struct fp_img *img;
|
||||
int height = 0;
|
||||
int i, y, x;
|
||||
gboolean reverse = FALSE;
|
||||
struct fpi_frame *fpi_frame;
|
||||
|
||||
//FIXME g_return_if_fail
|
||||
BUG_ON(num_stripes == 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 < num_stripes);
|
||||
|
||||
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 < num_stripes);
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_assemble_lines:
|
||||
* @ctx: #fpi_frame_asmbl_ctx - frame assembling context
|
||||
* @lines: linked list of lines
|
||||
* @num_lines: number of items in @lines to process
|
||||
*
|
||||
* #fpi_assemble_lines assembles individual lines into a single image.
|
||||
* It also rescales image to account variable swiping speed.
|
||||
*
|
||||
* Note that @num_lines might be shorter than the length of the list,
|
||||
* if some lines should be skipped.
|
||||
*
|
||||
* Returns: a newly allocated #fp_img.
|
||||
*/
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t num_lines)
|
||||
{
|
||||
/* 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((num_lines / 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 (num_lines >= 2, NULL);
|
||||
|
||||
fp_dbg("%"G_GINT64_FORMAT, g_get_real_time());
|
||||
|
||||
row1 = lines;
|
||||
for (i = 0; (i < num_lines - 1) && row1; i += 2) {
|
||||
int bestmatch = i;
|
||||
int bestdiff = 0;
|
||||
int j, firstrow, lastrow;
|
||||
|
||||
firstrow = i + 1;
|
||||
lastrow = MIN(i + ctx->max_search_offset, num_lines - 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, (num_lines / 2) - 1, ctx->median_filter_size);
|
||||
|
||||
fp_dbg("offsets_filtered: %"G_GINT64_FORMAT, g_get_real_time());
|
||||
for (i = 0; i <= (num_lines / 2) - 1; i++)
|
||||
fp_dbg("%d", offsets[i]);
|
||||
row1 = lines;
|
||||
for (i = 0; i < num_lines - 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;
|
||||
memmove(img->data, output, ctx->line_width * line_ind);
|
||||
g_free(offsets);
|
||||
g_free(output);
|
||||
return img;
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* 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 __FPI_ASSEMBLING_H__
|
||||
#define __FPI_ASSEMBLING_H__
|
||||
|
||||
#include <fprint.h>
|
||||
|
||||
/**
|
||||
* fpi_frame:
|
||||
* @delta_x: X offset of the frame
|
||||
* @delta_y: Y offset of the frame
|
||||
* @data: bitmap
|
||||
*
|
||||
* #fpi_frame is used to store frames for swipe sensors. Drivers should
|
||||
* populate delta_x and delta_y if the device supports hardware movement
|
||||
* estimation.
|
||||
*/
|
||||
struct fpi_frame {
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* fpi_frame_asmbl_ctx:
|
||||
* @frame_width: width of the frame
|
||||
* @frame_height: height of the frame
|
||||
* @image_width: resulting image width
|
||||
* @get_pixel: pixel accessor, returns pixel brightness at x,y of frame
|
||||
*
|
||||
* #fpi_frame_asmbl_ctx is a structure holding the context for frame
|
||||
* assembling routines.
|
||||
*
|
||||
* Drivers should define their own #fpi_frame_asmbl_ctx depending on
|
||||
* hardware parameters of scanner. @image_width is usually 25% wider than
|
||||
* @frame_width to take horizontal movement into account.
|
||||
*/
|
||||
struct fpi_frame_asmbl_ctx {
|
||||
unsigned int frame_width;
|
||||
unsigned int frame_height;
|
||||
unsigned int image_width;
|
||||
unsigned char (*get_pixel)(struct fpi_frame_asmbl_ctx *ctx,
|
||||
struct fpi_frame *frame,
|
||||
unsigned int x,
|
||||
unsigned int y);
|
||||
};
|
||||
|
||||
void fpi_do_movement_estimation(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes);
|
||||
|
||||
struct fp_img *fpi_assemble_frames(struct fpi_frame_asmbl_ctx *ctx,
|
||||
GSList *stripes, size_t num_stripes);
|
||||
|
||||
/**
|
||||
* fpi_line_asmbl_ctx:
|
||||
* @line_width: width of line
|
||||
* @max_height: maximal height of assembled image
|
||||
* @resolution: scale factor used for line assembling routines.
|
||||
* @median_filter_size: size of median filter for movement estimation
|
||||
* @max_search_offset: the number of lines to search for the next line
|
||||
* @get_deviation: pointer to a function that returns the numerical difference
|
||||
* between two lines
|
||||
* @get_pixel: pixel accessor, returns pixel brightness at x of line
|
||||
*
|
||||
* #fpi_line_asmbl_ctx is a structure holding the context for line assembling
|
||||
* routines.
|
||||
*
|
||||
* Drivers should define their own #fpi_line_asmbl_ctx depending on
|
||||
* the hardware parameters of the scanner. Swipe scanners of this type usually
|
||||
* return two lines, the second line is often narrower than first and is used
|
||||
* for movement estimation.
|
||||
*
|
||||
* The @max_search_offset value indicates how many lines forward the assembling
|
||||
* routines should look while searching for next line. This value depends on
|
||||
* how fast the hardware sends frames.
|
||||
*
|
||||
* The function pointed to by @get_deviation should return the numerical difference
|
||||
* between two lines. Higher values means lines are more different. If the reader
|
||||
* returns two lines at a time, this function should be used to estimate the
|
||||
* difference between pairs of lines.
|
||||
*/
|
||||
struct fpi_line_asmbl_ctx {
|
||||
unsigned int line_width;
|
||||
unsigned int max_height;
|
||||
unsigned int resolution;
|
||||
unsigned int median_filter_size;
|
||||
unsigned int 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 int x);
|
||||
};
|
||||
|
||||
struct fp_img *fpi_assemble_lines(struct fpi_line_asmbl_ctx *ctx,
|
||||
GSList *lines, size_t num_lines);
|
||||
|
||||
#endif
|
|
@ -1,706 +0,0 @@
|
|||
/*
|
||||
* Asynchronous I/O functionality
|
||||
* Copyright (C) 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 "async"
|
||||
|
||||
#include "fp_internal.h"
|
||||
#include "fpi-async.h"
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
|
||||
/*
|
||||
* SECTION:fpi-async
|
||||
* @title: Asynchronous operations reporting
|
||||
* @short_description: Asynchronous operations reporting functions
|
||||
*
|
||||
* Those functions are used by primitive drivers to report back their
|
||||
* current status. Most drivers, imaging ones, do not need to use them.
|
||||
*/
|
||||
|
||||
/* Drivers call this when device initialisation has completed */
|
||||
void fpi_drvcb_open_complete(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_INITIALIZING);
|
||||
dev->state = (status) ? DEV_STATE_ERROR : DEV_STATE_INITIALIZED;
|
||||
opened_devices = g_slist_prepend(opened_devices, dev);
|
||||
if (dev->open_cb)
|
||||
dev->open_cb(dev, status, dev->open_cb_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_dev_open:
|
||||
* @ddev: the struct #fp_dscv_dev discovered device to open
|
||||
* @callback: the callback to call when the device has been opened
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Opens and initialises a device. This is the function you call in order
|
||||
* to convert a #fp_dscv_dev discovered device into an actual device handle
|
||||
* that you can perform operations with.
|
||||
*
|
||||
* The error status of the opening will be provided as an argument to the
|
||||
* #fp_dev_open_cb callback.
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_dev_open(struct fp_dscv_dev *ddev, fp_dev_open_cb callback,
|
||||
void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
struct fp_dev *dev;
|
||||
libusb_device_handle *udevh;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(ddev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = ddev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
r = libusb_open(ddev->udev, &udevh);
|
||||
if (r < 0) {
|
||||
fp_err("usb_open failed, error %d", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
dev = g_malloc0(sizeof(*dev));
|
||||
dev->drv = drv;
|
||||
dev->udev = udevh;
|
||||
dev->__enroll_stage = -1;
|
||||
dev->state = DEV_STATE_INITIALIZING;
|
||||
dev->open_cb = callback;
|
||||
dev->open_cb_data = user_data;
|
||||
|
||||
if (!drv->open) {
|
||||
fpi_drvcb_open_complete(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->state = DEV_STATE_INITIALIZING;
|
||||
r = drv->open(dev, ddev->driver_data);
|
||||
if (r) {
|
||||
fp_err("device initialisation failed, driver=%s", drv->name);
|
||||
libusb_close(udevh);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when device deinitialisation has completed */
|
||||
void fpi_drvcb_close_complete(struct fp_dev *dev)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_DEINITIALIZING);
|
||||
dev->state = DEV_STATE_DEINITIALIZED;
|
||||
fpi_timeout_cancel_all_for_dev(dev);
|
||||
libusb_close(dev->udev);
|
||||
if (dev->close_cb)
|
||||
dev->close_cb(dev, dev->close_cb_data);
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_dev_close:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the device has been closed
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Closes a device. You must call this function when you have finished using
|
||||
* a fingerprint device.
|
||||
*/
|
||||
API_EXPORTED void fp_async_dev_close(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
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);
|
||||
opened_devices = g_slist_remove(opened_devices, (gconstpointer) dev);
|
||||
|
||||
dev->close_cb = callback;
|
||||
dev->close_cb_data = user_data;
|
||||
dev->state = DEV_STATE_DEINITIALIZING;
|
||||
drv->close(dev);
|
||||
}
|
||||
|
||||
/* Drivers call this when enrollment has started */
|
||||
void fpi_drvcb_enroll_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_ENROLL_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->enroll_stage_cb)
|
||||
dev->enroll_stage_cb(dev, status, NULL, NULL,
|
||||
dev->enroll_stage_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_ENROLLING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_enroll_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call for each stage of the enrollment
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Starts an enrollment and calls @callback for each enrollment stage.
|
||||
* See [Enrolling](libfprint-Devices-operations.html#enrolling)
|
||||
* for an explanation of enroll stages.
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_enroll_start(struct fp_dev *dev,
|
||||
fp_enroll_stage_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
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);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
fp_dbg("starting enrollment");
|
||||
dev->enroll_stage_cb = callback;
|
||||
dev->enroll_stage_cb_data = user_data;
|
||||
|
||||
dev->state = DEV_STATE_ENROLL_STARTING;
|
||||
r = drv->enroll_start(dev);
|
||||
if (r < 0) {
|
||||
dev->enroll_stage_cb = NULL;
|
||||
fp_err("failed to start enrollment");
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when an enroll stage has completed */
|
||||
void fpi_drvcb_enroll_stage_completed(struct fp_dev *dev, int result,
|
||||
struct fp_print_data *data, struct fp_img *img)
|
||||
{
|
||||
BUG_ON(dev->state != DEV_STATE_ENROLLING);
|
||||
fp_dbg("result %d", result);
|
||||
if (!dev->enroll_stage_cb) {
|
||||
fp_dbg("ignoring enroll result as no callback is subscribed");
|
||||
return;
|
||||
}
|
||||
if (result == FP_ENROLL_COMPLETE && !data) {
|
||||
fp_err("BUG: complete but no data?");
|
||||
result = FP_ENROLL_FAIL;
|
||||
}
|
||||
dev->enroll_stage_cb(dev, result, data, img, dev->enroll_stage_cb_data);
|
||||
}
|
||||
|
||||
/* Drivers call this when enrollment has stopped */
|
||||
void fpi_drvcb_enroll_stopped(struct fp_dev *dev)
|
||||
{
|
||||
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: the struct #fp_dev device
|
||||
* @callback: the callback to call when the enrollment has been cancelled
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing enrollment started with fp_async_enroll_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_enroll_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();
|
||||
if (!drv->enroll_start)
|
||||
return -ENOTSUP;
|
||||
|
||||
dev->enroll_stage_cb = NULL;
|
||||
dev->enroll_stop_cb = callback;
|
||||
dev->enroll_stop_cb_data = user_data;
|
||||
dev->state = DEV_STATE_ENROLL_STOPPING;
|
||||
|
||||
if (!drv->enroll_stop) {
|
||||
fpi_drvcb_enroll_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->enroll_stop(dev);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop enrollment");
|
||||
dev->enroll_stop_cb = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_verify_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @data: the print to verify against. Must have been previously
|
||||
* enrolled with a device compatible to the device selected to perform the scan
|
||||
* @callback: the callback to call when the verification has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Starts a verification and calls @callback when the verification has
|
||||
* finished. See fp_verify_finger_img() for the synchronous API. When the
|
||||
* @callback has been called, you must call fp_async_verify_stop().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_verify_start(struct fp_dev *dev,
|
||||
struct fp_print_data *data, fp_img_operation_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (!drv->verify_start)
|
||||
return -ENOTSUP;
|
||||
|
||||
dev->state = DEV_STATE_VERIFY_STARTING;
|
||||
dev->verify_cb = callback;
|
||||
dev->verify_cb_data = user_data;
|
||||
dev->verify_data = data;
|
||||
|
||||
r = drv->verify_start(dev);
|
||||
if (r < 0) {
|
||||
dev->verify_cb = NULL;
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
fp_err("failed to start verification, error %d", r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when verification has started */
|
||||
void fpi_drvcb_verify_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
G_DEBUG_HERE();
|
||||
BUG_ON(dev->state != DEV_STATE_VERIFY_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->verify_cb)
|
||||
dev->verify_cb(dev, status, NULL, dev->verify_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_VERIFYING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers call this to report a verify result (which might mark completion) */
|
||||
void fpi_drvcb_report_verify_result(struct fp_dev *dev, int result,
|
||||
struct fp_img *img)
|
||||
{
|
||||
fp_dbg("result %d", result);
|
||||
BUG_ON(dev->state != DEV_STATE_VERIFYING);
|
||||
if (result < 0 || result == FP_VERIFY_NO_MATCH
|
||||
|| result == FP_VERIFY_MATCH)
|
||||
dev->state = DEV_STATE_VERIFY_DONE;
|
||||
|
||||
if (dev->verify_cb)
|
||||
dev->verify_cb(dev, result, img, dev->verify_cb_data);
|
||||
else
|
||||
fp_dbg("ignoring verify result as no callback is subscribed");
|
||||
}
|
||||
|
||||
/* Drivers call this when verification has stopped */
|
||||
void fpi_drvcb_verify_stopped(struct fp_dev *dev)
|
||||
{
|
||||
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: the struct #fp_dev device
|
||||
* @callback: the callback to call to finish a verification
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Finishes an ongoing verification started with fp_async_verify_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_verify_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_VERIFYING);
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (dev->state == DEV_STATE_VERIFY_STOPPING) {
|
||||
fp_dbg ("Already stopping verification, returning -EINPROGRESS");
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
if (dev->state == DEV_STATE_INITIALIZED) {
|
||||
if (callback)
|
||||
callback(dev, user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
BUG_ON(dev->state != DEV_STATE_ERROR
|
||||
&& dev->state != DEV_STATE_VERIFYING
|
||||
&& dev->state != DEV_STATE_VERIFY_DONE);
|
||||
|
||||
dev->verify_cb = NULL;
|
||||
dev->verify_stop_cb = callback;
|
||||
dev->verify_stop_cb_data = user_data;
|
||||
dev->state = DEV_STATE_VERIFY_STOPPING;
|
||||
|
||||
if (!drv->verify_start)
|
||||
return -ENOTSUP;
|
||||
if (!drv->verify_stop) {
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
fpi_drvcb_verify_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->verify_stop(dev, iterating);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop verification");
|
||||
dev->verify_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_identify_start:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @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
|
||||
* @callback: the callback to call when the identification has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Performs a new scan and verifies it against a previously enrolled print.
|
||||
* See also: fp_verify_finger_img()
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
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;
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
G_DEBUG_HERE();
|
||||
if (!drv->identify_start)
|
||||
return -ENOTSUP;
|
||||
dev->state = DEV_STATE_IDENTIFY_STARTING;
|
||||
dev->identify_cb = callback;
|
||||
dev->identify_cb_data = user_data;
|
||||
dev->identify_gallery = gallery;
|
||||
|
||||
r = drv->identify_start(dev);
|
||||
if (r < 0) {
|
||||
fp_err("identify_start failed with error %d", r);
|
||||
dev->identify_cb = NULL;
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Driver-lib: identification has started, expect results soon */
|
||||
void fpi_drvcb_identify_started(struct fp_dev *dev, int status)
|
||||
{
|
||||
fp_dbg("status %d", status);
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFY_STARTING);
|
||||
if (status) {
|
||||
if (status > 0) {
|
||||
status = -status;
|
||||
fp_dbg("adjusted to %d", status);
|
||||
}
|
||||
dev->state = DEV_STATE_ERROR;
|
||||
if (dev->identify_cb)
|
||||
dev->identify_cb(dev, status, 0, NULL, dev->identify_cb_data);
|
||||
} else {
|
||||
dev->state = DEV_STATE_IDENTIFYING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drivers report an identify result (which might mark completion) */
|
||||
void fpi_drvcb_report_identify_result(struct fp_dev *dev, int result,
|
||||
size_t match_offset, struct fp_img *img)
|
||||
{
|
||||
fp_dbg("result %d", result);
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
||||
&& dev->state != DEV_STATE_ERROR);
|
||||
if (result < 0 || result == FP_VERIFY_NO_MATCH
|
||||
|| result == FP_VERIFY_MATCH)
|
||||
dev->state = DEV_STATE_IDENTIFY_DONE;
|
||||
|
||||
if (dev->identify_cb)
|
||||
dev->identify_cb(dev, result, match_offset, img, dev->identify_cb_data);
|
||||
else
|
||||
fp_dbg("ignoring verify result as no callback is subscribed");
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_async_identify_stop:
|
||||
* @dev: the struct #fp_dev device
|
||||
* @callback: the callback to call when the identification has stopped
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing identification started with fp_async_identify_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
API_EXPORTED int fp_async_identify_stop(struct fp_dev *dev,
|
||||
fp_operation_stop_cb callback, void *user_data)
|
||||
{
|
||||
struct fp_driver *drv;
|
||||
gboolean iterating = (dev->state == DEV_STATE_IDENTIFYING);
|
||||
int r;
|
||||
|
||||
g_return_val_if_fail(dev != NULL, -ENODEV);
|
||||
|
||||
G_DEBUG_HERE();
|
||||
|
||||
if (dev->state == DEV_STATE_IDENTIFY_STOPPING) {
|
||||
fp_dbg ("Already stopping identification, returning -EINPROGRESS");
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
if (dev->state == DEV_STATE_INITIALIZED) {
|
||||
if (callback)
|
||||
callback(dev, user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
drv = dev->drv;
|
||||
|
||||
BUG_ON(dev->state != DEV_STATE_IDENTIFYING
|
||||
&& dev->state != DEV_STATE_IDENTIFY_DONE);
|
||||
|
||||
dev->state = DEV_STATE_IDENTIFY_STOPPING;
|
||||
dev->identify_cb = NULL;
|
||||
dev->identify_stop_cb = callback;
|
||||
dev->identify_stop_cb_data = user_data;
|
||||
|
||||
if (!drv->identify_start)
|
||||
return -ENOTSUP;
|
||||
if (!drv->identify_stop) {
|
||||
dev->state = DEV_STATE_INITIALIZED;
|
||||
fpi_drvcb_identify_stopped(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = drv->identify_stop(dev, iterating);
|
||||
if (r < 0) {
|
||||
fp_err("failed to stop identification");
|
||||
dev->identify_stop_cb = NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Drivers call this when identification has stopped */
|
||||
void fpi_drvcb_identify_stopped(struct fp_dev *dev)
|
||||
{
|
||||
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: the struct #fp_dev device
|
||||
* @unconditional: whether to unconditionally capture an image, or to only capture when a finger is detected
|
||||
* @callback: the callback to call when the capture has finished
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Start the capture of an #fp_img from a device. When the @callback has been called,
|
||||
* you must call fp_async_capture_stop().
|
||||
*
|
||||
* 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_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);
|
||||
g_return_val_if_fail (callback != NULL, -EINVAL);
|
||||
|
||||
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 capture, 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: the struct #fp_dev device
|
||||
* @callback: the callback to call when the capture has been stopped
|
||||
* @user_data: user data to pass to the callback
|
||||
*
|
||||
* Stops an ongoing verification started with fp_async_capture_start().
|
||||
*
|
||||
* Returns: 0 on success, non-zero on error
|
||||
*/
|
||||
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 capture");
|
||||
dev->capture_stop_cb = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue